summaryrefslogtreecommitdiff
path: root/qpid/cpp/src
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src')
-rw-r--r--qpid/cpp/src/CMakeLists.txt1305
-rw-r--r--qpid/cpp/src/CMakeWinVersions.cmake57
-rw-r--r--qpid/cpp/src/Makefile.am896
-rw-r--r--qpid/cpp/src/acl.mk41
-rw-r--r--qpid/cpp/src/cluster.cmake168
-rw-r--r--qpid/cpp/src/cluster.mk113
-rw-r--r--qpid/cpp/src/config.h.cmake66
-rwxr-xr-xqpid/cpp/src/generate.sh67
-rw-r--r--qpid/cpp/src/posix/QpiddBroker.cpp190
-rwxr-xr-xqpid/cpp/src/prof39
-rw-r--r--qpid/cpp/src/qmf.mk167
-rw-r--r--qpid/cpp/src/qmf/Agent.cpp657
-rw-r--r--qpid/cpp/src/qmf/AgentEvent.cpp85
-rw-r--r--qpid/cpp/src/qmf/AgentEventImpl.h96
-rw-r--r--qpid/cpp/src/qmf/AgentImpl.h123
-rw-r--r--qpid/cpp/src/qmf/AgentSession.cpp1072
-rw-r--r--qpid/cpp/src/qmf/AgentSubscription.cpp51
-rw-r--r--qpid/cpp/src/qmf/AgentSubscription.h52
-rw-r--r--qpid/cpp/src/qmf/ConsoleEvent.cpp82
-rw-r--r--qpid/cpp/src/qmf/ConsoleEventImpl.h84
-rw-r--r--qpid/cpp/src/qmf/ConsoleSession.cpp618
-rw-r--r--qpid/cpp/src/qmf/ConsoleSessionImpl.h108
-rw-r--r--qpid/cpp/src/qmf/Data.cpp130
-rw-r--r--qpid/cpp/src/qmf/DataAddr.cpp106
-rw-r--r--qpid/cpp/src/qmf/DataAddrImpl.h73
-rw-r--r--qpid/cpp/src/qmf/DataImpl.h84
-rw-r--r--qpid/cpp/src/qmf/Expression.cpp441
-rw-r--r--qpid/cpp/src/qmf/Expression.h73
-rw-r--r--qpid/cpp/src/qmf/Hash.cpp45
-rw-r--r--qpid/cpp/src/qmf/Hash.h44
-rw-r--r--qpid/cpp/src/qmf/PrivateImplRef.h93
-rw-r--r--qpid/cpp/src/qmf/Query.cpp153
-rw-r--r--qpid/cpp/src/qmf/QueryImpl.h77
-rw-r--r--qpid/cpp/src/qmf/Schema.cpp358
-rw-r--r--qpid/cpp/src/qmf/SchemaCache.cpp91
-rw-r--r--qpid/cpp/src/qmf/SchemaCache.h56
-rw-r--r--qpid/cpp/src/qmf/SchemaId.cpp96
-rw-r--r--qpid/cpp/src/qmf/SchemaIdImpl.h83
-rw-r--r--qpid/cpp/src/qmf/SchemaImpl.h95
-rw-r--r--qpid/cpp/src/qmf/SchemaMethod.cpp186
-rw-r--r--qpid/cpp/src/qmf/SchemaMethodImpl.h75
-rw-r--r--qpid/cpp/src/qmf/SchemaProperty.cpp434
-rw-r--r--qpid/cpp/src/qmf/SchemaPropertyImpl.h93
-rw-r--r--qpid/cpp/src/qmf/Subscription.cpp88
-rw-r--r--qpid/cpp/src/qmf/SubscriptionImpl.h57
-rw-r--r--qpid/cpp/src/qmf/agentCapability.h39
-rw-r--r--qpid/cpp/src/qmf/constants.cpp77
-rw-r--r--qpid/cpp/src/qmf/constants.h83
-rw-r--r--qpid/cpp/src/qmf/engine/Agent.cpp915
-rw-r--r--qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp827
-rw-r--r--qpid/cpp/src/qmf/engine/BrokerProxyImpl.h241
-rw-r--r--qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp278
-rw-r--r--qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h63
-rw-r--r--qpid/cpp/src/qmf/engine/ConsoleImpl.cpp458
-rw-r--r--qpid/cpp/src/qmf/engine/ConsoleImpl.h148
-rw-r--r--qpid/cpp/src/qmf/engine/EventImpl.cpp120
-rw-r--r--qpid/cpp/src/qmf/engine/EventImpl.h57
-rw-r--r--qpid/cpp/src/qmf/engine/MessageImpl.cpp43
-rw-r--r--qpid/cpp/src/qmf/engine/MessageImpl.h44
-rw-r--r--qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp210
-rw-r--r--qpid/cpp/src/qmf/engine/ObjectIdImpl.h72
-rw-r--r--qpid/cpp/src/qmf/engine/ObjectImpl.cpp232
-rw-r--r--qpid/cpp/src/qmf/engine/ObjectImpl.h76
-rw-r--r--qpid/cpp/src/qmf/engine/Protocol.cpp52
-rw-r--r--qpid/cpp/src/qmf/engine/Protocol.h69
-rw-r--r--qpid/cpp/src/qmf/engine/QueryImpl.cpp103
-rw-r--r--qpid/cpp/src/qmf/engine/QueryImpl.h102
-rw-r--r--qpid/cpp/src/qmf/engine/ResilientConnection.cpp514
-rw-r--r--qpid/cpp/src/qmf/engine/SchemaImpl.cpp614
-rw-r--r--qpid/cpp/src/qmf/engine/SchemaImpl.h227
-rw-r--r--qpid/cpp/src/qmf/engine/SequenceManager.cpp96
-rw-r--r--qpid/cpp/src/qmf/engine/SequenceManager.h68
-rw-r--r--qpid/cpp/src/qmf/engine/ValueImpl.cpp571
-rw-r--r--qpid/cpp/src/qmf/engine/ValueImpl.h166
-rw-r--r--qpid/cpp/src/qmf/exceptions.cpp37
-rw-r--r--qpid/cpp/src/qmfc.mk57
-rw-r--r--qpid/cpp/src/qpid.pc.in11
-rw-r--r--qpid/cpp/src/qpid/Address.cpp38
-rw-r--r--qpid/cpp/src/qpid/BufferRef.h70
-rw-r--r--qpid/cpp/src/qpid/DataDir.cpp50
-rw-r--r--qpid/cpp/src/qpid/DataDir.h54
-rw-r--r--qpid/cpp/src/qpid/DisableExceptionLogging.h39
-rw-r--r--qpid/cpp/src/qpid/Exception.cpp67
-rw-r--r--qpid/cpp/src/qpid/Modules.cpp97
-rw-r--r--qpid/cpp/src/qpid/Modules.h44
-rw-r--r--qpid/cpp/src/qpid/Options.cpp200
-rw-r--r--qpid/cpp/src/qpid/Plugin.cpp94
-rw-r--r--qpid/cpp/src/qpid/Plugin.h129
-rw-r--r--qpid/cpp/src/qpid/RefCounted.h63
-rw-r--r--qpid/cpp/src/qpid/RefCountedBuffer.cpp43
-rw-r--r--qpid/cpp/src/qpid/RefCountedBuffer.h44
-rw-r--r--qpid/cpp/src/qpid/Sasl.h60
-rw-r--r--qpid/cpp/src/qpid/SaslFactory.cpp429
-rw-r--r--qpid/cpp/src/qpid/SaslFactory.h47
-rw-r--r--qpid/cpp/src/qpid/Serializer.h197
-rw-r--r--qpid/cpp/src/qpid/SessionId.cpp47
-rw-r--r--qpid/cpp/src/qpid/SessionState.cpp287
-rw-r--r--qpid/cpp/src/qpid/SessionState.h235
-rw-r--r--qpid/cpp/src/qpid/SharedObject.h55
-rw-r--r--qpid/cpp/src/qpid/StringUtils.cpp50
-rw-r--r--qpid/cpp/src/qpid/StringUtils.h45
-rw-r--r--qpid/cpp/src/qpid/Url.cpp265
-rwxr-xr-xqpid/cpp/src/qpid/Version.h40
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.cpp191
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.h86
-rw-r--r--qpid/cpp/src/qpid/acl/AclData.cpp261
-rw-r--r--qpid/cpp/src/qpid/acl/AclData.h84
-rw-r--r--qpid/cpp/src/qpid/acl/AclPlugin.cpp96
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.cpp581
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.h118
-rw-r--r--qpid/cpp/src/qpid/acl/AclValidator.cpp150
-rw-r--r--qpid/cpp/src/qpid/acl/AclValidator.h83
-rw-r--r--qpid/cpp/src/qpid/acl/management-schema.xml44
-rw-r--r--qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp1390
-rw-r--r--qpid/cpp/src/qpid/agent/ManagementAgentImpl.h298
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Array.cpp34
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Array.h124
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Body.h55
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Codec.h213
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp347
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Command.h62
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h60
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Connection.cpp153
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Connection.h83
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Control.h70
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Decimal.h51
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Exception.h96
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp50
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h90
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Header.cpp34
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Header.h53
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Holder.h103
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Map.cpp66
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Map.h188
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Packer.h195
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/SerializableString.h62
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp332
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h118
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Struct.h60
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp36
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Struct32.h64
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Unit.cpp65
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/Unit.h82
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h35
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp34
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h55
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp56
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/UnknownType.h87
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/apply.h86
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/built_in_types.h171
-rw-r--r--qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp84
-rw-r--r--qpid/cpp/src/qpid/assert.cpp47
-rw-r--r--qpid/cpp/src/qpid/assert.h38
-rw-r--r--qpid/cpp/src/qpid/broker/AclModule.h281
-rw-r--r--qpid/cpp/src/qpid/broker/AsyncCompletion.h201
-rw-r--r--qpid/cpp/src/qpid/broker/Bridge.cpp323
-rw-r--r--qpid/cpp/src/qpid/broker/Bridge.h111
-rw-r--r--qpid/cpp/src/qpid/broker/Broker.cpp967
-rw-r--r--qpid/cpp/src/qpid/broker/Broker.h351
-rw-r--r--qpid/cpp/src/qpid/broker/BrokerImportExport.h42
-rw-r--r--qpid/cpp/src/qpid/broker/Connection.cpp487
-rw-r--r--qpid/cpp/src/qpid/broker/Connection.h216
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionFactory.cpp66
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionFactory.h51
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionHandler.cpp364
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionHandler.h112
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionState.h117
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionToken.h40
-rw-r--r--qpid/cpp/src/qpid/broker/Consumer.h58
-rw-r--r--qpid/cpp/src/qpid/broker/Daemon.cpp219
-rw-r--r--qpid/cpp/src/qpid/broker/Daemon.h84
-rw-r--r--qpid/cpp/src/qpid/broker/Deliverable.h43
-rw-r--r--qpid/cpp/src/qpid/broker/DeliverableMessage.cpp45
-rw-r--r--qpid/cpp/src/qpid/broker/DeliverableMessage.h45
-rw-r--r--qpid/cpp/src/qpid/broker/DeliveryAdapter.h53
-rw-r--r--qpid/cpp/src/qpid/broker/DeliveryId.h35
-rw-r--r--qpid/cpp/src/qpid/broker/DeliveryRecord.cpp196
-rw-r--r--qpid/cpp/src/qpid/broker/DeliveryRecord.h143
-rw-r--r--qpid/cpp/src/qpid/broker/DirectExchange.cpp194
-rw-r--r--qpid/cpp/src/qpid/broker/DirectExchange.h74
-rw-r--r--qpid/cpp/src/qpid/broker/DtxAck.cpp73
-rw-r--r--qpid/cpp/src/qpid/broker/DtxAck.h48
-rw-r--r--qpid/cpp/src/qpid/broker/DtxBuffer.cpp83
-rw-r--r--qpid/cpp/src/qpid/broker/DtxBuffer.h57
-rw-r--r--qpid/cpp/src/qpid/broker/DtxManager.cpp171
-rw-r--r--qpid/cpp/src/qpid/broker/DtxManager.h74
-rw-r--r--qpid/cpp/src/qpid/broker/DtxTimeout.cpp35
-rw-r--r--qpid/cpp/src/qpid/broker/DtxTimeout.h48
-rw-r--r--qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp177
-rw-r--r--qpid/cpp/src/qpid/broker/DtxWorkRecord.h81
-rw-r--r--qpid/cpp/src/qpid/broker/Exchange.cpp403
-rw-r--r--qpid/cpp/src/qpid/broker/Exchange.h248
-rw-r--r--qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp116
-rw-r--r--qpid/cpp/src/qpid/broker/ExchangeRegistry.h93
-rw-r--r--qpid/cpp/src/qpid/broker/ExpiryPolicy.cpp38
-rw-r--r--qpid/cpp/src/qpid/broker/ExpiryPolicy.h46
-rw-r--r--qpid/cpp/src/qpid/broker/Fairshare.cpp186
-rw-r--r--qpid/cpp/src/qpid/broker/Fairshare.h61
-rw-r--r--qpid/cpp/src/qpid/broker/FanOutExchange.cpp119
-rw-r--r--qpid/cpp/src/qpid/broker/FanOutExchange.h74
-rw-r--r--qpid/cpp/src/qpid/broker/FedOps.h38
-rw-r--r--qpid/cpp/src/qpid/broker/HandlerImpl.h53
-rw-r--r--qpid/cpp/src/qpid/broker/HeadersExchange.cpp341
-rw-r--r--qpid/cpp/src/qpid/broker/HeadersExchange.h122
-rw-r--r--qpid/cpp/src/qpid/broker/LegacyLVQ.cpp116
-rw-r--r--qpid/cpp/src/qpid/broker/LegacyLVQ.h59
-rw-r--r--qpid/cpp/src/qpid/broker/Link.cpp474
-rw-r--r--qpid/cpp/src/qpid/broker/Link.h145
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.cpp399
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.h163
-rw-r--r--qpid/cpp/src/qpid/broker/Message.cpp452
-rw-r--r--qpid/cpp/src/qpid/broker/Message.h196
-rw-r--r--qpid/cpp/src/qpid/broker/MessageAdapter.cpp81
-rw-r--r--qpid/cpp/src/qpid/broker/MessageAdapter.h62
-rw-r--r--qpid/cpp/src/qpid/broker/MessageBuilder.cpp114
-rw-r--r--qpid/cpp/src/qpid/broker/MessageBuilder.h56
-rw-r--r--qpid/cpp/src/qpid/broker/MessageDeque.cpp140
-rw-r--r--qpid/cpp/src/qpid/broker/MessageDeque.h62
-rw-r--r--qpid/cpp/src/qpid/broker/MessageMap.cpp166
-rw-r--r--qpid/cpp/src/qpid/broker/MessageMap.h72
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStore.h205
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStoreModule.cpp180
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStoreModule.h87
-rw-r--r--qpid/cpp/src/qpid/broker/Messages.h117
-rw-r--r--qpid/cpp/src/qpid/broker/NameGenerator.cpp32
-rw-r--r--qpid/cpp/src/qpid/broker/NameGenerator.h39
-rw-r--r--qpid/cpp/src/qpid/broker/NullMessageStore.cpp167
-rw-r--r--qpid/cpp/src/qpid/broker/NullMessageStore.h100
-rw-r--r--qpid/cpp/src/qpid/broker/OwnershipToken.h38
-rw-r--r--qpid/cpp/src/qpid/broker/Persistable.h63
-rw-r--r--qpid/cpp/src/qpid/broker/PersistableConfig.h45
-rw-r--r--qpid/cpp/src/qpid/broker/PersistableExchange.h45
-rw-r--r--qpid/cpp/src/qpid/broker/PersistableMessage.cpp161
-rw-r--r--qpid/cpp/src/qpid/broker/PersistableMessage.h143
-rw-r--r--qpid/cpp/src/qpid/broker/PersistableQueue.h78
-rw-r--r--qpid/cpp/src/qpid/broker/PriorityQueue.cpp212
-rw-r--r--qpid/cpp/src/qpid/broker/PriorityQueue.h78
-rw-r--r--qpid/cpp/src/qpid/broker/Queue.cpp1225
-rw-r--r--qpid/cpp/src/qpid/broker/Queue.h390
-rw-r--r--qpid/cpp/src/qpid/broker/QueueBindings.cpp47
-rw-r--r--qpid/cpp/src/qpid/broker/QueueBindings.h61
-rw-r--r--qpid/cpp/src/qpid/broker/QueueCleaner.cpp74
-rw-r--r--qpid/cpp/src/qpid/broker/QueueCleaner.h59
-rw-r--r--qpid/cpp/src/qpid/broker/QueueEvents.cpp147
-rw-r--r--qpid/cpp/src/qpid/broker/QueueEvents.h85
-rw-r--r--qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp407
-rw-r--r--qpid/cpp/src/qpid/broker/QueueFlowLimit.h129
-rw-r--r--qpid/cpp/src/qpid/broker/QueueListeners.cpp98
-rw-r--r--qpid/cpp/src/qpid/broker/QueueListeners.h86
-rw-r--r--qpid/cpp/src/qpid/broker/QueueObserver.h42
-rw-r--r--qpid/cpp/src/qpid/broker/QueuePolicy.cpp360
-rw-r--r--qpid/cpp/src/qpid/broker/QueuePolicy.h123
-rw-r--r--qpid/cpp/src/qpid/broker/QueueRegistry.cpp127
-rw-r--r--qpid/cpp/src/qpid/broker/QueueRegistry.h151
-rw-r--r--qpid/cpp/src/qpid/broker/QueuedMessage.h48
-rw-r--r--qpid/cpp/src/qpid/broker/RateFlowcontrol.h105
-rw-r--r--qpid/cpp/src/qpid/broker/RateTracker.cpp51
-rw-r--r--qpid/cpp/src/qpid/broker/RateTracker.h57
-rw-r--r--qpid/cpp/src/qpid/broker/RecoverableConfig.h45
-rw-r--r--qpid/cpp/src/qpid/broker/RecoverableExchange.h52
-rw-r--r--qpid/cpp/src/qpid/broker/RecoverableMessage.h59
-rw-r--r--qpid/cpp/src/qpid/broker/RecoverableQueue.h59
-rw-r--r--qpid/cpp/src/qpid/broker/RecoverableTransaction.h49
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp48
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveredDequeue.h56
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp45
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveredEnqueue.h57
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveryManager.h61
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp278
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h58
-rw-r--r--qpid/cpp/src/qpid/broker/RetryList.cpp56
-rw-r--r--qpid/cpp/src/qpid/broker/RetryList.h54
-rw-r--r--qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp467
-rw-r--r--qpid/cpp/src/qpid/broker/SaslAuthenticator.h64
-rw-r--r--qpid/cpp/src/qpid/broker/SecureConnection.cpp90
-rw-r--r--qpid/cpp/src/qpid/broker/SecureConnection.h60
-rw-r--r--qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp75
-rw-r--r--qpid/cpp/src/qpid/broker/SecureConnectionFactory.h49
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.cpp823
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.h257
-rw-r--r--qpid/cpp/src/qpid/broker/SessionAdapter.cpp693
-rw-r--r--qpid/cpp/src/qpid/broker/SessionAdapter.h273
-rw-r--r--qpid/cpp/src/qpid/broker/SessionContext.h56
-rw-r--r--qpid/cpp/src/qpid/broker/SessionHandler.cpp120
-rw-r--r--qpid/cpp/src/qpid/broker/SessionHandler.h99
-rw-r--r--qpid/cpp/src/qpid/broker/SessionManager.cpp104
-rw-r--r--qpid/cpp/src/qpid/broker/SessionManager.h87
-rw-r--r--qpid/cpp/src/qpid/broker/SessionOutputException.h47
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.cpp593
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.h284
-rw-r--r--qpid/cpp/src/qpid/broker/SignalHandler.cpp54
-rw-r--r--qpid/cpp/src/qpid/broker/SignalHandler.h50
-rw-r--r--qpid/cpp/src/qpid/broker/StatefulQueueObserver.h63
-rw-r--r--qpid/cpp/src/qpid/broker/System.cpp84
-rw-r--r--qpid/cpp/src/qpid/broker/System.h51
-rw-r--r--qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp191
-rw-r--r--qpid/cpp/src/qpid/broker/ThresholdAlerts.h73
-rw-r--r--qpid/cpp/src/qpid/broker/TopicExchange.cpp692
-rw-r--r--qpid/cpp/src/qpid/broker/TopicExchange.h208
-rw-r--r--qpid/cpp/src/qpid/broker/TransactionalStore.h60
-rw-r--r--qpid/cpp/src/qpid/broker/TxAccept.cpp100
-rw-r--r--qpid/cpp/src/qpid/broker/TxAccept.h83
-rw-r--r--qpid/cpp/src/qpid/broker/TxBuffer.cpp80
-rw-r--r--qpid/cpp/src/qpid/broker/TxBuffer.h119
-rw-r--r--qpid/cpp/src/qpid/broker/TxOp.h46
-rw-r--r--qpid/cpp/src/qpid/broker/TxOpVisitor.h97
-rw-r--r--qpid/cpp/src/qpid/broker/TxPublish.cpp111
-rw-r--r--qpid/cpp/src/qpid/broker/TxPublish.h91
-rw-r--r--qpid/cpp/src/qpid/broker/Vhost.cpp49
-rw-r--r--qpid/cpp/src/qpid/broker/Vhost.h50
-rw-r--r--qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp40
-rw-r--r--qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp47
-rw-r--r--qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp193
-rw-r--r--qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp297
-rw-r--r--qpid/cpp/src/qpid/client/Bounds.cpp71
-rw-r--r--qpid/cpp/src/qpid/client/Bounds.h49
-rw-r--r--qpid/cpp/src/qpid/client/ChainableFrameHandler.h47
-rw-r--r--qpid/cpp/src/qpid/client/Completion.cpp40
-rw-r--r--qpid/cpp/src/qpid/client/CompletionImpl.h51
-rw-r--r--qpid/cpp/src/qpid/client/Connection.cpp161
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionAccess.h42
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionHandler.cpp356
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionHandler.h139
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionImpl.cpp451
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionImpl.h107
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionSettings.cpp57
-rw-r--r--qpid/cpp/src/qpid/client/Connector.cpp72
-rw-r--r--qpid/cpp/src/qpid/client/Connector.h84
-rw-r--r--qpid/cpp/src/qpid/client/Demux.cpp132
-rw-r--r--qpid/cpp/src/qpid/client/Demux.h103
-rw-r--r--qpid/cpp/src/qpid/client/Dispatcher.cpp151
-rw-r--r--qpid/cpp/src/qpid/client/Dispatcher.h87
-rw-r--r--qpid/cpp/src/qpid/client/Execution.h53
-rw-r--r--qpid/cpp/src/qpid/client/FailoverListener.cpp97
-rw-r--r--qpid/cpp/src/qpid/client/FailoverManager.cpp130
-rw-r--r--qpid/cpp/src/qpid/client/Future.cpp46
-rw-r--r--qpid/cpp/src/qpid/client/FutureCompletion.cpp48
-rw-r--r--qpid/cpp/src/qpid/client/FutureResult.cpp43
-rw-r--r--qpid/cpp/src/qpid/client/LoadPlugins.cpp64
-rw-r--r--qpid/cpp/src/qpid/client/LoadPlugins.h33
-rw-r--r--qpid/cpp/src/qpid/client/LocalQueue.cpp52
-rw-r--r--qpid/cpp/src/qpid/client/LocalQueueImpl.cpp78
-rw-r--r--qpid/cpp/src/qpid/client/LocalQueueImpl.h108
-rw-r--r--qpid/cpp/src/qpid/client/Message.cpp62
-rw-r--r--qpid/cpp/src/qpid/client/MessageImpl.cpp71
-rw-r--r--qpid/cpp/src/qpid/client/MessageImpl.h80
-rw-r--r--qpid/cpp/src/qpid/client/MessageListener.cpp24
-rw-r--r--qpid/cpp/src/qpid/client/MessageReplayTracker.cpp78
-rw-r--r--qpid/cpp/src/qpid/client/PrivateImplRef.h94
-rw-r--r--qpid/cpp/src/qpid/client/QueueOptions.cpp123
-rw-r--r--qpid/cpp/src/qpid/client/RdmaConnector.cpp431
-rw-r--r--qpid/cpp/src/qpid/client/Results.cpp75
-rw-r--r--qpid/cpp/src/qpid/client/Results.h56
-rw-r--r--qpid/cpp/src/qpid/client/SessionBase_0_10.cpp85
-rw-r--r--qpid/cpp/src/qpid/client/SessionBase_0_10Access.h42
-rw-r--r--qpid/cpp/src/qpid/client/SessionImpl.cpp824
-rw-r--r--qpid/cpp/src/qpid/client/SessionImpl.h254
-rw-r--r--qpid/cpp/src/qpid/client/SslConnector.cpp381
-rw-r--r--qpid/cpp/src/qpid/client/StateManager.cpp100
-rw-r--r--qpid/cpp/src/qpid/client/StateManager.h50
-rw-r--r--qpid/cpp/src/qpid/client/Subscription.cpp55
-rw-r--r--qpid/cpp/src/qpid/client/SubscriptionImpl.cpp169
-rw-r--r--qpid/cpp/src/qpid/client/SubscriptionImpl.h125
-rw-r--r--qpid/cpp/src/qpid/client/SubscriptionManager.cpp106
-rw-r--r--qpid/cpp/src/qpid/client/SubscriptionManagerImpl.cpp162
-rw-r--r--qpid/cpp/src/qpid/client/SubscriptionManagerImpl.h278
-rw-r--r--qpid/cpp/src/qpid/client/TCPConnector.cpp331
-rw-r--r--qpid/cpp/src/qpid/client/TCPConnector.h120
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp131
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h87
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp966
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h64
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp323
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h80
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp361
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h100
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h52
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h47
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp104
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h48
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp225
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h151
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp182
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h160
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp525
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h247
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp79
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h42
-rw-r--r--qpid/cpp/src/qpid/client/windows/SaslFactory.cpp177
-rw-r--r--qpid/cpp/src/qpid/client/windows/SslConnector.cpp181
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.cpp1176
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.h308
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterMap.cpp176
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterMap.h106
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp123
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterSettings.h50
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterTimer.cpp138
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterTimer.h64
-rw-r--r--qpid/cpp/src/qpid/cluster/Connection.cpp728
-rw-r--r--qpid/cpp/src/qpid/cluster/Connection.h276
-rw-r--r--qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp92
-rw-r--r--qpid/cpp/src/qpid/cluster/ConnectionCodec.h82
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.cpp280
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.h236
-rw-r--r--qpid/cpp/src/qpid/cluster/Decoder.cpp65
-rw-r--r--qpid/cpp/src/qpid/cluster/Decoder.h59
-rw-r--r--qpid/cpp/src/qpid/cluster/Dispatchable.h52
-rw-r--r--qpid/cpp/src/qpid/cluster/ErrorCheck.cpp155
-rw-r--r--qpid/cpp/src/qpid/cluster/ErrorCheck.h90
-rw-r--r--qpid/cpp/src/qpid/cluster/Event.cpp134
-rw-r--r--qpid/cpp/src/qpid/cluster/Event.h116
-rw-r--r--qpid/cpp/src/qpid/cluster/EventFrame.cpp41
-rw-r--r--qpid/cpp/src/qpid/cluster/EventFrame.h60
-rw-r--r--qpid/cpp/src/qpid/cluster/ExpiryPolicy.cpp126
-rw-r--r--qpid/cpp/src/qpid/cluster/ExpiryPolicy.h93
-rw-r--r--qpid/cpp/src/qpid/cluster/FailoverExchange.cpp104
-rw-r--r--qpid/cpp/src/qpid/cluster/FailoverExchange.h71
-rw-r--r--qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp226
-rw-r--r--qpid/cpp/src/qpid/cluster/InitialStatusMap.h91
-rw-r--r--qpid/cpp/src/qpid/cluster/LockedConnectionMap.h65
-rw-r--r--qpid/cpp/src/qpid/cluster/McastFrameHandler.h46
-rw-r--r--qpid/cpp/src/qpid/cluster/MemberSet.cpp60
-rw-r--r--qpid/cpp/src/qpid/cluster/MemberSet.h45
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.cpp104
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.h92
-rw-r--r--qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h47
-rw-r--r--qpid/cpp/src/qpid/cluster/Numbering.h68
-rw-r--r--qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp125
-rw-r--r--qpid/cpp/src/qpid/cluster/OutputInterceptor.h79
-rw-r--r--qpid/cpp/src/qpid/cluster/PollableQueue.h89
-rw-r--r--qpid/cpp/src/qpid/cluster/PollerDispatch.cpp69
-rw-r--r--qpid/cpp/src/qpid/cluster/PollerDispatch.h60
-rw-r--r--qpid/cpp/src/qpid/cluster/ProxyInputHandler.h56
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum.h32
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_cman.cpp105
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_cman.h65
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_null.h42
-rw-r--r--qpid/cpp/src/qpid/cluster/RetractClient.cpp62
-rw-r--r--qpid/cpp/src/qpid/cluster/RetractClient.h50
-rw-r--r--qpid/cpp/src/qpid/cluster/SecureConnectionFactory.cpp73
-rw-r--r--qpid/cpp/src/qpid/cluster/SecureConnectionFactory.h58
-rw-r--r--qpid/cpp/src/qpid/cluster/StoreStatus.cpp170
-rw-r--r--qpid/cpp/src/qpid/cluster/StoreStatus.h68
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateClient.cpp641
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateClient.h133
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp77
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateDataExchange.h84
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateExchange.cpp47
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateExchange.h45
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateReceiver.h45
-rw-r--r--qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp137
-rw-r--r--qpid/cpp/src/qpid/cluster/management-schema.xml61
-rw-r--r--qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp63
-rw-r--r--qpid/cpp/src/qpid/cluster/types.h84
-rw-r--r--qpid/cpp/src/qpid/console/Agent.cpp30
-rw-r--r--qpid/cpp/src/qpid/console/Broker.cpp333
-rw-r--r--qpid/cpp/src/qpid/console/ClassKey.cpp105
-rw-r--r--qpid/cpp/src/qpid/console/Event.cpp205
-rw-r--r--qpid/cpp/src/qpid/console/Object.cpp384
-rw-r--r--qpid/cpp/src/qpid/console/ObjectId.cpp91
-rw-r--r--qpid/cpp/src/qpid/console/Package.cpp41
-rw-r--r--qpid/cpp/src/qpid/console/Schema.cpp165
-rw-r--r--qpid/cpp/src/qpid/console/SequenceManager.cpp48
-rw-r--r--qpid/cpp/src/qpid/console/SessionManager.cpp517
-rw-r--r--qpid/cpp/src/qpid/console/Value.cpp171
-rw-r--r--qpid/cpp/src/qpid/framing/AMQBody.cpp64
-rw-r--r--qpid/cpp/src/qpid/framing/AMQBody.h86
-rw-r--r--qpid/cpp/src/qpid/framing/AMQCommandControlBody.h70
-rw-r--r--qpid/cpp/src/qpid/framing/AMQContentBody.cpp46
-rw-r--r--qpid/cpp/src/qpid/framing/AMQContentBody.h55
-rw-r--r--qpid/cpp/src/qpid/framing/AMQDataBlock.h42
-rw-r--r--qpid/cpp/src/qpid/framing/AMQFrame.cpp153
-rw-r--r--qpid/cpp/src/qpid/framing/AMQFrame.h112
-rw-r--r--qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp63
-rw-r--r--qpid/cpp/src/qpid/framing/AMQHeaderBody.h109
-rw-r--r--qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp29
-rw-r--r--qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h48
-rw-r--r--qpid/cpp/src/qpid/framing/AMQMethodBody.cpp28
-rw-r--r--qpid/cpp/src/qpid/framing/AMQMethodBody.h72
-rw-r--r--qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h40
-rw-r--r--qpid/cpp/src/qpid/framing/AccumulatedAck.cpp164
-rw-r--r--qpid/cpp/src/qpid/framing/AccumulatedAck.h77
-rw-r--r--qpid/cpp/src/qpid/framing/Array.cpp131
-rw-r--r--qpid/cpp/src/qpid/framing/Blob.cpp31
-rw-r--r--qpid/cpp/src/qpid/framing/Blob.h21
-rw-r--r--qpid/cpp/src/qpid/framing/BodyFactory.h47
-rw-r--r--qpid/cpp/src/qpid/framing/BodyHandler.cpp56
-rw-r--r--qpid/cpp/src/qpid/framing/BodyHandler.h56
-rw-r--r--qpid/cpp/src/qpid/framing/Buffer.cpp345
-rw-r--r--qpid/cpp/src/qpid/framing/ChannelHandler.h53
-rw-r--r--qpid/cpp/src/qpid/framing/Endian.cpp52
-rw-r--r--qpid/cpp/src/qpid/framing/Endian.h46
-rw-r--r--qpid/cpp/src/qpid/framing/FieldTable.cpp247
-rw-r--r--qpid/cpp/src/qpid/framing/FieldValue.cpp234
-rw-r--r--qpid/cpp/src/qpid/framing/FrameDecoder.cpp81
-rw-r--r--qpid/cpp/src/qpid/framing/FrameDecoder.h52
-rw-r--r--qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h60
-rw-r--r--qpid/cpp/src/qpid/framing/FrameHandler.h33
-rw-r--r--qpid/cpp/src/qpid/framing/FrameSet.cpp105
-rw-r--r--qpid/cpp/src/qpid/framing/FrameSet.h119
-rw-r--r--qpid/cpp/src/qpid/framing/Handler.h101
-rw-r--r--qpid/cpp/src/qpid/framing/HeaderProperties.h44
-rw-r--r--qpid/cpp/src/qpid/framing/InitiationHandler.cpp24
-rw-r--r--qpid/cpp/src/qpid/framing/InitiationHandler.h41
-rw-r--r--qpid/cpp/src/qpid/framing/InputHandler.h41
-rw-r--r--qpid/cpp/src/qpid/framing/Invoker.h86
-rw-r--r--qpid/cpp/src/qpid/framing/IsInSequenceSet.h51
-rw-r--r--qpid/cpp/src/qpid/framing/List.cpp84
-rw-r--r--qpid/cpp/src/qpid/framing/MethodBodyFactory.h45
-rw-r--r--qpid/cpp/src/qpid/framing/MethodContent.h40
-rw-r--r--qpid/cpp/src/qpid/framing/ModelMethod.h49
-rw-r--r--qpid/cpp/src/qpid/framing/OutputHandler.h42
-rw-r--r--qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp66
-rw-r--r--qpid/cpp/src/qpid/framing/ProtocolInitiation.h59
-rw-r--r--qpid/cpp/src/qpid/framing/ProtocolVersion.cpp44
-rw-r--r--qpid/cpp/src/qpid/framing/Proxy.cpp51
-rw-r--r--qpid/cpp/src/qpid/framing/Proxy.h64
-rw-r--r--qpid/cpp/src/qpid/framing/ResizableBuffer.h60
-rw-r--r--qpid/cpp/src/qpid/framing/SendContent.cpp66
-rw-r--r--qpid/cpp/src/qpid/framing/SendContent.h56
-rw-r--r--qpid/cpp/src/qpid/framing/SequenceNumber.cpp50
-rw-r--r--qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp90
-rw-r--r--qpid/cpp/src/qpid/framing/SequenceNumberSet.h69
-rw-r--r--qpid/cpp/src/qpid/framing/SequenceSet.cpp102
-rw-r--r--qpid/cpp/src/qpid/framing/TemplateVisitor.h89
-rw-r--r--qpid/cpp/src/qpid/framing/TransferContent.cpp102
-rw-r--r--qpid/cpp/src/qpid/framing/TransferContent.h64
-rw-r--r--qpid/cpp/src/qpid/framing/TypeFilter.h51
-rw-r--r--qpid/cpp/src/qpid/framing/Uuid.cpp97
-rw-r--r--qpid/cpp/src/qpid/framing/Visitor.h92
-rw-r--r--qpid/cpp/src/qpid/framing/amqp_framing.h32
-rw-r--r--qpid/cpp/src/qpid/framing/frame_functors.h116
-rw-r--r--qpid/cpp/src/qpid/framing/variant.h91
-rw-r--r--qpid/cpp/src/qpid/log/Helpers.h79
-rw-r--r--qpid/cpp/src/qpid/log/Logger.cpp167
-rw-r--r--qpid/cpp/src/qpid/log/Options.cpp111
-rw-r--r--qpid/cpp/src/qpid/log/OstreamOutput.cpp41
-rw-r--r--qpid/cpp/src/qpid/log/OstreamOutput.h41
-rw-r--r--qpid/cpp/src/qpid/log/Selector.cpp68
-rw-r--r--qpid/cpp/src/qpid/log/Statement.cpp83
-rw-r--r--qpid/cpp/src/qpid/log/posix/SinkOptions.cpp215
-rw-r--r--qpid/cpp/src/qpid/log/posix/SinkOptions.h64
-rw-r--r--qpid/cpp/src/qpid/log/windows/SinkOptions.cpp148
-rw-r--r--qpid/cpp/src/qpid/log/windows/SinkOptions.h54
-rw-r--r--qpid/cpp/src/qpid/management/Buffer.cpp106
-rw-r--r--qpid/cpp/src/qpid/management/ConnectionSettings.cpp40
-rw-r--r--qpid/cpp/src/qpid/management/Manageable.cpp53
-rw-r--r--qpid/cpp/src/qpid/management/ManagementAgent.cpp3121
-rw-r--r--qpid/cpp/src/qpid/management/ManagementAgent.h432
-rw-r--r--qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp67
-rw-r--r--qpid/cpp/src/qpid/management/ManagementDirectExchange.h59
-rw-r--r--qpid/cpp/src/qpid/management/ManagementObject.cpp385
-rw-r--r--qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp75
-rw-r--r--qpid/cpp/src/qpid/management/ManagementTopicExchange.h63
-rw-r--r--qpid/cpp/src/qpid/management/Mutex.cpp29
-rw-r--r--qpid/cpp/src/qpid/memory.h32
-rw-r--r--qpid/cpp/src/qpid/messaging/Address.cpp151
-rw-r--r--qpid/cpp/src/qpid/messaging/AddressParser.cpp265
-rw-r--r--qpid/cpp/src/qpid/messaging/AddressParser.h66
-rw-r--r--qpid/cpp/src/qpid/messaging/Connection.cpp82
-rw-r--r--qpid/cpp/src/qpid/messaging/ConnectionImpl.h52
-rw-r--r--qpid/cpp/src/qpid/messaging/Duration.cpp55
-rw-r--r--qpid/cpp/src/qpid/messaging/FailoverUpdates.cpp85
-rw-r--r--qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp64
-rw-r--r--qpid/cpp/src/qpid/messaging/Message.cpp148
-rw-r--r--qpid/cpp/src/qpid/messaging/MessageImpl.cpp79
-rw-r--r--qpid/cpp/src/qpid/messaging/MessageImpl.h90
-rw-r--r--qpid/cpp/src/qpid/messaging/PrivateImplRef.h94
-rw-r--r--qpid/cpp/src/qpid/messaging/Receiver.cpp48
-rw-r--r--qpid/cpp/src/qpid/messaging/ReceiverImpl.h52
-rw-r--r--qpid/cpp/src/qpid/messaging/Sender.cpp44
-rw-r--r--qpid/cpp/src/qpid/messaging/SenderImpl.h47
-rw-r--r--qpid/cpp/src/qpid/messaging/Session.cpp109
-rw-r--r--qpid/cpp/src/qpid/messaging/SessionImpl.h63
-rw-r--r--qpid/cpp/src/qpid/messaging/exceptions.cpp58
-rw-r--r--qpid/cpp/src/qpid/pointer_to_other.h62
-rw-r--r--qpid/cpp/src/qpid/ptr_map.h57
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp201
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicatingEventListener.h78
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicationExchange.cpp234
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicationExchange.h72
-rw-r--r--qpid/cpp/src/qpid/replication/constants.h34
-rw-r--r--qpid/cpp/src/qpid/store/CMakeLists.txt111
-rw-r--r--qpid/cpp/src/qpid/store/MessageStorePlugin.cpp469
-rw-r--r--qpid/cpp/src/qpid/store/MessageStorePlugin.h288
-rw-r--r--qpid/cpp/src/qpid/store/StorageProvider.h343
-rw-r--r--qpid/cpp/src/qpid/store/StoreException.h49
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Log.cpp182
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Log.h78
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Lsn.h36
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp1121
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp406
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/MessageLog.h107
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Messages.cpp472
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Messages.h144
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Transaction.cpp83
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Transaction.h146
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp428
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.h104
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp67
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.h85
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp165
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.h88
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp64
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.h62
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp133
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.h61
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp86
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.h54
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp91
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.h64
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/Exception.h66
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp1305
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp267
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h100
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp184
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.h85
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/Recordset.cpp92
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/Recordset.h75
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.cpp71
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.h67
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/State.cpp45
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/State.h52
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/TplRecordset.cpp128
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/TplRecordset.h58
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/VariantHelper.cpp71
-rw-r--r--qpid/cpp/src/qpid/store/ms-sql/VariantHelper.h61
-rw-r--r--qpid/cpp/src/qpid/sys/AggregateOutput.cpp88
-rw-r--r--qpid/cpp/src/qpid/sys/AggregateOutput.h77
-rw-r--r--qpid/cpp/src/qpid/sys/AsynchIO.h160
-rw-r--r--qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp228
-rw-r--r--qpid/cpp/src/qpid/sys/AsynchIOHandler.h80
-rw-r--r--qpid/cpp/src/qpid/sys/AtomicCount.h52
-rw-r--r--qpid/cpp/src/qpid/sys/AtomicValue.h39
-rw-r--r--qpid/cpp/src/qpid/sys/AtomicValue_gcc.h68
-rw-r--r--qpid/cpp/src/qpid/sys/AtomicValue_mutex.h83
-rw-r--r--qpid/cpp/src/qpid/sys/BlockingQueue.h129
-rw-r--r--qpid/cpp/src/qpid/sys/ClusterSafe.cpp66
-rw-r--r--qpid/cpp/src/qpid/sys/ClusterSafe.h87
-rw-r--r--qpid/cpp/src/qpid/sys/Codec.h52
-rw-r--r--qpid/cpp/src/qpid/sys/ConnectionCodec.h68
-rw-r--r--qpid/cpp/src/qpid/sys/ConnectionInputHandler.h52
-rw-r--r--qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h54
-rw-r--r--qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h43
-rw-r--r--qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h56
-rw-r--r--qpid/cpp/src/qpid/sys/CopyOnWriteArray.h156
-rw-r--r--qpid/cpp/src/qpid/sys/DeletionManager.h162
-rw-r--r--qpid/cpp/src/qpid/sys/DispatchHandle.cpp352
-rw-r--r--qpid/cpp/src/qpid/sys/DispatchHandle.h150
-rw-r--r--qpid/cpp/src/qpid/sys/Dispatcher.cpp40
-rw-r--r--qpid/cpp/src/qpid/sys/Dispatcher.h44
-rwxr-xr-xqpid/cpp/src/qpid/sys/FileSysDir.h62
-rw-r--r--qpid/cpp/src/qpid/sys/Fork.h24
-rw-r--r--qpid/cpp/src/qpid/sys/LockFile.h64
-rw-r--r--qpid/cpp/src/qpid/sys/LockPtr.h89
-rw-r--r--qpid/cpp/src/qpid/sys/OutputControl.h43
-rw-r--r--qpid/cpp/src/qpid/sys/OutputTask.h41
-rwxr-xr-xqpid/cpp/src/qpid/sys/PipeHandle.h51
-rw-r--r--qpid/cpp/src/qpid/sys/PollableCondition.h64
-rw-r--r--qpid/cpp/src/qpid/sys/PollableQueue.h176
-rw-r--r--qpid/cpp/src/qpid/sys/Poller.h135
-rw-r--r--qpid/cpp/src/qpid/sys/ProtocolFactory.h57
-rw-r--r--qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp399
-rw-r--r--qpid/cpp/src/qpid/sys/Runnable.cpp32
-rw-r--r--qpid/cpp/src/qpid/sys/ScopedIncrement.h67
-rw-r--r--qpid/cpp/src/qpid/sys/SecurityLayer.h42
-rw-r--r--qpid/cpp/src/qpid/sys/SecuritySettings.h58
-rw-r--r--qpid/cpp/src/qpid/sys/Semaphore.h79
-rw-r--r--qpid/cpp/src/qpid/sys/Shlib.cpp38
-rw-r--r--qpid/cpp/src/qpid/sys/Shlib.h76
-rw-r--r--qpid/cpp/src/qpid/sys/ShutdownHandler.h37
-rw-r--r--qpid/cpp/src/qpid/sys/Socket.h103
-rw-r--r--qpid/cpp/src/qpid/sys/SocketAddress.h53
-rw-r--r--qpid/cpp/src/qpid/sys/SslPlugin.cpp186
-rw-r--r--qpid/cpp/src/qpid/sys/StateMonitor.h78
-rw-r--r--qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp152
-rw-r--r--qpid/cpp/src/qpid/sys/TimeoutHandler.h39
-rw-r--r--qpid/cpp/src/qpid/sys/Timer.cpp205
-rw-r--r--qpid/cpp/src/qpid/sys/Timer.h107
-rw-r--r--qpid/cpp/src/qpid/sys/TimerWarnings.cpp82
-rw-r--r--qpid/cpp/src/qpid/sys/TimerWarnings.h81
-rw-r--r--qpid/cpp/src/qpid/sys/Waitable.h117
-rw-r--r--qpid/cpp/src/qpid/sys/alloca.h42
-rw-r--r--qpid/cpp/src/qpid/sys/apr/APRBase.cpp89
-rw-r--r--qpid/cpp/src/qpid/sys/apr/APRBase.h74
-rw-r--r--qpid/cpp/src/qpid/sys/apr/APRPool.cpp41
-rw-r--r--qpid/cpp/src/qpid/sys/apr/APRPool.h50
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Condition.h84
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Mutex.h124
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Shlib.cpp49
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Socket.cpp114
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Thread.cpp34
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Thread.h106
-rw-r--r--qpid/cpp/src/qpid/sys/apr/Time.cpp36
-rw-r--r--qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp127
-rw-r--r--qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h68
-rw-r--r--qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp674
-rw-r--r--qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp611
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/FileSysDir.cpp54
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Fork.cpp129
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Fork.h82
-rw-r--r--qpid/cpp/src/qpid/sys/posix/IOHandle.cpp44
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/LockFile.cpp108
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Mutex.cpp46
-rw-r--r--qpid/cpp/src/qpid/sys/posix/PidFile.h62
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/PipeHandle.cpp64
-rw-r--r--qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp124
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Shlib.cpp60
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Socket.cpp247
-rw-r--r--qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp107
-rw-r--r--qpid/cpp/src/qpid/sys/posix/StrError.cpp41
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/SystemInfo.cpp153
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Thread.cpp88
-rw-r--r--qpid/cpp/src/qpid/sys/posix/Time.cpp121
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp247
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp720
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/RdmaIO.h250
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp210
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/rdma_exception.h69
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp105
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/rdma_factories.h40
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp566
-rw-r--r--qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h287
-rw-r--r--qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp444
-rwxr-xr-xqpid/cpp/src/qpid/sys/solaris/SystemInfo.cpp124
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp195
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslHandler.h78
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslIo.cpp447
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslIo.h172
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp360
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslSocket.h132
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/check.cpp85
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/check.h57
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/util.cpp120
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/util.h50
-rw-r--r--qpid/cpp/src/qpid/sys/uuid.h28
-rw-r--r--qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp755
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/AsynchIoResult.h204
-rw-r--r--qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp53
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/IOHandle.cpp42
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h61
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/IocpPoller.cpp219
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/LockFile.cpp64
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/PipeHandle.cpp101
-rw-r--r--qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp114
-rw-r--r--qpid/cpp/src/qpid/sys/windows/Shlib.cpp54
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/Socket.cpp289
-rw-r--r--qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp76
-rw-r--r--qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp661
-rw-r--r--qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h191
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/StrError.cpp52
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/SystemInfo.cpp203
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/Thread.cpp100
-rw-r--r--qpid/cpp/src/qpid/sys/windows/Time.cpp136
-rw-r--r--qpid/cpp/src/qpid/sys/windows/mingw32_compat.h39
-rw-r--r--qpid/cpp/src/qpid/sys/windows/uuid.cpp67
-rw-r--r--qpid/cpp/src/qpid/sys/windows/uuid.h39
-rw-r--r--qpid/cpp/src/qpid/types/Exception.cpp30
-rw-r--r--qpid/cpp/src/qpid/types/Uuid.cpp140
-rw-r--r--qpid/cpp/src/qpid/types/Variant.cpp890
-rw-r--r--qpid/cpp/src/qpid/xml/XmlExchange.cpp408
-rw-r--r--qpid/cpp/src/qpid/xml/XmlExchange.h119
-rw-r--r--qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp69
-rw-r--r--qpid/cpp/src/qpidd.cpp86
-rw-r--r--qpid/cpp/src/qpidd.h70
-rw-r--r--qpid/cpp/src/rdma.cmake118
-rw-r--r--qpid/cpp/src/replication.mk52
-rw-r--r--qpid/cpp/src/ssl.cmake117
-rw-r--r--qpid/cpp/src/ssl.mk64
-rw-r--r--qpid/cpp/src/tests/.valgrind.supp202
-rw-r--r--qpid/cpp/src/tests/AccumulatedAckTest.cpp237
-rw-r--r--qpid/cpp/src/tests/Address.cpp124
-rw-r--r--qpid/cpp/src/tests/Array.cpp84
-rw-r--r--qpid/cpp/src/tests/AsyncCompletion.cpp120
-rw-r--r--qpid/cpp/src/tests/AtomicValue.cpp54
-rw-r--r--qpid/cpp/src/tests/Blob.cpp21
-rw-r--r--qpid/cpp/src/tests/BrokerFixture.h154
-rw-r--r--qpid/cpp/src/tests/BrokerMgmtAgent.cpp792
-rw-r--r--qpid/cpp/src/tests/BrokerMgmtAgent.xml38
-rw-r--r--qpid/cpp/src/tests/CMakeLists.txt356
-rw-r--r--qpid/cpp/src/tests/ClientMessage.cpp46
-rw-r--r--qpid/cpp/src/tests/ClientMessageTest.cpp51
-rw-r--r--qpid/cpp/src/tests/ClientSessionTest.cpp682
-rw-r--r--qpid/cpp/src/tests/ClusterFailover.cpp115
-rw-r--r--qpid/cpp/src/tests/ClusterFixture.cpp160
-rw-r--r--qpid/cpp/src/tests/ClusterFixture.h115
-rw-r--r--qpid/cpp/src/tests/ConnectionOptions.h62
-rw-r--r--qpid/cpp/src/tests/ConsoleTest.cpp46
-rw-r--r--qpid/cpp/src/tests/DeliveryRecordTest.cpp67
-rw-r--r--qpid/cpp/src/tests/DispatcherTest.cpp241
-rw-r--r--qpid/cpp/src/tests/DtxWorkRecordTest.cpp193
-rw-r--r--qpid/cpp/src/tests/ExchangeTest.cpp289
-rw-r--r--qpid/cpp/src/tests/FieldTable.cpp213
-rw-r--r--qpid/cpp/src/tests/FieldValue.cpp95
-rw-r--r--qpid/cpp/src/tests/ForkedBroker.cpp158
-rw-r--r--qpid/cpp/src/tests/ForkedBroker.h82
-rw-r--r--qpid/cpp/src/tests/Frame.cpp85
-rw-r--r--qpid/cpp/src/tests/FrameDecoder.cpp78
-rw-r--r--qpid/cpp/src/tests/FramingTest.cpp167
-rw-r--r--qpid/cpp/src/tests/HeaderTest.cpp114
-rw-r--r--qpid/cpp/src/tests/HeadersExchangeTest.cpp120
-rw-r--r--qpid/cpp/src/tests/InitialStatusMap.cpp235
-rw-r--r--qpid/cpp/src/tests/InlineAllocator.cpp68
-rw-r--r--qpid/cpp/src/tests/InlineVector.cpp128
-rw-r--r--qpid/cpp/src/tests/Makefile.am398
-rw-r--r--qpid/cpp/src/tests/ManagementTest.cpp124
-rw-r--r--qpid/cpp/src/tests/MessageBuilderTest.cpp190
-rw-r--r--qpid/cpp/src/tests/MessageReplayTracker.cpp102
-rw-r--r--qpid/cpp/src/tests/MessageTest.cpp92
-rw-r--r--qpid/cpp/src/tests/MessageUtils.h63
-rw-r--r--qpid/cpp/src/tests/MessagingFixture.h345
-rw-r--r--qpid/cpp/src/tests/MessagingSessionTests.cpp997
-rw-r--r--qpid/cpp/src/tests/MessagingThreadTests.cpp144
-rw-r--r--qpid/cpp/src/tests/PartialFailure.cpp291
-rw-r--r--qpid/cpp/src/tests/PollableCondition.cpp109
-rw-r--r--qpid/cpp/src/tests/PollerTest.cpp263
-rw-r--r--qpid/cpp/src/tests/ProxyTest.cpp56
-rw-r--r--qpid/cpp/src/tests/Qmf2.cpp320
-rw-r--r--qpid/cpp/src/tests/QueueEvents.cpp238
-rw-r--r--qpid/cpp/src/tests/QueueFlowLimitTest.cpp463
-rw-r--r--qpid/cpp/src/tests/QueueOptionsTest.cpp102
-rw-r--r--qpid/cpp/src/tests/QueuePolicyTest.cpp406
-rw-r--r--qpid/cpp/src/tests/QueueRegistryTest.cpp100
-rw-r--r--qpid/cpp/src/tests/QueueTest.cpp1124
-rw-r--r--qpid/cpp/src/tests/README.txt54
-rw-r--r--qpid/cpp/src/tests/RangeSet.cpp146
-rw-r--r--qpid/cpp/src/tests/RateFlowcontrolTest.cpp71
-rw-r--r--qpid/cpp/src/tests/RefCounted.cpp55
-rw-r--r--qpid/cpp/src/tests/ReplicationTest.cpp144
-rw-r--r--qpid/cpp/src/tests/RetryList.cpp111
-rw-r--r--qpid/cpp/src/tests/SequenceNumberTest.cpp209
-rw-r--r--qpid/cpp/src/tests/SequenceSet.cpp187
-rw-r--r--qpid/cpp/src/tests/SessionState.cpp304
-rw-r--r--qpid/cpp/src/tests/Shlib.cpp77
-rw-r--r--qpid/cpp/src/tests/SocketProxy.h183
-rw-r--r--qpid/cpp/src/tests/Statistics.cpp131
-rw-r--r--qpid/cpp/src/tests/Statistics.h111
-rw-r--r--qpid/cpp/src/tests/StoreStatus.cpp117
-rw-r--r--qpid/cpp/src/tests/StringUtils.cpp77
-rw-r--r--qpid/cpp/src/tests/TestMessageStore.h63
-rw-r--r--qpid/cpp/src/tests/TestOptions.h79
-rw-r--r--qpid/cpp/src/tests/TimerTest.cpp130
-rw-r--r--qpid/cpp/src/tests/TopicExchangeTest.cpp406
-rw-r--r--qpid/cpp/src/tests/TxBufferTest.cpp181
-rw-r--r--qpid/cpp/src/tests/TxMocks.h235
-rw-r--r--qpid/cpp/src/tests/TxPublishTest.cpp99
-rw-r--r--qpid/cpp/src/tests/Url.cpp90
-rw-r--r--qpid/cpp/src/tests/Uuid.cpp137
-rw-r--r--qpid/cpp/src/tests/Variant.cpp775
-rw-r--r--qpid/cpp/src/tests/XmlClientSessionTest.cpp297
-rwxr-xr-xqpid/cpp/src/tests/acl.py1077
-rwxr-xr-xqpid/cpp/src/tests/ais_check34
-rw-r--r--qpid/cpp/src/tests/ais_test.cpp23
-rwxr-xr-xqpid/cpp/src/tests/allhosts77
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/Map.cpp98
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp49
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/apply.cpp99
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/handlers.cpp125
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/serialize.cpp429
-rw-r--r--qpid/cpp/src/tests/background.ps155
-rwxr-xr-xqpid/cpp/src/tests/benchmark95
-rw-r--r--qpid/cpp/src/tests/brokermgmt.mk44
-rw-r--r--qpid/cpp/src/tests/brokertest.py671
-rwxr-xr-xqpid/cpp/src/tests/cli_tests.py475
-rw-r--r--qpid/cpp/src/tests/cluster.cmake90
-rw-r--r--qpid/cpp/src/tests/cluster.mk100
-rw-r--r--qpid/cpp/src/tests/cluster_authentication_soak.cpp310
-rwxr-xr-xqpid/cpp/src/tests/cluster_python_tests27
-rw-r--r--qpid/cpp/src/tests/cluster_python_tests_failing.txt32
-rwxr-xr-xqpid/cpp/src/tests/cluster_read_credit27
-rw-r--r--qpid/cpp/src/tests/cluster_test.cpp1231
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_logs.py119
-rw-r--r--qpid/cpp/src/tests/cluster_test_scripts/README.txt20
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_scripts/cluster_check37
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_scripts/cluster_start56
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_scripts/cluster_stop38
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_scripts/config_example.sh44
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_scripts/perftest54
-rw-r--r--qpid/cpp/src/tests/cluster_tests.fail3
-rwxr-xr-xqpid/cpp/src/tests/cluster_tests.py1057
-rwxr-xr-xqpid/cpp/src/tests/clustered_replication_test110
-rw-r--r--qpid/cpp/src/tests/config.null1
-rw-r--r--qpid/cpp/src/tests/consume.cpp131
-rw-r--r--qpid/cpp/src/tests/datagen.cpp103
-rw-r--r--qpid/cpp/src/tests/declare_queues.cpp101
-rw-r--r--qpid/cpp/src/tests/dlclose_noop.c30
-rwxr-xr-xqpid/cpp/src/tests/dynamic_log_level_test57
-rw-r--r--qpid/cpp/src/tests/echotest.cpp158
-rw-r--r--qpid/cpp/src/tests/exception_test.cpp127
-rw-r--r--qpid/cpp/src/tests/failover_soak.cpp827
-rwxr-xr-xqpid/cpp/src/tests/fanout_perftest22
-rwxr-xr-xqpid/cpp/src/tests/federated_cluster_test152
-rwxr-xr-xqpid/cpp/src/tests/federated_cluster_test_with_node_failure23
-rwxr-xr-xqpid/cpp/src/tests/federated_topic_test129
-rwxr-xr-xqpid/cpp/src/tests/federation.py2091
-rw-r--r--qpid/cpp/src/tests/find_prog.ps136
-rw-r--r--qpid/cpp/src/tests/header_test.cpp59
-rwxr-xr-xqpid/cpp/src/tests/header_test.py86
-rw-r--r--qpid/cpp/src/tests/headers_federation.py99
-rw-r--r--qpid/cpp/src/tests/install_env.sh.in26
-rw-r--r--qpid/cpp/src/tests/logging.cpp385
-rwxr-xr-xqpid/cpp/src/tests/long_cluster_tests.py38
-rwxr-xr-xqpid/cpp/src/tests/multiq_perftest22
-rwxr-xr-xqpid/cpp/src/tests/perfdist87
-rw-r--r--qpid/cpp/src/tests/policy.acl1
-rw-r--r--qpid/cpp/src/tests/publish.cpp135
-rwxr-xr-xqpid/cpp/src/tests/python_tests29
-rw-r--r--qpid/cpp/src/tests/python_tests.ps145
-rwxr-xr-xqpid/cpp/src/tests/qpid-build-rinstall28
-rw-r--r--qpid/cpp/src/tests/qpid-client-test.cpp139
-rwxr-xr-xqpid/cpp/src/tests/qpid-cluster-benchmark58
-rwxr-xr-xqpid/cpp/src/tests/qpid-cluster-lag.py93
-rwxr-xr-xqpid/cpp/src/tests/qpid-cpp-benchmark260
-rwxr-xr-xqpid/cpp/src/tests/qpid-ctrl120
-rw-r--r--qpid/cpp/src/tests/qpid-latency-test.cpp469
-rw-r--r--qpid/cpp/src/tests/qpid-perftest.cpp746
-rw-r--r--qpid/cpp/src/tests/qpid-ping.cpp76
-rw-r--r--qpid/cpp/src/tests/qpid-receive.cpp269
-rw-r--r--qpid/cpp/src/tests/qpid-send.cpp375
-rwxr-xr-xqpid/cpp/src/tests/qpid-src-rinstall31
-rw-r--r--qpid/cpp/src/tests/qpid-stream.cpp193
-rwxr-xr-xqpid/cpp/src/tests/qpid-test-cluster109
-rw-r--r--qpid/cpp/src/tests/qpid-topic-listener.cpp209
-rw-r--r--qpid/cpp/src/tests/qpid-topic-publisher.cpp230
-rw-r--r--qpid/cpp/src/tests/qpid-txtest.cpp340
-rw-r--r--qpid/cpp/src/tests/queue_flow_limit_tests.py371
-rwxr-xr-xqpid/cpp/src/tests/quick_perftest22
-rwxr-xr-xqpid/cpp/src/tests/quick_topictest30
-rw-r--r--qpid/cpp/src/tests/quick_topictest.ps130
-rwxr-xr-xqpid/cpp/src/tests/quick_txtest22
-rw-r--r--qpid/cpp/src/tests/receiver.cpp140
-rwxr-xr-xqpid/cpp/src/tests/reliable_replication_test93
-rw-r--r--qpid/cpp/src/tests/replaying_sender.cpp165
-rwxr-xr-xqpid/cpp/src/tests/replication_test182
-rwxr-xr-xqpid/cpp/src/tests/restart_cluster38
-rw-r--r--qpid/cpp/src/tests/resuming_receiver.cpp193
-rwxr-xr-xqpid/cpp/src/tests/ring_queue_test174
-rwxr-xr-xqpid/cpp/src/tests/rsynchosts49
-rwxr-xr-xqpid/cpp/src/tests/run-unit-tests48
-rwxr-xr-xqpid/cpp/src/tests/run_acl_tests62
-rw-r--r--qpid/cpp/src/tests/run_acl_tests.ps1102
-rwxr-xr-xqpid/cpp/src/tests/run_cli_tests81
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_authentication_soak26
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_authentication_test26
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_test26
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_tests37
-rwxr-xr-xqpid/cpp/src/tests/run_failover_soak38
-rwxr-xr-xqpid/cpp/src/tests/run_federation_tests64
-rw-r--r--qpid/cpp/src/tests/run_federation_tests.ps184
-rwxr-xr-xqpid/cpp/src/tests/run_header_test37
-rw-r--r--qpid/cpp/src/tests/run_header_test.ps147
-rw-r--r--qpid/cpp/src/tests/run_headers_federation_tests49
-rwxr-xr-xqpid/cpp/src/tests/run_long_cluster_tests24
-rwxr-xr-xqpid/cpp/src/tests/run_perftest28
-rwxr-xr-xqpid/cpp/src/tests/run_queue_flow_limit_tests57
-rwxr-xr-xqpid/cpp/src/tests/run_ring_queue_test36
-rw-r--r--qpid/cpp/src/tests/run_store_tests.ps1133
-rwxr-xr-xqpid/cpp/src/tests/run_test82
-rw-r--r--qpid/cpp/src/tests/run_test.ps169
-rw-r--r--qpid/cpp/src/tests/sasl.mk49
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed166
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex361
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_dynamic27
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster28
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_link27
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_link_cluster28
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_queue27
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_queue_cluster28
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_route27
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_route_cluster28
-rwxr-xr-xqpid/cpp/src/tests/sasl_test_setup.sh41
-rw-r--r--qpid/cpp/src/tests/sasl_version.cpp48
-rw-r--r--qpid/cpp/src/tests/sender.cpp157
-rwxr-xr-xqpid/cpp/src/tests/shared_perftest22
-rw-r--r--qpid/cpp/src/tests/shlibtest.cpp34
-rw-r--r--qpid/cpp/src/tests/ssl.mk22
-rwxr-xr-xqpid/cpp/src/tests/ssl_test142
-rwxr-xr-xqpid/cpp/src/tests/start_broker24
-rw-r--r--qpid/cpp/src/tests/start_broker.ps160
-rwxr-xr-xqpid/cpp/src/tests/start_cluster42
-rwxr-xr-xqpid/cpp/src/tests/start_cluster_hosts70
-rwxr-xr-xqpid/cpp/src/tests/stop_broker41
-rw-r--r--qpid/cpp/src/tests/stop_broker.ps156
-rwxr-xr-xqpid/cpp/src/tests/stop_cluster33
-rwxr-xr-xqpid/cpp/src/tests/store.py197
-rw-r--r--qpid/cpp/src/tests/test.xquery6
-rw-r--r--qpid/cpp/src/tests/test_env.sh.in79
-rw-r--r--qpid/cpp/src/tests/test_store.cpp178
-rw-r--r--qpid/cpp/src/tests/test_tools.h106
-rwxr-xr-xqpid/cpp/src/tests/test_watchdog36
-rwxr-xr-xqpid/cpp/src/tests/test_wrap48
-rw-r--r--qpid/cpp/src/tests/testagent.cpp203
-rw-r--r--qpid/cpp/src/tests/testagent.mk51
-rw-r--r--qpid/cpp/src/tests/testagent.xml64
-rw-r--r--qpid/cpp/src/tests/testlib.py766
-rwxr-xr-xqpid/cpp/src/tests/topic_perftest22
-rwxr-xr-xqpid/cpp/src/tests/topictest61
-rw-r--r--qpid/cpp/src/tests/topictest.ps173
-rw-r--r--qpid/cpp/src/tests/txjob.cpp102
-rw-r--r--qpid/cpp/src/tests/txshift.cpp193
-rw-r--r--qpid/cpp/src/tests/unit_test.cpp23
-rw-r--r--qpid/cpp/src/tests/unit_test.h70
-rwxr-xr-xqpid/cpp/src/tests/verify_cluster_objects107
-rw-r--r--qpid/cpp/src/tests/vg_check43
-rw-r--r--qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp76
-rw-r--r--qpid/cpp/src/windows/QpiddBroker.cpp310
-rw-r--r--qpid/cpp/src/windows/resources/qpid-icon.icobin0 -> 52972 bytes
-rw-r--r--qpid/cpp/src/windows/resources/template-resource.rc122
-rw-r--r--qpid/cpp/src/windows/resources/version-resource.h35
-rw-r--r--qpid/cpp/src/xml.mk29
1010 files changed, 151792 insertions, 0 deletions
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
new file mode 100644
index 0000000000..0fe2d7e4d0
--- /dev/null
+++ b/qpid/cpp/src/CMakeLists.txt
@@ -0,0 +1,1305 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Option to require building optional plugins
+foreach (r ${REQUIRE})
+ set(${r}_force ON)
+ message(STATUS "Forcing ${r} to ${${r}_force}")
+endforeach(r)
+
+include(CheckFunctionExists)
+include(CheckIncludeFileCXX)
+include(CheckIncludeFiles)
+include(CheckIncludeFileCXX)
+include(CheckLibraryExists)
+include(CheckSymbolExists)
+include(FindBoost)
+include(FindDoxygen)
+
+#set (CMAKE_VERBOSE_MAKEFILE ON) # for debugging
+
+#
+# Set up installation of .pdb files if the compiler is Visual Studio
+#
+# Sample: install_pdb (qpidcommon ${QPID_COMPONENT_COMMON})
+#
+MACRO (install_pdb theLibrary theComponent)
+ if (MSVC)
+ get_target_property(library_dll ${theLibrary} LOCATION)
+ string(REPLACE .dll .pdb library_pdb ${library_dll})
+ string(REPLACE $(OutDir) \${CMAKE_INSTALL_CONFIG_NAME} library_pdb ${library_pdb})
+ string(REPLACE .pdb d.pdb libraryd_pdb ${library_pdb})
+ #message(STATUS "_pdb: ${library_pdb}, ${libraryd_pdb}")
+ install (PROGRAMS
+ ${library_pdb}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/ReleasePDB
+ COMPONENT ${theComponent}
+ OPTIONAL
+ CONFIGURATIONS Release|MinSizeRel)
+ install (PROGRAMS
+ ${library_pdb}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/ReleasePDB
+ COMPONENT ${theComponent}
+ CONFIGURATIONS RelWithDebInfo)
+ install (PROGRAMS
+ ${libraryd_pdb}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/DebugPDB
+ COMPONENT ${theComponent}
+ CONFIGURATIONS Debug)
+ endif (MSVC)
+ENDMACRO (install_pdb)
+
+#
+# inherit_value - if the symbol is undefined then set it to the given value.
+# Set flag to indicate this symbol was defined here.
+#
+MACRO (inherit_value theSymbol theValue)
+ if (NOT DEFINED ${theSymbol})
+ set (${theSymbol} ${theValue})
+ # message ("Set symbol '${theSymbol}' to value '${theValue}'")
+ set (${theSymbol}_inherited = "true")
+ endif (NOT DEFINED ${theSymbol})
+ENDMACRO (inherit_value)
+
+#
+# If compiler is Visual Studio then create a "version resource" for the project.
+# Use this call to override CPACK and file global settings but not file per-project settings.
+# Two groups of four version numbers specify "file" and "product" versions separately.
+#
+# Sample: add_msvc_version_full (qmfengine library dll 1 0 0 1 1 0 0 1)
+#
+MACRO (add_msvc_version_full verProject verProjectType verProjectFileExt verFN1 verFN2 verFN3 verFN4 verPN1 verPN2 verPN3 verPN4)
+ if (MSVC)
+ # Create project-specific version strings
+ inherit_value ("winver_${verProject}_FileVersionBinary" "${verFN1},${verFN2},${verFN3},${verFN4}")
+ inherit_value ("winver_${verProject}_ProductVersionBinary" "${verPN1},${verPN2},${verPN3},${verPN4}")
+ inherit_value ("winver_${verProject}_FileVersionString" "${verFN1}, ${verFN2}, ${verFN3}, ${verFN4}")
+ inherit_value ("winver_${verProject}_ProductVersionString" "${verPN1}, ${verPN2}, ${verPN3}, ${verPN4}")
+ inherit_value ("winver_${verProject}_FileDescription" "${winver_PACKAGE_NAME}-${verProject} ${verProjectType}")
+ inherit_value ("winver_${verProject}_LegalCopyright" "${winver_LEGAL_COPYRIGHT}")
+ inherit_value ("winver_${verProject}_InternalName" "${verProject}")
+ inherit_value ("winver_${verProject}_OriginalFilename" "${verProject}.${verProjectFileExt}")
+ inherit_value ("winver_${verProject}_ProductName" "${winver_DESCRIPTION_SUMMARY}")
+
+ # Create strings to be substituted into the template file
+ set ("winverFileVersionBinary" "${winver_${verProject}_FileVersionBinary}")
+ set ("winverProductVersionBinary" "${winver_${verProject}_ProductVersionBinary}")
+ set ("winverFileVersionString" "${winver_${verProject}_FileVersionString}")
+ set ("winverProductVersionString" "${winver_${verProject}_ProductVersionString}")
+ set ("winverFileDescription" "${winver_${verProject}_FileDescription}")
+ set ("winverLegalCopyright" "${winver_${verProject}_LegalCopyright}")
+ set ("winverInternalName" "${winver_${verProject}_InternalName}")
+ set ("winverOriginalFilename" "${winver_${verProject}_OriginalFilename}")
+ set ("winverProductName" "${winver_${verProject}_ProductName}")
+
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/resources/template-resource.rc
+ ${CMAKE_CURRENT_BINARY_DIR}/windows/resources/${verProject}-resource.rc)
+ set (${verProject}_SOURCES
+ ${${verProject}_SOURCES}
+ ${CMAKE_CURRENT_BINARY_DIR}/windows/resources/${verProject}-resource.rc
+ )
+ endif (MSVC)
+ENDMACRO (add_msvc_version_full)
+
+#
+# If compiler is Visual Studio then create a "version resource" for the project.
+# Use this call to accept file override version settings or
+# inherited CPACK_PACKAGE_VERSION version settings.
+#
+# Sample: add_msvc_version (qpidcommon library dll)
+#
+MACRO (add_msvc_version verProject verProjectType verProjectFileExt)
+ if (MSVC)
+ add_msvc_version_full (${verProject}
+ ${verProjectType}
+ ${verProjectFileExt}
+ ${winver_FILE_VERSION_N1}
+ ${winver_FILE_VERSION_N2}
+ ${winver_FILE_VERSION_N3}
+ ${winver_FILE_VERSION_N4}
+ ${winver_PRODUCT_VERSION_N1}
+ ${winver_PRODUCT_VERSION_N2}
+ ${winver_PRODUCT_VERSION_N3}
+ ${winver_PRODUCT_VERSION_N4})
+ endif (MSVC)
+ENDMACRO (add_msvc_version)
+
+
+#
+# Install optional windows version settings. Override variables are specified in a file.
+#
+include (./CMakeWinVersions.cmake OPTIONAL)
+
+#
+# Inherit global windows version settings from CPACK settings.
+#
+inherit_value ("winver_PACKAGE_NAME" "${CPACK_PACKAGE_NAME}")
+inherit_value ("winver_DESCRIPTION_SUMMARY" "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
+inherit_value ("winver_FILE_VERSION_N1" "${CPACK_PACKAGE_VERSION_MAJOR}")
+inherit_value ("winver_FILE_VERSION_N2" "${CPACK_PACKAGE_VERSION_MINOR}")
+inherit_value ("winver_FILE_VERSION_N3" "${CPACK_PACKAGE_VERSION_PATCH}")
+inherit_value ("winver_FILE_VERSION_N4" "1")
+inherit_value ("winver_PRODUCT_VERSION_N1" "${winver_FILE_VERSION_N1}")
+inherit_value ("winver_PRODUCT_VERSION_N2" "${winver_FILE_VERSION_N2}")
+inherit_value ("winver_PRODUCT_VERSION_N3" "${winver_FILE_VERSION_N3}")
+inherit_value ("winver_PRODUCT_VERSION_N4" "${winver_FILE_VERSION_N4}")
+inherit_value ("winver_LEGAL_COPYRIGHT" "")
+
+
+# check if we generate source as part of the build
+# - rubygen generates the amqp spec and clustering
+# - managementgen generates the broker management code
+#
+# rubygen subdir is excluded from stable distributions
+# If the main AMQP spec is present, then check if ruby and python are
+# present, and if any sources have changed, forcing a re-gen of source code.
+
+set(AMQP_SPEC_DIR ${qpid-cpp_SOURCE_DIR}/../specs)
+set(AMQP_SPEC ${AMQP_SPEC_DIR}/amqp.0-10-qpid-errata.xml)
+if (EXISTS ${AMQP_SPEC})
+ include(FindRuby)
+ include(FindPythonInterp)
+ if (NOT RUBY_EXECUTABLE)
+ message(FATAL_ERROR "Can't locate ruby, needed to generate source files.")
+ endif (NOT RUBY_EXECUTABLE)
+ if (NOT PYTHON_EXECUTABLE)
+ message(FATAL_ERROR "Can't locate python, needed to generate source files.")
+ endif (NOT PYTHON_EXECUTABLE)
+
+ set(specs ${AMQP_SPEC} ${qpid-cpp_SOURCE_DIR}/xml/cluster.xml)
+ set(regen_amqp OFF)
+ set(rgen_dir ${qpid-cpp_SOURCE_DIR}/rubygen)
+ file(GLOB_RECURSE rgen_progs ${rgen_dir}/*.rb)
+ # If any of the specs, or any of the sources used to generate code, change
+ # then regenerate the sources.
+ foreach (spec_file ${specs} ${rgen_progs})
+ if (${spec_file} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/rubygen.cmake)
+ set(regen_amqp ON)
+ endif (${spec_file} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/rubygen.cmake)
+ endforeach (spec_file ${specs})
+ if (regen_amqp)
+ message(STATUS "Regenerating AMQP protocol sources")
+execute_process(COMMAND ${RUBY_EXECUTABLE} -I ${rgen_dir} ${rgen_dir}/generate ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../include ${specs} all rubygen.cmake
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ else (regen_amqp)
+ message(STATUS "No need to generate AMQP protocol sources")
+ endif (regen_amqp)
+
+ set(mgmt_specs ${AMQP_SPEC_DIR}/management-schema.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/qpid/acl/management-schema.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/qpid/cluster/management-schema.xml)
+ set(mgen_dir ${qpid-cpp_SOURCE_DIR}/managementgen)
+ set(regen_mgmt OFF)
+ foreach (spec_file ${mgmt_specs})
+ if (${spec_file} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/managementgen.cmake)
+ message(STATUS "${spec_file} is newer")
+ set(regen_mgmt ON)
+ endif (${spec_file} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/managementgen.cmake)
+ endforeach (spec_file ${mgmt_specs})
+ if (regen_mgmt)
+ message(STATUS "Regenerating Qpid Management Framework sources")
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${mgen_dir}/qmf-gen -c managementgen.cmake -b -q -o ${CMAKE_CURRENT_BINARY_DIR}/qmf ${mgmt_specs}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ else (regen_mgmt)
+ message(STATUS "No need to generate Qpid Management Framework sources")
+ endif (regen_mgmt)
+
+ # Pull in the names of the generated files, i.e. ${rgen_framing_srcs}
+ include (${CMAKE_CURRENT_BINARY_DIR}/rubygen.cmake)
+ include (${CMAKE_CURRENT_BINARY_DIR}/managementgen.cmake)
+
+else (EXISTS ${AMQP_SPEC})
+ message(STATUS "No AMQP spec... presume generated sources are included")
+ set(QPID_GENERATED_HEADERS_IN_SOURCE ON)
+ include (rubygen.cmake)
+ include (managementgen.cmake)
+endif (EXISTS ${AMQP_SPEC})
+
+find_program(HELP2MAN help2man DOC "Location of the help2man program")
+option(GEN_MANPAGES "Use help2man to generate man pages" ON)
+if (GEN_MANPAGES AND NOT HELP2MAN)
+ message(STATUS "Can't locate the help2man command; man pages will not be generated")
+ set (GEN_MANPAGES OFF)
+endif (GEN_MANPAGES AND NOT HELP2MAN)
+
+# FindDoxygen module tries to locate doxygen and Graphviz dot
+set (docs_default ON)
+if (NOT DOXYGEN_EXECUTABLE)
+ set (docs_default OFF)
+endif (NOT DOXYGEN_EXECUTABLE)
+option(GEN_DOXYGEN "Use doxygen to generate user documentation" ${docs_default})
+if (GEN_DOXYGEN AND NOT DOXYGEN_EXECUTABLE)
+ message(STATUS "Can't locate the doxygen command; user documentation will not be generated")
+ set (GEN_DOXYGEN OFF)
+endif (GEN_DOXYGEN AND NOT DOXYGEN_EXECUTABLE)
+
+find_program(VALGRIND valgrind DOC "Location of the valgrind program")
+option(ENABLE_VALGRIND "Use valgrind to detect run-time problems" ON)
+if (ENABLE_VALGRIND AND NOT VALGRIND)
+ message(STATUS "Can't locate the valgrind command; no run-time error detection")
+endif (ENABLE_VALGRIND AND NOT VALGRIND)
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set (COMPILER_FLAGS "")
+ # Warnings: Enable as many as possible, keep the code clean. Please
+ # do not disable warnings or remove -Werror without discussing on
+ # qpid-dev list.
+ #
+ # The following warnings are deliberately omitted, they warn on valid code.
+ # -Wunreachable-code -Wpadded -Winline
+ # -Wshadow - warns about boost headers.
+ set (WARNING_FLAGS
+ "-Werror -pedantic -Wall -Wextra -Wno-shadow -Wpointer-arith -Wcast-qual -Wcast-align -Wno-long-long -Wvolatile-register-var -Winvalid-pch -Wno-system-headers -Woverloaded-virtual")
+endif (CMAKE_COMPILER_IS_GNUCXX)
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
+ set (COMPILER_FLAGS "-library=stlport4 -mt")
+ set (WARNING_FLAGS "+w2")
+endif (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
+
+option(ENABLE_WARNINGS "Enable lots of compiler warnings (recommended)" ON)
+if (NOT ENABLE_WARNINGS)
+ set (WARNING_FLAGS "")
+endif (NOT ENABLE_WARNINGS)
+
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS} ${WARNING_FLAGS}")
+
+# Expand a bit from the basic Find_Boost; be specific about what's needed.
+# TODO: Not all these libs are needed everywhere:
+# Linux only uses filesystem program_options unit_test_framework
+# (which itself uses regex).
+# Boost.system is sometimes needed; it's handled separately, below.
+find_package(Boost 1.33 REQUIRED
+ COMPONENTS filesystem program_options date_time thread
+ regex unit_test_framework)
+if(NOT Boost_FOUND)
+ message(FATAL_ERROR "Boost C++ libraries not found. Please install or try setting BOOST_ROOT")
+endif(NOT Boost_FOUND)
+
+# Boost.system was introduced at Boost 1.35; it's needed secondarily by other
+# Boost libs Qpid needs, so be sure it's there.
+if (Boost_VERSION GREATER 103499)
+ find_package(Boost COMPONENTS system)
+
+ # Allow for cmake pre 2.6 and boost post 1.35
+ if (NOT Boost_SYSTEM_LIBRARY)
+ set(Boost_SYSTEM_LIBRARY boost_system)
+ endif (NOT Boost_SYSTEM_LIBRARY)
+endif (Boost_VERSION GREATER 103499)
+
+# Versions of cmake pre 2.6 don't set the Boost_*_LIBRARY variables correctly
+# these values are correct for Linux
+if (NOT Boost_PROGRAM_OPTIONS_LIBRARY)
+ set(Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options)
+endif (NOT Boost_PROGRAM_OPTIONS_LIBRARY)
+
+if (NOT Boost_FILESYSTEM_LIBRARY)
+ set(Boost_FILESYSTEM_LIBRARY boost_filesystem)
+endif (NOT Boost_FILESYSTEM_LIBRARY)
+
+if (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
+ set(Boost_UNIT_TEST_FRAMEWORK_LIBRARY boost_unit_test_framework)
+endif (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
+
+if (NOT Boost_REGEX_LIBRARY)
+ set(Boost_REGEX_LIBRARY boost_regex)
+endif (NOT Boost_REGEX_LIBRARY)
+
+# The Windows install also wants the Boost DLLs, libs and headers that the
+# release is built with. The DLLs enable everything to run, and the headers
+# and libs ensure that users building Qpid C++ client programs can compile
+# (the C++ API still exposes Boost headers, but hopefully this will be fixed
+# in the future).
+#
+# On Windows you can pick whether the static or dynamic versions of the libs
+# are used; allow this choice to the user. Since we also install the Boost
+# DLLs that are needed for the Windows package, none are needed for the
+# static link case; else drop them into the install. Do this all first, since
+# Boost on Windows can use automatic linking to pick up the correct
+# Boost libs based on compile-time touching of the headers. Since we don't
+# really need to add them to the link lines, set the names to blanks.
+if (MSVC)
+ install (PROGRAMS
+ ${Boost_DATE_TIME_LIBRARY_DEBUG} ${Boost_DATE_TIME_LIBRARY_RELEASE}
+ ${Boost_FILESYSTEM_LIBRARY_DEBUG} ${Boost_FILESYSTEM_LIBRARY_RELEASE}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG} ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE}
+ ${Boost_REGEX_LIBRARY_DEBUG} ${Boost_REGEX_LIBRARY_RELEASE}
+ ${Boost_THREAD_LIBRARY_DEBUG} ${Boost_THREAD_LIBRARY_RELEASE}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/boost
+ COMPONENT ${QPID_COMPONENT_COMMON})
+
+ if (NOT Boost_VERSION LESS 103500)
+ install (PROGRAMS
+ ${Boost_SYSTEM_LIBRARY_DEBUG} ${Boost_SYSTEM_LIBRARY_RELEASE}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/boost
+ COMPONENT ${QPID_COMPONENT_COMMON})
+ endif (NOT Boost_VERSION LESS 103500)
+
+ option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static)" ON)
+ if (QPID_LINK_BOOST_DYNAMIC)
+ add_definitions( /D BOOST_ALL_DYN_LINK)
+ string (REPLACE .lib .dll
+ _boost_date_time_debug ${Boost_DATE_TIME_LIBRARY_DEBUG})
+ string (REPLACE .lib .dll
+ _boost_date_time_release ${Boost_DATE_TIME_LIBRARY_RELEASE})
+ string (REPLACE .lib .dll
+ _boost_filesystem_debug ${Boost_FILESYSTEM_LIBRARY_DEBUG})
+ string (REPLACE .lib .dll
+ _boost_filesystem_release ${Boost_FILESYSTEM_LIBRARY_RELEASE})
+ string (REPLACE .lib .dll
+ _boost_program_options_debug ${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG})
+ string (REPLACE .lib .dll
+ _boost_program_options_release ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE})
+ string (REPLACE .lib .dll
+ _boost_regex_debug ${Boost_REGEX_LIBRARY_DEBUG})
+ string (REPLACE .lib .dll
+ _boost_regex_release ${Boost_REGEX_LIBRARY_RELEASE})
+ string (REPLACE .lib .dll
+ _boost_thread_debug ${Boost_THREAD_LIBRARY_DEBUG})
+ string (REPLACE .lib .dll
+ _boost_thread_release ${Boost_THREAD_LIBRARY_RELEASE})
+ # Boost 1.35 added the system library, which gets indirectly linked in
+ # via other Boost libs. So, if building with Boost 1.35 or later, also
+ # include system in the Windows install package.
+ if (NOT Boost_VERSION LESS 103500)
+ string (REPLACE boost_thread boost_system
+ _boost_system_debug ${_boost_thread_debug})
+ string (REPLACE boost_thread boost_system
+ _boost_system_release ${_boost_thread_release})
+ endif (NOT Boost_VERSION LESS 103500)
+ install (PROGRAMS
+ ${_boost_date_time_debug} ${_boost_date_time_release}
+ ${_boost_filesystem_debug} ${_boost_filesystem_release}
+ ${_boost_program_options_debug} ${_boost_program_options_release}
+ ${_boost_regex_debug} ${_boost_regex_release}
+ ${_boost_system_debug} ${_boost_system_release}
+ ${_boost_thread_debug} ${_boost_thread_release}
+ DESTINATION ${QPID_INSTALL_LIBDIR}/boost
+ COMPONENT ${QPID_COMPONENT_COMMON})
+ endif (QPID_LINK_BOOST_DYNAMIC)
+
+ # Need the boost headers regardless of which way the libs go. Try to
+ # weed out what we don't need, else it's giant and unnecessary.
+ install (DIRECTORY ${Boost_INCLUDE_DIR}/boost
+ DESTINATION ${QPID_INSTALL_INCLUDEDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT_INCLUDE}
+ PATTERN "accumulators/*" EXCLUDE
+ PATTERN "algorithm/*" EXCLUDE
+ PATTERN "archive/*" EXCLUDE
+ PATTERN "asio*" EXCLUDE
+ PATTERN "bimap*" EXCLUDE
+ PATTERN "circular_buffer*" EXCLUDE
+ PATTERN "concept*" EXCLUDE
+ PATTERN "dynamic_bitset*" EXCLUDE
+ PATTERN "flyweight*" EXCLUDE
+ PATTERN "fusion/*" EXCLUDE
+ PATTERN "gil/*" EXCLUDE
+ PATTERN "graph/*" EXCLUDE
+ PATTERN "interprocess/*" EXCLUDE
+ PATTERN "lambda/*" EXCLUDE
+ PATTERN "logic/*" EXCLUDE
+ PATTERN "math*" EXCLUDE
+ PATTERN "mpi*" EXCLUDE
+ PATTERN "multi_*" EXCLUDE
+ PATTERN "numeric/*" EXCLUDE
+ PATTERN "pending/*" EXCLUDE
+ PATTERN "pool/*" EXCLUDE
+ PATTERN "property_map/*" EXCLUDE
+ PATTERN "proto/*" EXCLUDE
+ PATTERN "random*" EXCLUDE
+ PATTERN "signals*" EXCLUDE
+ PATTERN "spirit*" EXCLUDE
+ PATTERN "statechart/*" EXCLUDE
+ PATTERN "units/*" EXCLUDE
+ PATTERN "unordered*" EXCLUDE
+ PATTERN "wave*" EXCLUDE
+ PATTERN "xpressive/*" EXCLUDE)
+
+ set(Boost_DATE_TIME_LIBRARY "")
+ set(Boost_THREAD_LIBRARY "")
+ set(Boost_PROGRAM_OPTIONS_LIBRARY "")
+ set(Boost_FILESYSTEM_LIBRARY "")
+ set(Boost_UNIT_TEST_FRAMEWORK_LIBRARY "")
+ set(Boost_REGEX_LIBRARY "")
+ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/windows/resources )
+endif (MSVC)
+
+include_directories( ${Boost_INCLUDE_DIR} )
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../include )
+include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
+include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../include )
+
+link_directories( ${Boost_LIBRARY_DIRS} )
+
+CHECK_LIBRARY_EXISTS (rt clock_gettime "" CLOCK_GETTIME_IN_RT)
+if (NOT CLOCK_GETTIME_IN_RT)
+ CHECK_FUNCTION_EXISTS (clock_gettime QPID_HAS_CLOCK_GETTIME)
+else (NOT CLOCK_GETTIME_IN_RT)
+ set(CMAKE_REQUIRED_LIBS ${CMAKE_REQUIRED_LIBS} rt)
+ set(QPID_HAS_CLOCK_GETTIME YES CACHE BOOL "Platform has clock_gettime")
+endif (NOT CLOCK_GETTIME_IN_RT)
+
+# See if Cyrus SASL is desired and available
+CHECK_LIBRARY_EXISTS (sasl2 sasl_checkpass "" HAVE_SASL)
+CHECK_INCLUDE_FILES (sasl/sasl.h HAVE_SASL_H)
+
+set (sasl_default ${sasl_force})
+if (HAVE_SASL AND HAVE_SASL_H)
+ set (sasl_default ON)
+endif (HAVE_SASL AND HAVE_SASL_H)
+
+option(BUILD_SASL "Build with Cyrus SASL support" ${sasl_default})
+if (BUILD_SASL)
+ if (NOT HAVE_SASL)
+ message(FATAL_ERROR "Cyrus SASL support requested but libsasl2 not found")
+ endif (NOT HAVE_SASL)
+ if (NOT HAVE_SASL_H)
+ message(FATAL_ERROR "Cyrus SASL support requested but sasl.h not found")
+ endif (NOT HAVE_SASL_H)
+
+ set(BROKER_SASL_NAME "qpidd" CACHE STRING "SASL app name for the qpid broker")
+ set(qpidcommon_sasl_source
+ qpid/sys/cyrus/CyrusSecurityLayer.h
+ qpid/sys/cyrus/CyrusSecurityLayer.cpp
+ )
+ set(qpidcommon_sasl_lib sasl2)
+endif (BUILD_SASL)
+
+# See if XML Exchange is desired and prerequisites are available
+CHECK_LIBRARY_EXISTS (xerces-c _init "" HAVE_XERCES)
+CHECK_INCLUDE_FILE_CXX (xercesc/framework/MemBufInputSource.hpp HAVE_XERCES_H)
+CHECK_INCLUDE_FILE_CXX (xqilla/xqilla-simple.hpp HAVE_XQILLA_H)
+CHECK_INCLUDE_FILE_CXX (xqilla/ast/XQEffectiveBooleanValue.hpp HAVE_XQ_EBV)
+
+set (xml_default ${xml_force})
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (HAVE_XERCES AND HAVE_XERCES_H)
+ if (HAVE_XQILLA_H)
+ set (xml_default ON)
+ endif (HAVE_XQILLA_H)
+ endif (HAVE_XERCES AND HAVE_XERCES_H)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+option(BUILD_XML "Build with XML Exchange" ${xml_default})
+if (BUILD_XML)
+ if (NOT HAVE_XERCES)
+ message(FATAL_ERROR "XML Exchange support requested but xerces-c library not found")
+ endif (NOT HAVE_XERCES)
+ if (NOT HAVE_XERCES_H)
+ message(FATAL_ERROR "XML Exchange support requested but Xerces-C headers not found")
+ endif (NOT HAVE_XERCES_H)
+ if (NOT HAVE_XQILLA_H)
+ message(FATAL_ERROR "XML Exchange support requested but XQilla headers not found")
+ endif (NOT HAVE_XQILLA_H)
+
+ if (HAVE_XQ_EBV)
+ add_definitions(-DXQ_EFFECTIVE_BOOLEAN_VALUE_HPP)
+ endif (HAVE_XQ_EBV)
+
+ add_library (xml MODULE
+ qpid/xml/XmlExchange.cpp
+ qpid/xml/XmlExchange.h
+ qpid/xml/XmlExchangePlugin.cpp)
+ set_target_properties (xml PROPERTIES PREFIX "")
+ target_link_libraries (xml xerces-c xqilla qpidbroker pthread)
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties (xml PROPERTIES
+ PREFIX ""
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+ install (TARGETS xml
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+ set(xml_tests XmlClientSessionTest)
+
+endif (BUILD_XML)
+
+# Build the ACL plugin
+set (acl_default ON)
+option(BUILD_ACL "Build ACL enforcement broker plugin" ${acl_default})
+if (BUILD_ACL)
+ set (acl_SOURCES
+ qpid/acl/Acl.cpp
+ qpid/acl/Acl.h
+ qpid/acl/AclData.cpp
+ qpid/acl/AclData.h
+ qpid/acl/AclPlugin.cpp
+ qpid/acl/AclReader.cpp
+ qpid/acl/AclReader.h
+ qpid/acl/AclValidator.cpp
+ qpid/acl/AclValidator.h
+ )
+ # Windows builds the ACL code into the qpidbroker library; see QPID-1842
+ # for history and rationale. If this is changed, remove the acl_SOURCES from
+ # the qpidbroker platform-specific source list.
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+ add_library (acl MODULE ${acl_SOURCES})
+ set_target_properties (acl PROPERTIES PREFIX "")
+ target_link_libraries (acl qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties (acl PROPERTIES
+ PREFIX ""
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+ install (TARGETS acl
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+ endif (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+endif (BUILD_ACL)
+
+# Check for optional cluster support requirements
+include (cluster.cmake)
+
+# Check for optional RDMA support requirements
+include (rdma.cmake)
+
+# Check for optional SSL support requirements
+include (ssl.cmake)
+
+# Check for syslog capabilities not present on all systems
+check_symbol_exists (LOG_AUTHPRIV "sys/syslog.h" HAVE_LOG_AUTHPRIV)
+check_symbol_exists (LOG_FTP "sys/syslog.h" HAVE_LOG_FTP)
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (MSVC)
+ add_definitions(
+ /D "_CRT_NONSTDC_NO_WARNINGS"
+ /D "NOMINMAX"
+ /D "WIN32_LEAN_AND_MEAN"
+ /wd4244
+ /wd4800
+ /wd4355
+ )
+ if (MSVC80)
+ add_definitions(/D "_WIN32_WINNT=0x0501")
+ endif (MSVC80)
+
+ # set the RelWithDebInfo compile/link switches to equal Release
+ set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /O2 /Ob2 /D NDEBUG")
+ set (CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "/debug /INCREMENTAL:NO")
+
+ if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/qpid/dotnet/src)
+ # Set the windows version for the .NET Binding cpp project
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../bindings/qpid/dotnet/src/org.apache.qpid.messaging.template.rc
+ ${CMAKE_CURRENT_BINARY_DIR}/windows/resources/org.apache.qpid.messaging.rc)
+ # Set the windows version for the .NET Binding sessionreceiver project
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../bindings/qpid/dotnet/src/sessionreceiver/properties/sessionreceiver-AssemblyInfo-template.cs
+ ${CMAKE_CURRENT_BINARY_DIR}/windows/generated_src/sessionreceiver-AssemblyInfo.cs)
+ endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/qpid/dotnet/src)
+ endif (MSVC)
+
+ set (qpidtypes_platform_SOURCES
+ qpid/sys/windows/uuid.cpp
+ )
+ set (qpidtypes_platform_LIBS
+ rpcrt4
+ )
+
+ set (qpidcommon_platform_SOURCES
+ qpid/log/windows/SinkOptions.cpp
+ qpid/sys/windows/AsynchIO.cpp
+ qpid/sys/windows/FileSysDir.cpp
+ qpid/sys/windows/IocpPoller.cpp
+ qpid/sys/windows/IOHandle.cpp
+ qpid/sys/windows/LockFile.cpp
+ qpid/sys/windows/PipeHandle.cpp
+ qpid/sys/windows/PollableCondition.cpp
+ qpid/sys/windows/Shlib.cpp
+ qpid/sys/windows/Socket.cpp
+ qpid/sys/windows/SocketAddress.cpp
+ qpid/sys/windows/StrError.cpp
+ qpid/sys/windows/SystemInfo.cpp
+ qpid/sys/windows/Thread.cpp
+ qpid/sys/windows/Time.cpp
+ qpid/client/windows/SaslFactory.cpp
+ ${sslcommon_windows_SOURCES}
+ )
+
+ set (qpidcommon_platform_LIBS
+ ${Boost_THREAD_LIBRARY} ${windows_ssl_libs} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ws2_32 )
+ set (qpidbroker_platform_SOURCES
+ qpid/broker/windows/BrokerDefaults.cpp
+ qpid/broker/windows/SaslAuthenticator.cpp
+ ${acl_SOURCES}
+ ${sslbroker_windows_SOURCES}
+ )
+ set (qpidbroker_platform_LIBS
+ ${windows_ssl_libs} ${windows_ssl_server_libs}
+ )
+ set (qpidclient_platform_SOURCES
+ ${sslclient_windows_SOURCES}
+ )
+ set (qpidclient_platform_LIBS
+ ${windows_ssl_libs}
+ )
+
+ set (qpidd_platform_SOURCES
+ windows/QpiddBroker.cpp
+ )
+
+ set (qpidmessaging_platform_SOURCES
+ qpid/messaging/HandleInstantiator.cpp
+ )
+
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+ # POSIX (Non-Windows) platforms have a lot of overlap in sources; the only
+ # major difference is the poller module.
+ if (CMAKE_SYSTEM_NAME STREQUAL Linux)
+ set (qpid_poller_module
+ qpid/sys/epoll/EpollPoller.cpp
+ qpid/sys/posix/SystemInfo.cpp
+ )
+ add_definitions(-pthread)
+ set (CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -pthread)
+ endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
+
+ if (CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ set (qpid_poller_module
+ qpid/sys/solaris/ECFPoller.cpp
+ qpid/sys/solaris/SystemInfo.cpp
+ )
+ endif (CMAKE_SYSTEM_NAME STREQUAL SunOS)
+
+ set (qpidtypes_platform_SOURCES)
+ set (qpidtypes_platform_LIBS
+ uuid
+ )
+
+ set (qpidcommon_platform_SOURCES
+ qpid/sys/posix/AsynchIO.cpp
+ qpid/sys/posix/Fork.cpp
+ qpid/sys/posix/FileSysDir.cpp
+ qpid/sys/posix/IOHandle.cpp
+ qpid/sys/posix/LockFile.cpp
+ qpid/sys/posix/Mutex.cpp
+ qpid/sys/posix/PipeHandle.cpp
+ qpid/sys/posix/PollableCondition.cpp
+ qpid/sys/posix/Shlib.cpp
+ qpid/log/posix/SinkOptions.cpp
+ qpid/sys/posix/Socket.cpp
+ qpid/sys/posix/SocketAddress.cpp
+ qpid/sys/posix/StrError.cpp
+ qpid/sys/posix/Thread.cpp
+ qpid/sys/posix/Time.cpp
+ qpid/SaslFactory.cpp
+
+ ${qpid_poller_module}
+ )
+ set (qpidcommon_platform_LIBS
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${CMAKE_DL_LIBS}
+ )
+
+ set (qpidbroker_platform_SOURCES
+ qpid/broker/Daemon.cpp
+ qpid/broker/SaslAuthenticator.cpp
+ qpid/broker/SignalHandler.h
+ qpid/broker/SignalHandler.cpp
+ qpid/broker/posix/BrokerDefaults.cpp
+ )
+
+ set (qpidclient_platform_SOURCES
+ )
+
+ set (qpidd_platform_SOURCES
+ posix/QpiddBroker.cpp
+ )
+
+ set (qpidmessaging_platform_SOURCES
+ )
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+set (qpidcommon_SOURCES
+ ${rgen_framing_srcs}
+ ${qpidcommon_platform_SOURCES}
+ ${qpidcommon_sasl_source}
+ qpid/assert.cpp
+ qpid/Address.cpp
+ qpid/DataDir.cpp
+ qpid/Exception.cpp
+ qpid/Modules.cpp
+ qpid/Options.cpp
+ qpid/Plugin.cpp
+ qpid/RefCountedBuffer.cpp
+ qpid/SessionState.cpp
+ qpid/SessionId.cpp
+ qpid/StringUtils.cpp
+ qpid/Url.cpp
+ qpid/amqp_0_10/SessionHandler.cpp
+ qpid/framing/AccumulatedAck.cpp
+ qpid/framing/AMQBody.cpp
+ qpid/framing/AMQMethodBody.cpp
+ qpid/framing/AMQContentBody.cpp
+ qpid/framing/AMQFrame.cpp
+ qpid/framing/AMQHeaderBody.cpp
+ qpid/framing/AMQHeartbeatBody.cpp
+ qpid/framing/Array.cpp
+ qpid/framing/BodyHandler.cpp
+ qpid/framing/Buffer.cpp
+ qpid/framing/Endian.cpp
+ qpid/framing/FieldTable.cpp
+ qpid/framing/FieldValue.cpp
+ qpid/framing/FrameSet.cpp
+ qpid/framing/FrameDecoder.cpp
+ qpid/framing/List.cpp
+ qpid/framing/ProtocolInitiation.cpp
+ qpid/framing/ProtocolVersion.cpp
+ qpid/framing/SendContent.cpp
+ qpid/framing/SequenceNumber.cpp
+ qpid/framing/SequenceNumberSet.cpp
+ qpid/framing/SequenceSet.cpp
+ qpid/framing/Proxy.cpp
+ qpid/framing/Uuid.cpp
+ qpid/framing/TransferContent.cpp
+ qpid/log/Logger.cpp
+ qpid/log/Options.cpp
+ qpid/log/OstreamOutput.cpp
+ qpid/log/Selector.cpp
+ qpid/log/Statement.cpp
+ qpid/management/Buffer.cpp
+ qpid/management/ConnectionSettings.cpp
+ qpid/management/Mutex.cpp
+ qpid/management/Manageable.cpp
+ qpid/management/ManagementObject.cpp
+ qpid/sys/AggregateOutput.cpp
+ qpid/sys/AsynchIOHandler.cpp
+ qpid/sys/ClusterSafe.cpp
+ qpid/sys/Dispatcher.cpp
+ qpid/sys/DispatchHandle.cpp
+ qpid/sys/Runnable.cpp
+ qpid/sys/Shlib.cpp
+ qpid/sys/Timer.cpp
+ qpid/sys/TimerWarnings.cpp
+ qpid/amqp_0_10/Codecs.cpp
+)
+add_msvc_version (qpidcommon library dll)
+
+add_library (qpidcommon SHARED ${qpidcommon_SOURCES})
+if (CLOCK_GETTIME_IN_RT)
+ set (qpidcommon_platform_LIBS ${qpidcommon_platform_LIBS} rt)
+endif (CLOCK_GETTIME_IN_RT)
+target_link_libraries (qpidcommon qpidtypes
+ ${qpidcommon_platform_LIBS}
+ ${qpidcommon_sasl_lib})
+set_target_properties (qpidcommon PROPERTIES
+ VERSION ${qpidc_version})
+install (TARGETS qpidcommon
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_COMMON})
+install_pdb (qpidcommon ${QPID_COMPONENT_COMMON})
+
+set(qpidtypes_SOURCES
+ qpid/types/Exception.cpp
+ qpid/types/Uuid.cpp
+ qpid/types/Variant.cpp
+ ${qpidtypes_platform_SOURCES}
+)
+add_msvc_version (qpidtypes library dll)
+add_library(qpidtypes SHARED ${qpidtypes_SOURCES})
+target_link_libraries(qpidtypes ${qpidtypes_platform_LIBS})
+set_target_properties (qpidtypes PROPERTIES VERSION ${qpidc_version})
+install(TARGETS qpidtypes
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_COMMON})
+install_pdb (qpidtypes ${QPID_COMPONENT_COMMON})
+
+set (qpidclient_SOURCES
+ ${rgen_client_srcs}
+ ${qpidclient_platform_SOURCES}
+ qpid/client/Bounds.cpp
+ qpid/client/Completion.cpp
+ qpid/client/Connection.cpp
+ qpid/client/ConnectionHandler.cpp
+ qpid/client/ConnectionImpl.cpp
+ qpid/client/ConnectionSettings.cpp
+ qpid/client/Connector.cpp
+ qpid/client/Demux.cpp
+ qpid/client/Dispatcher.cpp
+ qpid/client/FailoverManager.cpp
+ qpid/client/FailoverListener.cpp
+ qpid/client/Future.cpp
+ qpid/client/FutureCompletion.cpp
+ qpid/client/FutureResult.cpp
+ qpid/client/LoadPlugins.cpp
+ qpid/client/LocalQueue.cpp
+ qpid/client/LocalQueueImpl.cpp
+ qpid/client/Message.cpp
+ qpid/client/MessageImpl.cpp
+ qpid/client/MessageListener.cpp
+ qpid/client/MessageReplayTracker.cpp
+ qpid/client/QueueOptions.cpp
+ qpid/client/Results.cpp
+ qpid/client/SessionBase_0_10.cpp
+ qpid/client/SessionBase_0_10Access.h
+ qpid/client/ConnectionAccess.h
+ qpid/client/SessionImpl.cpp
+ qpid/client/StateManager.cpp
+ qpid/client/Subscription.cpp
+ qpid/client/SubscriptionImpl.cpp
+ qpid/client/SubscriptionManager.cpp
+ qpid/client/SubscriptionManagerImpl.cpp
+ qpid/client/TCPConnector.cpp
+)
+add_msvc_version (qpidclient library dll)
+
+add_library (qpidclient SHARED ${qpidclient_SOURCES})
+target_link_libraries (qpidclient qpidcommon ${qpidclient_platform_LIBS})
+set_target_properties (qpidclient PROPERTIES VERSION ${qpidc_version})
+install (TARGETS qpidclient
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+install (DIRECTORY ../include/qpid
+ DESTINATION ${QPID_INSTALL_INCLUDEDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT_INCLUDE}
+ PATTERN ".svn" EXCLUDE)
+install_pdb (qpidclient ${QPID_COMPONENT_CLIENT})
+
+
+set (qpidmessaging_SOURCES
+ ${qpidmessaging_platform_SOURCES}
+ qpid/messaging/Address.cpp
+ qpid/messaging/AddressParser.h
+ qpid/messaging/AddressParser.cpp
+ qpid/messaging/Connection.cpp
+ qpid/messaging/ConnectionImpl.h
+ qpid/messaging/Duration.cpp
+ qpid/messaging/exceptions.cpp
+ qpid/messaging/Message.cpp
+ qpid/messaging/MessageImpl.h
+ qpid/messaging/MessageImpl.cpp
+ qpid/messaging/Receiver.cpp
+ qpid/messaging/ReceiverImpl.h
+ qpid/messaging/Session.cpp
+ qpid/messaging/SessionImpl.h
+ qpid/messaging/Sender.cpp
+ qpid/messaging/SenderImpl.h
+ qpid/messaging/FailoverUpdates.cpp
+ qpid/client/amqp0_10/AcceptTracker.h
+ qpid/client/amqp0_10/AcceptTracker.cpp
+ qpid/client/amqp0_10/AddressResolution.h
+ qpid/client/amqp0_10/AddressResolution.cpp
+ qpid/client/amqp0_10/ConnectionImpl.h
+ qpid/client/amqp0_10/ConnectionImpl.cpp
+ qpid/client/amqp0_10/IncomingMessages.h
+ qpid/client/amqp0_10/IncomingMessages.cpp
+ qpid/client/amqp0_10/MessageSink.h
+ qpid/client/amqp0_10/MessageSource.h
+ qpid/client/amqp0_10/OutgoingMessage.h
+ qpid/client/amqp0_10/OutgoingMessage.cpp
+ qpid/client/amqp0_10/ReceiverImpl.h
+ qpid/client/amqp0_10/ReceiverImpl.cpp
+ qpid/client/amqp0_10/SessionImpl.h
+ qpid/client/amqp0_10/SessionImpl.cpp
+ qpid/client/amqp0_10/SenderImpl.h
+ qpid/client/amqp0_10/SenderImpl.cpp
+ qpid/client/amqp0_10/SimpleUrlParser.h
+ qpid/client/amqp0_10/SimpleUrlParser.cpp
+)
+add_msvc_version (qpidmessaging library dll)
+
+add_library (qpidmessaging SHARED ${qpidmessaging_SOURCES})
+target_link_libraries (qpidmessaging qpidclient)
+set_target_properties (qpidmessaging PROPERTIES VERSION ${qpidc_version})
+install (TARGETS qpidmessaging
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+install_pdb (qpidmessaging ${QPID_COMPONENT_CLIENT})
+
+# Released source artifacts from Apache have the generated headers included in
+# the source tree, not the binary tree. So don't attempt to grab them when
+# they're not supposed to be there.
+if (NOT QPID_GENERATED_HEADERS_IN_SOURCE)
+ install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../include/qpid
+ DESTINATION ${QPID_INSTALL_INCLUDEDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT_INCLUDE})
+endif (NOT QPID_GENERATED_HEADERS_IN_SOURCE)
+
+
+if (_MSC_VER)
+ # Install the DtcPlugin project and call it qpidxarm.
+ set(AMQP_WCF_DIR ${qpid-cpp_SOURCE_DIR}/../wcf)
+ set(qpidxarm_SOURCES ${AMQP_WCF_DIR}/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp)
+ if (EXISTS ${qpidxarm_SOURCES})
+ add_msvc_version (qpidxarm library dll)
+ add_library (qpidxarm SHARED ${qpidxarm_SOURCES})
+ target_link_libraries (qpidxarm qpidclient qpidcommon)
+ install (TARGETS qpidxarm
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+ install_pdb (qpidxarm ${QPID_COMPONENT_CLIENT})
+ endif (EXISTS ${qpidxarm_SOURCES})
+endif (_MSC_VER)
+
+set (qpidbroker_SOURCES
+ ${mgen_broker_cpp}
+ ${qpidbroker_platform_SOURCES}
+ qpid/amqp_0_10/Connection.h
+ qpid/amqp_0_10/Connection.cpp
+ qpid/broker/Broker.cpp
+ qpid/broker/Exchange.cpp
+ qpid/broker/ExpiryPolicy.cpp
+ qpid/broker/Fairshare.cpp
+ qpid/broker/LegacyLVQ.cpp
+ qpid/broker/MessageDeque.cpp
+ qpid/broker/MessageMap.cpp
+ qpid/broker/PriorityQueue.cpp
+ qpid/broker/Queue.cpp
+ qpid/broker/QueueCleaner.cpp
+ qpid/broker/QueueListeners.cpp
+ qpid/broker/PersistableMessage.cpp
+ qpid/broker/Bridge.cpp
+ qpid/broker/Connection.cpp
+ qpid/broker/ConnectionHandler.cpp
+ qpid/broker/ConnectionFactory.cpp
+ qpid/broker/DeliverableMessage.cpp
+ qpid/broker/DeliveryRecord.cpp
+ qpid/broker/DirectExchange.cpp
+ qpid/broker/DtxAck.cpp
+ qpid/broker/DtxBuffer.cpp
+ qpid/broker/DtxManager.cpp
+ qpid/broker/DtxTimeout.cpp
+ qpid/broker/DtxWorkRecord.cpp
+ qpid/broker/ExchangeRegistry.cpp
+ qpid/broker/FanOutExchange.cpp
+ qpid/broker/HeadersExchange.cpp
+ qpid/broker/Link.cpp
+ qpid/broker/LinkRegistry.cpp
+ qpid/broker/Message.cpp
+ qpid/broker/MessageAdapter.cpp
+ qpid/broker/MessageBuilder.cpp
+ qpid/broker/MessageStoreModule.cpp
+ qpid/broker/NameGenerator.cpp
+ qpid/broker/NullMessageStore.cpp
+ qpid/broker/QueueBindings.cpp
+ qpid/broker/QueueEvents.cpp
+ qpid/broker/QueuePolicy.cpp
+ qpid/broker/QueueRegistry.cpp
+ qpid/broker/QueueFlowLimit.cpp
+ qpid/broker/RateTracker.cpp
+ qpid/broker/RecoveryManagerImpl.cpp
+ qpid/broker/RecoveredEnqueue.cpp
+ qpid/broker/RecoveredDequeue.cpp
+ qpid/broker/RetryList.cpp
+ qpid/broker/SecureConnection.cpp
+ qpid/broker/SecureConnectionFactory.cpp
+ qpid/broker/SemanticState.h
+ qpid/broker/SemanticState.cpp
+ qpid/broker/SessionAdapter.cpp
+ qpid/broker/SessionState.h
+ qpid/broker/SessionState.cpp
+ qpid/broker/SessionManager.h
+ qpid/broker/SessionManager.cpp
+ qpid/broker/SessionContext.h
+ qpid/broker/SessionHandler.h
+ qpid/broker/SessionHandler.cpp
+ qpid/broker/System.cpp
+ qpid/broker/ThresholdAlerts.cpp
+ qpid/broker/TopicExchange.cpp
+ qpid/broker/TxAccept.cpp
+ qpid/broker/TxBuffer.cpp
+ qpid/broker/TxPublish.cpp
+ qpid/broker/Vhost.cpp
+ qpid/management/ManagementAgent.cpp
+ qpid/management/ManagementDirectExchange.cpp
+ qpid/management/ManagementTopicExchange.cpp
+ qpid/sys/TCPIOPlugin.cpp
+)
+add_msvc_version (qpidbroker library dll)
+add_library (qpidbroker SHARED ${qpidbroker_SOURCES})
+target_link_libraries (qpidbroker qpidcommon ${qpidbroker_platform_LIBS})
+set_target_properties (qpidbroker PROPERTIES VERSION ${qpidc_version})
+if (MSVC)
+ set_target_properties (qpidbroker PROPERTIES COMPILE_FLAGS /wd4290)
+endif (MSVC)
+install (TARGETS qpidbroker
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+install_pdb (qpidbroker ${QPID_COMPONENT_BROKER})
+
+
+set (qpidd_SOURCES
+ ${qpidd_platform_SOURCES}
+ qpidd.cpp
+ qpidd.h
+)
+add_msvc_version (qpidd application exe)
+add_executable (qpidd ${qpidd_SOURCES})
+target_link_libraries (qpidd qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY})
+install (TARGETS qpidd RUNTIME
+ DESTINATION ${QPID_INSTALL_BINDIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+if (CPACK_GENERATOR STREQUAL "NSIS")
+ set (CPACK_NSIS_MENU_LINKS
+ "qpidd" "Start Qpid Broker")
+endif (CPACK_GENERATOR STREQUAL "NSIS")
+
+# QMF library
+# Library Version Information (CURRENT.REVISION.AGE):
+#
+# CURRENT => API/ABI version. Bump this if the interface changes
+# REVISION => Version of underlying implementation.
+# Bump if implementation changes but API/ABI doesn't
+# AGE => Number of API/ABI versions this is backward compatible with
+set (qmf_version 2.0.0)
+set (qmf2_version 1.0.0)
+set (qmfengine_version 1.0.0)
+
+set (qmf_SOURCES
+ qpid/agent/ManagementAgentImpl.cpp
+ qpid/agent/ManagementAgentImpl.h
+ )
+
+add_msvc_version (qmf library dll)
+add_library (qmf SHARED ${qmf_SOURCES})
+target_link_libraries (qmf qpidclient)
+set_target_properties (qmf PROPERTIES
+ VERSION ${qmf_version})
+install (TARGETS qmf OPTIONAL
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_QMF})
+install_pdb (qmf ${QPID_COMPONENT_QMF})
+
+if(NOT WIN32)
+ set (qmf2_HEADERS
+ ../include/qmf/AgentEvent.h
+ ../include/qmf/Agent.h
+ ../include/qmf/AgentSession.h
+ ../include/qmf/ConsoleEvent.h
+ ../include/qmf/ConsoleSession.h
+ ../include/qmf/DataAddr.h
+ ../include/qmf/Data.h
+ ../include/qmf/exceptions.h
+ ../include/qmf/Handle.h
+ ../include/qmf/ImportExport.h
+ ../include/qmf/Query.h
+ ../include/qmf/Schema.h
+ ../include/qmf/SchemaId.h
+ ../include/qmf/SchemaMethod.h
+ ../include/qmf/SchemaProperty.h
+ ../include/qmf/SchemaTypes.h
+ ../include/qmf/Subscription.h
+ )
+
+ set (qmf2_SOURCES
+ ${qmf2_HEADERS}
+ qmf/agentCapability.h
+ qmf/Agent.cpp
+ qmf/AgentEvent.cpp
+ qmf/AgentEventImpl.h
+ qmf/AgentImpl.h
+ qmf/AgentSession.cpp
+ qmf/AgentSubscription.cpp
+ qmf/AgentSubscription.h
+ qmf/ConsoleEvent.cpp
+ qmf/ConsoleEventImpl.h
+ qmf/ConsoleSession.cpp
+ qmf/ConsoleSessionImpl.h
+ qmf/constants.cpp
+ qmf/constants.h
+ qmf/DataAddr.cpp
+ qmf/DataAddrImpl.h
+ qmf/Data.cpp
+ qmf/DataImpl.h
+ qmf/exceptions.cpp
+ qmf/Expression.cpp
+ qmf/Expression.h
+ qmf/Hash.cpp
+ qmf/Hash.h
+ qmf/PrivateImplRef.h
+ qmf/Query.cpp
+ qmf/QueryImpl.h
+ qmf/Schema.cpp
+ qmf/SchemaCache.cpp
+ qmf/SchemaCache.h
+ qmf/SchemaId.cpp
+ qmf/SchemaIdImpl.h
+ qmf/SchemaImpl.h
+ qmf/SchemaMethod.cpp
+ qmf/SchemaMethodImpl.h
+ qmf/SchemaProperty.cpp
+ qmf/SchemaPropertyImpl.h
+ qmf/Subscription.cpp
+ qmf/SubscriptionImpl.h
+ )
+
+ add_msvc_version (qmf2 library dll)
+ add_library (qmf2 SHARED ${qmf2_SOURCES})
+ target_link_libraries (qmf2 qpidmessaging qpidtypes qpidclient qpidcommon)
+ set_target_properties (qmf2 PROPERTIES
+ VERSION ${qmf2_version})
+ install (TARGETS qmf2 OPTIONAL
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_QMF})
+ install (FILES ${qmf2_HEADERS}
+ DESTINATION ${QPID_INSTALL_INCLUDEDIR}/qmf
+ COMPONENT ${QPID_COMPONENT_QMF})
+ install_pdb (qmf2 ${QPID_COMPONENT_QMF})
+endif (NOT WIN32)
+
+set (qmfengine_SOURCES
+ qmf/engine/Agent.cpp
+ qmf/engine/BrokerProxyImpl.cpp
+ qmf/engine/BrokerProxyImpl.h
+ qmf/engine/ConnectionSettingsImpl.cpp
+ qmf/engine/ConnectionSettingsImpl.h
+ qmf/engine/ConsoleImpl.cpp
+ qmf/engine/ConsoleImpl.h
+ qmf/engine/EventImpl.cpp
+ qmf/engine/EventImpl.h
+ qmf/engine/MessageImpl.cpp
+ qmf/engine/MessageImpl.h
+ qmf/engine/ObjectIdImpl.cpp
+ qmf/engine/ObjectIdImpl.h
+ qmf/engine/ObjectImpl.cpp
+ qmf/engine/ObjectImpl.h
+ qmf/engine/Protocol.cpp
+ qmf/engine/Protocol.h
+ qmf/engine/QueryImpl.cpp
+ qmf/engine/QueryImpl.h
+ qmf/engine/SequenceManager.cpp
+ qmf/engine/SequenceManager.h
+ qmf/engine/SchemaImpl.cpp
+ qmf/engine/SchemaImpl.h
+ qmf/engine/ValueImpl.cpp
+ qmf/engine/ValueImpl.h
+ )
+if (NOT WIN32)
+ list(APPEND qmfengine_SOURCES qmf/engine/ResilientConnection.cpp)
+endif (NOT WIN32)
+add_msvc_version (qmfengine library dll)
+
+add_library (qmfengine SHARED ${qmfengine_SOURCES})
+target_link_libraries (qmfengine qpidclient)
+set_target_properties (qmfengine PROPERTIES
+ VERSION ${qmfengine_version})
+install (TARGETS qmfengine OPTIONAL
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_QMF})
+install_pdb (qmfengine ${QPID_COMPONENT_QMF})
+
+# QMF console library
+#module_hdr += \
+# qpid/console/Agent.h \
+# qpid/console/Broker.h \
+# qpid/console/ClassKey.h \
+# qpid/console/ConsoleImportExport.h \
+# qpid/console/ConsoleListener.h \
+# qpid/console/Event.h \
+# qpid/console/Object.h \
+# qpid/console/ObjectId.h \
+# qpid/console/Package.h \
+# qpid/console/Schema.h \
+# qpid/console/SequenceManager.h \
+# qpid/console/SessionManager.h \
+# qpid/console/Value.h
+set (qmfconsole_SOURCES
+ ../include/qpid/console/Agent.h
+ ../include/qpid/console/Broker.h
+ ../include/qpid/console/ClassKey.h
+ ../include/qpid/console/ConsoleImportExport.h
+ ../include/qpid/console/ConsoleListener.h
+ ../include/qpid/console/Event.h
+ ../include/qpid/console/Object.h
+ ../include/qpid/console/ObjectId.h
+ ../include/qpid/console/Package.h
+ ../include/qpid/console/Schema.h
+ ../include/qpid/console/SequenceManager.h
+ ../include/qpid/console/SessionManager.h
+ ../include/qpid/console/Value.h
+ qpid/console/Agent.cpp
+ qpid/console/Broker.cpp
+ qpid/console/ClassKey.cpp
+ qpid/console/Event.cpp
+ qpid/console/Object.cpp
+ qpid/console/ObjectId.cpp
+ qpid/console/Package.cpp
+ qpid/console/Schema.cpp
+ qpid/console/SequenceManager.cpp
+ qpid/console/SessionManager.cpp
+ qpid/console/Value.cpp
+ )
+add_msvc_version (qmfconsole library dll)
+add_library (qmfconsole SHARED ${qmfconsole_SOURCES})
+target_link_libraries (qmfconsole qpidclient)
+set_target_properties (qmfconsole PROPERTIES
+ VERSION ${qpidc_version})
+install (TARGETS qmfconsole
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_QMF})
+install_pdb (qmfconsole ${QPID_COMPONENT_QMF})
+
+# A queue event listener plugin that creates messages on a replication
+# queue corresponding to enqueue and dequeue events:
+set (replicating_listener_SOURCES
+ qpid/replication/constants.h
+ qpid/replication/ReplicatingEventListener.cpp
+ qpid/replication/ReplicatingEventListener.h
+ )
+add_msvc_version (replicating_listener library dll)
+add_library (replicating_listener MODULE ${replicating_listener_SOURCES})
+target_link_libraries (replicating_listener qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
+set_target_properties (replicating_listener PROPERTIES PREFIX "")
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(replicating_listener PROPERTIES
+ LINK_FLAGS -Wl,--no-undefined)
+endif (CMAKE_COMPILER_IS_GNUCXX)
+install (TARGETS replicating_listener
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+# A custom exchange plugin that allows an exchange to be created that
+# can process the messages from a replication queue (populated on the
+# source system by the replicating listener plugin above) and take the
+# corresponding action on the local queues
+set (replication_exchange_SOURCES
+ qpid/replication/constants.h
+ qpid/replication/ReplicationExchange.cpp
+ qpid/replication/ReplicationExchange.h
+ )
+add_msvc_version (replication_exchange library dll)
+add_library (replication_exchange MODULE ${replication_exchange_SOURCES})
+target_link_libraries (replication_exchange qpidbroker)
+set_target_properties (replication_exchange PROPERTIES PREFIX "")
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(replication_exchange PROPERTIES
+ LINK_FLAGS -Wl,--no-undefined)
+endif (CMAKE_COMPILER_IS_GNUCXX)
+install (TARGETS replication_exchange
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+# This is only really needed until all the trunk builds (Linux, UNIX, Windows)
+# are all on cmake only. This is because cmake builds always have a config.h
+# file whereas older builds only have config.h on autoconf-generated builds.
+add_definitions(-DHAVE_CONFIG_H)
+
+add_definitions(-DBOOST_FILESYSTEM_VERSION=2)
+
+# Now create the config file from all the info learned above.
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+add_subdirectory(qpid/store)
+add_subdirectory(tests)
diff --git a/qpid/cpp/src/CMakeWinVersions.cmake b/qpid/cpp/src/CMakeWinVersions.cmake
new file mode 100644
index 0000000000..0bac7cab47
--- /dev/null
+++ b/qpid/cpp/src/CMakeWinVersions.cmake
@@ -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.
+#
+
+#
+# Versions settings overrides for Windows dll/exe file version resource.
+# These values are compiled into the dll and exe files.
+#
+# The settings override precedence from lowest to highest:
+# 1. CPACK settings from cpp/CMakeLists.txt
+# 2. Global settings from this file
+# 3. Command line version number (only) from add_msvc_version_full call
+# 4. Per-project settings from this file
+#
+
+#
+# Specification of global settings for all projects.
+#
+# set ("winver_PACKAGE_NAME" "qpid-cpp")
+# set ("winver_DESCRIPTION_SUMMARY" "Apache Qpid C++")
+# set ("winver_FILE_VERSION_N1" "0")
+# set ("winver_FILE_VERSION_N2" "11")
+# set ("winver_FILE_VERSION_N3" "0")
+# set ("winver_FILE_VERSION_N4" "0")
+# set ("winver_PRODUCT_VERSION_N1" "0")
+# set ("winver_PRODUCT_VERSION_N2" "11")
+# set ("winver_PRODUCT_VERSION_N3" "0")
+# set ("winver_PRODUCT_VERSION_N4" "0")
+# set ("winver_LEGAL_COPYRIGHT" "")
+
+#
+# Specification of per-project settings:
+#
+# set ("winver_${projectName}_FileVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_ProductVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_FileVersionString" "0, 11, 0, 0")
+# set ("winver_${projectName}_ProductVersionString" "0, 11, 0, 0")
+# set ("winver_${projectName}_FileDescription" "qpid-cpp-qpidcommon Library")
+# set ("winver_${projectName}_LegalCopyright" "")
+# set ("winver_${projectName}_InternalName" "qpidcommon")
+# set ("winver_${projectName}_OriginalFilename" "qpidcommon.dll")
+# set ("winver_${projectName}_ProductName" "Apache Qpid C++")
diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am
new file mode 100644
index 0000000000..15021cc68b
--- /dev/null
+++ b/qpid/cpp/src/Makefile.am
@@ -0,0 +1,896 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS = . tests
+
+# The Windows-only sources are not compiled using this Makefile, but
+# are listed here to ensure they're included in releases. They are built
+# using Visual Studio solutions/projects.
+windows_dist = \
+ qpid/client/windows/SaslFactory.cpp \
+ qpid/client/windows/SslConnector.cpp \
+ qpid/log/windows/SinkOptions.cpp \
+ qpid/log/windows/SinkOptions.h \
+ ../include/qpid/sys/windows/check.h \
+ qpid/sys/windows/AsynchIO.cpp \
+ qpid/sys/windows/AsynchIoResult.h \
+ ../include/qpid/sys/windows/Condition.h \
+ qpid/sys/windows/FileSysDir.cpp \
+ ../include/qpid/sys/windows/IntegerTypes.h \
+ qpid/sys/windows/IocpPoller.cpp \
+ qpid/sys/windows/IOHandle.cpp \
+ qpid/sys/windows/IoHandlePrivate.h \
+ qpid/sys/windows/LockFile.cpp \
+ qpid/sys/windows/PollableCondition.cpp \
+ qpid/sys/windows/PipeHandle.cpp \
+ ../include/qpid/sys/windows/Mutex.h \
+ qpid/sys/windows/Shlib.cpp \
+ qpid/sys/windows/SocketAddress.cpp \
+ qpid/sys/windows/Socket.cpp \
+ qpid/sys/windows/SslAsynchIO.cpp \
+ qpid/sys/windows/SslAsynchIO.h \
+ qpid/sys/windows/StrError.cpp \
+ qpid/sys/windows/SystemInfo.cpp \
+ qpid/sys/windows/Thread.cpp \
+ qpid/sys/windows/Time.cpp \
+ ../include/qpid/sys/windows/Time.h \
+ qpid/sys/windows/uuid.cpp \
+ qpid/sys/windows/uuid.h \
+ windows/QpiddBroker.cpp \
+ qpid/broker/windows/BrokerDefaults.cpp \
+ qpid/broker/windows/SaslAuthenticator.cpp \
+ qpid/broker/windows/SslProtocolFactory.cpp \
+ qpid/messaging/HandleInstantiator.cpp \
+ windows/resources/template-resource.rc \
+ windows/resources/version-resource.h \
+ windows/resources/qpid-icon.ico
+
+EXTRA_DIST= $(platform_dist) $(rgen_srcs) $(windows_dist)
+
+# Define variables that are be appended to by this file and included .mk files.
+nobase_include_HEADERS =
+libqpidcommon_la_SOURCES =
+
+## Generated code
+
+# Note: generated soure and makefiles included in distribution so a
+# distribution can be built without code generation tools and XML
+# sources.
+
+# This phony target is needed by generated makefile fragments:
+force:
+
+if GENERATE
+
+# AMQP_FINAL_XML is defined in ../configure.ac
+amqp_0_10_xml=@AMQP_FINAL_XML@
+specs=$(amqp_0_10_xml) $(top_srcdir)/xml/cluster.xml
+
+# Ruby generator.
+rgen_dir=$(top_srcdir)/rubygen
+rgen_cmd=ruby -I $(rgen_dir) $(rgen_dir)/generate . ../include $(specs) all
+
+$(rgen_srcs) $(srcdir)/rubygen.mk: rgen.timestamp
+rgen.timestamp: $(rgen_generator) $(specs)
+ $(rgen_cmd) $(srcdir)/rubygen.mk; touch $@
+$(rgen_generator):
+
+# The CMake version is needed for dist
+$(srcdir)/rubygen.cmake: $(rgen_generator) $(specs)
+ $(rgen_cmd) $(srcdir)/rubygen.cmake
+
+# Management generator.
+mgen_dir=$(top_srcdir)/managementgen
+mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk \
+ -c $(srcdir)/managementgen.cmake -q -b -o qmf \
+ $(top_srcdir)/../specs/management-schema.xml \
+ $(srcdir)/qpid/acl/management-schema.xml \
+ $(srcdir)/qpid/cluster/management-schema.xml
+
+$(srcdir)/managementgen.mk $(mgen_broker_cpp) $(dist_qpid_management_HEADERS): mgen.timestamp
+mgen.timestamp: $(mgen_generator)
+ $(mgen_cmd); touch $@
+$(mgen_generator):
+
+endif # GENERATE
+
+include $(srcdir)/rubygen.mk
+include $(srcdir)/managementgen.mk
+
+## Compiler flags
+AM_CXXFLAGS = $(WARNING_CFLAGS)
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(srcdir) -I=$(builddir)
+
+#
+# Destination for intalled programs and tests defined here
+#
+qpidexecdir = $(libexecdir)/qpid
+AM_CXXFLAGS += -DQPID_LIBEXEC_DIR=\"$(qpidexecdir)\"
+qpidexec_PROGRAMS =
+qpidexec_SCRIPTS =
+qpidtestdir = $(qpidexecdir)/tests
+qpidtest_PROGRAMS =
+qpidtest_SCRIPTS =
+tmoduleexecdir = $(libdir)/qpid/tests
+tmoduleexec_LTLIBRARIES=
+
+AM_CXXFLAGS += -DBOOST_FILESYSTEM_VERSION=2
+
+## Automake macros to build libraries and executables.
+qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\"
+libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduleexecdir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
+
+qpidd_LDADD = \
+ libqpidbroker.la \
+ libqpidcommon.la
+
+posix_qpidd_src = posix/QpiddBroker.cpp
+
+sbin_PROGRAMS = qpidd
+qpidd_SOURCES = qpidd.cpp qpidd.h $(posix_qpidd_src)
+
+## Platform specific code.
+
+# Posix-specific code
+libqpidcommon_la_SOURCES += \
+ qpid/log/posix/SinkOptions.cpp \
+ qpid/sys/posix/IOHandle.cpp \
+ qpid/sys/posix/Socket.cpp \
+ qpid/sys/posix/SocketAddress.cpp \
+ qpid/sys/posix/AsynchIO.cpp \
+ qpid/sys/posix/FileSysDir.cpp \
+ qpid/sys/posix/LockFile.cpp \
+ qpid/sys/posix/Time.cpp \
+ qpid/sys/posix/Thread.cpp \
+ qpid/sys/posix/Shlib.cpp \
+ qpid/sys/posix/Mutex.cpp \
+ qpid/sys/posix/Fork.cpp \
+ qpid/sys/posix/StrError.cpp \
+ qpid/sys/posix/PollableCondition.cpp \
+ qpid/sys/posix/PidFile.h \
+ qpid/sys/posix/PipeHandle.cpp \
+ qpid/log/posix/SinkOptions.h \
+ qpid/sys/posix/Fork.h
+
+nobase_include_HEADERS += \
+ ../include/qpid/sys/posix/Condition.h \
+ ../include/qpid/sys/posix/IntegerTypes.h \
+ ../include/qpid/sys/posix/Mutex.h \
+ ../include/qpid/sys/posix/PrivatePosix.h \
+ ../include/qpid/sys/posix/Time.h \
+ ../include/qpid/sys/posix/check.h
+
+if HAVE_EPOLL
+ poller = qpid/sys/epoll/EpollPoller.cpp
+endif
+
+if HAVE_ECF
+ poller = qpid/sys/solaris/ECFPoller.cpp
+endif
+
+if SUNOS
+ systeminfo = qpid/sys/solaris/SystemInfo.cpp
+else
+ systeminfo = qpid/sys/posix/SystemInfo.cpp
+endif
+
+libqpidcommon_la_SOURCES += $(poller) $(systeminfo)
+
+posix_broker_src = \
+ qpid/broker/posix/BrokerDefaults.cpp
+
+lib_LTLIBRARIES = libqpidtypes.la libqpidcommon.la libqpidbroker.la libqpidclient.la libqpidmessaging.la
+
+# Definitions for client and daemon plugins
+PLUGINLDFLAGS=-no-undefined -module -avoid-version
+confdir=$(sysconfdir)/qpid
+dmoduleexecdir=$(libdir)/qpid/daemon
+cmoduleexecdir=$(libdir)/qpid/client
+dmoduleexec_LTLIBRARIES =
+cmoduleexec_LTLIBRARIES =
+
+include cluster.mk
+include acl.mk
+include qmf.mk
+include qmfc.mk
+if HAVE_XML
+include xml.mk
+endif
+include replication.mk
+
+if RDMA
+
+# RDMA (Infiniband) protocol code
+librdmawrap_la_SOURCES = \
+ qpid/sys/rdma/rdma_exception.h \
+ qpid/sys/rdma/rdma_factories.cpp \
+ qpid/sys/rdma/rdma_factories.h \
+ qpid/sys/rdma/RdmaIO.cpp \
+ qpid/sys/rdma/RdmaIO.h \
+ qpid/sys/rdma/rdma_wrap.cpp \
+ qpid/sys/rdma/rdma_wrap.h
+librdmawrap_la_LIBADD = \
+ libqpidcommon.la \
+ -lrdmacm \
+ -libverbs
+librdmawrap_la_CXXFLAGS = \
+ $(AM_CXXFLAGS) -Wno-missing-field-initializers
+lib_LTLIBRARIES += \
+ librdmawrap.la
+RDMAWRAP_VERSION_INFO = 2:0:0
+librdmawrap_la_LDFLAGS = -version-info $(RDMAWRAP_VERSION_INFO) -no-undefined
+
+rdma_la_SOURCES = \
+ qpid/sys/RdmaIOPlugin.cpp
+rdma_la_LIBADD = \
+ libqpidbroker.la \
+ librdmawrap.la \
+ -libverbs
+rdma_la_LDFLAGS = $(PLUGINLDFLAGS)
+rdma_la_CXXFLAGS = \
+ $(AM_CXXFLAGS) -Wno-missing-field-initializers
+dmoduleexec_LTLIBRARIES += \
+ rdma.la
+
+rdmaconnector_la_SOURCES = \
+ qpid/client/RdmaConnector.cpp
+rdmaconnector_la_LIBADD = \
+ libqpidclient.la \
+ librdmawrap.la \
+ -libverbs
+rdmaconnector_la_LDFLAGS = $(PLUGINLDFLAGS)
+rdmaconnector_la_CXXFLAGS = \
+ $(AM_CXXFLAGS) -Wno-missing-field-initializers
+cmoduleexec_LTLIBRARIES += \
+ rdmaconnector.la
+
+# RDMA test/sample programs
+noinst_PROGRAMS = RdmaServer RdmaClient
+RdmaServer_SOURCES = qpid/sys/rdma/RdmaServer.cpp
+RdmaServer_LDADD = \
+ librdmawrap.la libqpidcommon.la
+RdmaClient_SOURCES = qpid/sys/rdma/RdmaClient.cpp
+RdmaClient_CXXFLAGS = \
+ $(AM_CXXFLAGS) -Wno-missing-field-initializers
+RdmaClient_LDADD = \
+ librdmawrap.la libqpidcommon.la
+
+endif
+
+if SSL
+include ssl.mk
+endif
+
+EXTRA_DIST +=\
+ CMakeLists.txt \
+ cluster.cmake \
+ config.h.cmake \
+ rdma.cmake \
+ ssl.cmake \
+ managementgen.cmake \
+ rubygen.cmake \
+ $(rgen_amqp_0_10_srcs) \
+ qpid/amqp_0_10/apply.h \
+ qpid/amqp_0_10/built_in_types.h \
+ qpid/amqp_0_10/complex_types.cpp \
+ qpid/amqp_0_10/Array.h \
+ qpid/amqp_0_10/Array.cpp \
+ qpid/amqp_0_10/Body.h \
+ qpid/amqp_0_10/Command.h \
+ qpid/amqp_0_10/CommmandPacker.h \
+ qpid/amqp_0_10/Control.h \
+ qpid/amqp_0_10/Header.h \
+ qpid/amqp_0_10/Header.cpp \
+ qpid/amqp_0_10/FrameHeader.h \
+ qpid/amqp_0_10/FrameHeader.cpp \
+ qpid/amqp_0_10/Holder.h \
+ qpid/amqp_0_10/Codec.h \
+ qpid/amqp_0_10/Packer.h \
+ qpid/amqp_0_10/Decimal.h \
+ qpid/amqp_0_10/SerializableString.h \
+ qpid/amqp_0_10/Map.h \
+ qpid/amqp_0_10/Map.cpp \
+ qpid/amqp_0_10/Struct.h \
+ qpid/amqp_0_10/Struct32.h \
+ qpid/amqp_0_10/Struct32.cpp \
+ qpid/amqp_0_10/Unit.h \
+ qpid/amqp_0_10/Unit.cpp \
+ qpid/amqp_0_10/UnitHandler.h \
+ qpid/amqp_0_10/UnknownType.h \
+ qpid/amqp_0_10/UnknownType.cpp \
+ qpid/amqp_0_10/UnknownStruct.h \
+ qpid/amqp_0_10/UnknownStruct.cpp \
+ qpid/store
+
+libqpidcommon_la_LIBADD = \
+ libqpidtypes.la \
+ -lboost_program_options \
+ -lboost_filesystem \
+ -luuid \
+ $(LIB_DLOPEN) \
+ $(LIB_CLOCK_GETTIME)
+
+libqpidcommon_la_SOURCES += \
+ $(rgen_framing_srcs) \
+ $(platform_src) \
+ qpid/Address.cpp \
+ qpid/DataDir.cpp \
+ qpid/DataDir.h \
+ qpid/DisableExceptionLogging.h \
+ qpid/Exception.cpp \
+ qpid/Modules.cpp \
+ qpid/Modules.h \
+ qpid/Options.cpp \
+ qpid/Plugin.cpp \
+ qpid/Plugin.h \
+ qpid/RefCounted.h \
+ qpid/RefCountedBuffer.cpp \
+ qpid/RefCountedBuffer.h \
+ qpid/BufferRef.h \
+ qpid/Sasl.h \
+ qpid/SaslFactory.cpp \
+ qpid/SaslFactory.h \
+ qpid/Serializer.h \
+ qpid/SessionId.cpp \
+ qpid/SessionState.cpp \
+ qpid/SessionState.h \
+ qpid/SessionState.h \
+ qpid/SharedObject.h \
+ qpid/StringUtils.cpp \
+ qpid/StringUtils.h \
+ qpid/Url.cpp \
+ qpid/Version.h \
+ qpid/amqp_0_10/Exception.h \
+ qpid/amqp_0_10/SessionHandler.cpp \
+ qpid/amqp_0_10/SessionHandler.h \
+ qpid/amqp_0_10/apply.h \
+ qpid/assert.cpp qpid/assert.h \
+ qpid/assert.h \
+ qpid/framing/AMQBody.cpp \
+ qpid/framing/AMQBody.h \
+ qpid/framing/AMQCommandControlBody.h \
+ qpid/framing/AMQContentBody.cpp \
+ qpid/framing/AMQContentBody.h \
+ qpid/framing/AMQDataBlock.h \
+ qpid/framing/AMQFrame.cpp \
+ qpid/framing/AMQFrame.h \
+ qpid/framing/AMQHeaderBody.cpp \
+ qpid/framing/AMQHeaderBody.h \
+ qpid/framing/AMQHeartbeatBody.cpp \
+ qpid/framing/AMQHeartbeatBody.h \
+ qpid/framing/AMQMethodBody.cpp \
+ qpid/framing/AMQMethodBody.h \
+ qpid/framing/AMQP_HighestVersion.h \
+ qpid/framing/AMQP_HighestVersion.h \
+ qpid/framing/AccumulatedAck.cpp \
+ qpid/framing/AccumulatedAck.h \
+ qpid/framing/Array.cpp \
+ qpid/framing/BodyFactory.h \
+ qpid/framing/BodyHandler.cpp \
+ qpid/framing/BodyHandler.h \
+ qpid/framing/Buffer.cpp \
+ qpid/framing/ResizableBuffer.h \
+ qpid/framing/ChannelHandler.h \
+ qpid/framing/Endian.cpp \
+ qpid/framing/Endian.h \
+ qpid/framing/FieldTable.cpp \
+ qpid/framing/FieldValue.cpp \
+ qpid/framing/FrameDecoder.cpp \
+ qpid/framing/FrameDecoder.h \
+ qpid/framing/FrameDefaultVisitor.h \
+ qpid/framing/FrameHandler.h \
+ qpid/framing/FrameSet.cpp \
+ qpid/framing/FrameSet.h \
+ qpid/framing/Handler.h \
+ qpid/framing/HeaderProperties.h \
+ qpid/framing/InitiationHandler.h \
+ qpid/framing/InputHandler.h \
+ qpid/framing/Invoker.h \
+ qpid/framing/IsInSequenceSet.h \
+ qpid/framing/List.cpp \
+ qpid/framing/MethodBodyFactory.h \
+ qpid/framing/MethodContent.h \
+ qpid/framing/ModelMethod.h \
+ qpid/framing/OutputHandler.h \
+ qpid/framing/ProtocolInitiation.cpp \
+ qpid/framing/ProtocolInitiation.h \
+ qpid/framing/ProtocolVersion.cpp \
+ qpid/framing/Proxy.cpp \
+ qpid/framing/Proxy.h \
+ qpid/framing/SendContent.cpp \
+ qpid/framing/SendContent.h \
+ qpid/framing/SequenceNumber.cpp \
+ qpid/framing/SequenceNumberSet.cpp \
+ qpid/framing/SequenceNumberSet.h \
+ qpid/framing/SequenceSet.cpp \
+ qpid/framing/TransferContent.cpp \
+ qpid/framing/TransferContent.h \
+ qpid/framing/TypeFilter.h \
+ qpid/framing/Uuid.cpp \
+ qpid/framing/Visitor.h \
+ qpid/framing/amqp_framing.h \
+ qpid/framing/frame_functors.h \
+ qpid/framing/variant.h \
+ qpid/log/Helpers.h \
+ qpid/log/Logger.cpp \
+ qpid/log/Options.cpp \
+ qpid/log/OstreamOutput.cpp \
+ qpid/log/OstreamOutput.h \
+ qpid/log/Selector.cpp \
+ qpid/log/Statement.cpp \
+ qpid/management/Buffer.cpp \
+ qpid/management/ConnectionSettings.cpp \
+ qpid/management/Manageable.cpp \
+ qpid/management/ManagementObject.cpp \
+ qpid/management/Mutex.cpp \
+ qpid/memory.h \
+ qpid/pointer_to_other.h \
+ qpid/ptr_map.h \
+ qpid/sys/AggregateOutput.cpp \
+ qpid/sys/AggregateOutput.h \
+ qpid/sys/AsynchIO.h \
+ qpid/sys/AsynchIOHandler.cpp \
+ qpid/sys/AsynchIOHandler.h \
+ qpid/sys/AtomicCount.h \
+ qpid/sys/AtomicValue.h \
+ qpid/sys/AtomicValue_gcc.h \
+ qpid/sys/AtomicValue_mutex.h \
+ qpid/sys/BlockingQueue.h \
+ qpid/sys/ClusterSafe.h \
+ qpid/sys/ClusterSafe.cpp \
+ qpid/sys/Codec.h \
+ qpid/sys/ConnectionCodec.h \
+ qpid/sys/ConnectionInputHandler.h \
+ qpid/sys/ConnectionInputHandlerFactory.h \
+ qpid/sys/ConnectionOutputHandler.h \
+ qpid/sys/ConnectionOutputHandlerPtr.h \
+ qpid/sys/CopyOnWriteArray.h \
+ qpid/sys/DeletionManager.h \
+ qpid/sys/DispatchHandle.cpp \
+ qpid/sys/DispatchHandle.h \
+ qpid/sys/Dispatcher.cpp \
+ qpid/sys/Dispatcher.h \
+ qpid/sys/FileSysDir.h \
+ qpid/sys/Fork.h \
+ qpid/sys/LockFile.h \
+ qpid/sys/LockPtr.h \
+ qpid/sys/OutputControl.h \
+ qpid/sys/OutputTask.h \
+ qpid/sys/PipeHandle.h \
+ qpid/sys/PollableCondition.h \
+ qpid/sys/PollableQueue.h \
+ qpid/sys/Poller.h \
+ qpid/sys/ProtocolFactory.h \
+ qpid/sys/Runnable.cpp \
+ qpid/sys/ScopedIncrement.h \
+ qpid/sys/SecurityLayer.h \
+ qpid/sys/SecuritySettings.h \
+ qpid/sys/Semaphore.h \
+ qpid/sys/Shlib.cpp \
+ qpid/sys/Shlib.h \
+ qpid/sys/ShutdownHandler.h \
+ qpid/sys/Socket.h \
+ qpid/sys/SocketAddress.h \
+ qpid/sys/StateMonitor.h \
+ qpid/sys/TimeoutHandler.h \
+ qpid/sys/Timer.cpp \
+ qpid/sys/Timer.h \
+ qpid/sys/TimerWarnings.cpp \
+ qpid/sys/TimerWarnings.h \
+ qpid/sys/Waitable.h \
+ qpid/sys/alloca.h \
+ qpid/sys/uuid.h \
+ qpid/amqp_0_10/Codecs.cpp
+
+if HAVE_SASL
+libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.h
+libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.cpp
+libqpidcommon_la_LIBADD += -lsasl2
+endif
+
+QPIDCOMMON_VERSION_INFO = 2:0:0
+libqpidcommon_la_LDFLAGS=-version-info $(QPIDCOMMON_VERSION_INFO)
+
+libqpidbroker_la_LIBADD = libqpidcommon.la
+libqpidbroker_la_SOURCES = \
+ $(mgen_broker_cpp) \
+ $(posix_broker_src) \
+ qpid/amqp_0_10/Connection.cpp \
+ qpid/amqp_0_10/Connection.h \
+ qpid/broker/AclModule.h \
+ qpid/broker/Bridge.cpp \
+ qpid/broker/Bridge.h \
+ qpid/broker/Broker.cpp \
+ qpid/broker/Broker.h \
+ qpid/broker/BrokerImportExport.h \
+ qpid/broker/Connection.cpp \
+ qpid/broker/Connection.h \
+ qpid/broker/ConnectionFactory.cpp \
+ qpid/broker/ConnectionFactory.h \
+ qpid/broker/ConnectionHandler.cpp \
+ qpid/broker/ConnectionHandler.h \
+ qpid/broker/ConnectionState.h \
+ qpid/broker/ConnectionToken.h \
+ qpid/broker/Consumer.h \
+ qpid/broker/Daemon.cpp \
+ qpid/broker/Daemon.h \
+ qpid/broker/Deliverable.h \
+ qpid/broker/DeliverableMessage.cpp \
+ qpid/broker/DeliverableMessage.h \
+ qpid/broker/DeliveryAdapter.h \
+ qpid/broker/DeliveryId.h \
+ qpid/broker/DeliveryRecord.cpp \
+ qpid/broker/DeliveryRecord.h \
+ qpid/broker/DirectExchange.cpp \
+ qpid/broker/DirectExchange.h \
+ qpid/broker/DtxAck.cpp \
+ qpid/broker/DtxAck.h \
+ qpid/broker/DtxBuffer.cpp \
+ qpid/broker/DtxBuffer.h \
+ qpid/broker/DtxManager.cpp \
+ qpid/broker/DtxManager.h \
+ qpid/broker/DtxTimeout.cpp \
+ qpid/broker/DtxTimeout.h \
+ qpid/broker/DtxWorkRecord.cpp \
+ qpid/broker/DtxWorkRecord.h \
+ qpid/broker/Exchange.cpp \
+ qpid/broker/Exchange.h \
+ qpid/broker/ExchangeRegistry.cpp \
+ qpid/broker/ExchangeRegistry.h \
+ qpid/broker/ExpiryPolicy.cpp \
+ qpid/broker/ExpiryPolicy.h \
+ qpid/broker/Fairshare.h \
+ qpid/broker/Fairshare.cpp \
+ qpid/broker/FanOutExchange.cpp \
+ qpid/broker/FanOutExchange.h \
+ qpid/broker/FedOps.h \
+ qpid/broker/HandlerImpl.h \
+ qpid/broker/HeadersExchange.cpp \
+ qpid/broker/HeadersExchange.h \
+ qpid/broker/AsyncCompletion.h \
+ qpid/broker/LegacyLVQ.h \
+ qpid/broker/LegacyLVQ.cpp \
+ qpid/broker/Link.cpp \
+ qpid/broker/Link.h \
+ qpid/broker/LinkRegistry.cpp \
+ qpid/broker/LinkRegistry.h \
+ qpid/broker/Message.cpp \
+ qpid/broker/Message.h \
+ qpid/broker/MessageAdapter.cpp \
+ qpid/broker/MessageAdapter.h \
+ qpid/broker/MessageBuilder.cpp \
+ qpid/broker/MessageBuilder.h \
+ qpid/broker/MessageDeque.h \
+ qpid/broker/MessageDeque.cpp \
+ qpid/broker/MessageMap.h \
+ qpid/broker/MessageMap.cpp \
+ qpid/broker/Messages.h \
+ qpid/broker/MessageStore.h \
+ qpid/broker/MessageStoreModule.cpp \
+ qpid/broker/MessageStoreModule.h \
+ qpid/broker/PriorityQueue.h \
+ qpid/broker/PriorityQueue.cpp \
+ qpid/broker/NameGenerator.cpp \
+ qpid/broker/NameGenerator.h \
+ qpid/broker/NullMessageStore.cpp \
+ qpid/broker/NullMessageStore.h \
+ qpid/broker/OwnershipToken.h \
+ qpid/broker/Persistable.h \
+ qpid/broker/PersistableConfig.h \
+ qpid/broker/PersistableExchange.h \
+ qpid/broker/PersistableMessage.cpp \
+ qpid/broker/PersistableMessage.h \
+ qpid/broker/PersistableQueue.h \
+ qpid/broker/Queue.cpp \
+ qpid/broker/Queue.h \
+ qpid/broker/QueueBindings.cpp \
+ qpid/broker/QueueBindings.h \
+ qpid/broker/QueueCleaner.cpp \
+ qpid/broker/QueueCleaner.h \
+ qpid/broker/QueueEvents.cpp \
+ qpid/broker/QueueEvents.h \
+ qpid/broker/QueueListeners.cpp \
+ qpid/broker/QueueListeners.h \
+ qpid/broker/QueueObserver.h \
+ qpid/broker/QueuePolicy.cpp \
+ qpid/broker/QueuePolicy.h \
+ qpid/broker/QueueRegistry.cpp \
+ qpid/broker/QueueRegistry.h \
+ qpid/broker/QueuedMessage.h \
+ qpid/broker/QueueFlowLimit.h \
+ qpid/broker/QueueFlowLimit.cpp \
+ qpid/broker/RateFlowcontrol.h \
+ qpid/broker/RateTracker.cpp \
+ qpid/broker/RateTracker.h \
+ qpid/broker/RecoverableConfig.h \
+ qpid/broker/RecoverableExchange.h \
+ qpid/broker/RecoverableMessage.h \
+ qpid/broker/RecoverableQueue.h \
+ qpid/broker/RecoverableTransaction.h \
+ qpid/broker/RecoveredDequeue.cpp \
+ qpid/broker/RecoveredDequeue.h \
+ qpid/broker/RecoveredEnqueue.cpp \
+ qpid/broker/RecoveredEnqueue.h \
+ qpid/broker/RecoveryManager.h \
+ qpid/broker/RecoveryManagerImpl.cpp \
+ qpid/broker/RecoveryManagerImpl.h \
+ qpid/broker/RetryList.cpp \
+ qpid/broker/RetryList.h \
+ qpid/broker/SaslAuthenticator.cpp \
+ qpid/broker/SaslAuthenticator.h \
+ qpid/broker/SecureConnection.cpp \
+ qpid/broker/SecureConnection.h \
+ qpid/broker/SecureConnectionFactory.cpp \
+ qpid/broker/SecureConnectionFactory.h \
+ qpid/broker/SemanticState.cpp \
+ qpid/broker/SemanticState.h \
+ qpid/broker/SessionAdapter.cpp \
+ qpid/broker/SessionAdapter.h \
+ qpid/broker/SessionAdapter.h \
+ qpid/broker/SessionContext.h \
+ qpid/broker/SessionHandler.cpp \
+ qpid/broker/SessionHandler.h \
+ qpid/broker/SessionManager.cpp \
+ qpid/broker/SessionManager.h \
+ qpid/broker/SessionManager.h \
+ qpid/broker/SessionOutputException.h \
+ qpid/broker/SessionState.cpp \
+ qpid/broker/SessionState.h \
+ qpid/broker/SignalHandler.cpp \
+ qpid/broker/SignalHandler.h \
+ qpid/broker/System.cpp \
+ qpid/broker/System.h \
+ qpid/broker/ThresholdAlerts.cpp \
+ qpid/broker/ThresholdAlerts.h \
+ qpid/broker/TopicExchange.cpp \
+ qpid/broker/TopicExchange.h \
+ qpid/broker/TransactionalStore.h \
+ qpid/broker/TxAccept.cpp \
+ qpid/broker/TxAccept.h \
+ qpid/broker/TxBuffer.cpp \
+ qpid/broker/TxBuffer.h \
+ qpid/broker/TxOp.h \
+ qpid/broker/TxOpVisitor.h \
+ qpid/broker/TxPublish.cpp \
+ qpid/broker/TxPublish.h \
+ qpid/broker/Vhost.cpp \
+ qpid/broker/Vhost.h \
+ qpid/management/ManagementAgent.cpp \
+ qpid/management/ManagementAgent.h \
+ qpid/management/ManagementDirectExchange.cpp \
+ qpid/management/ManagementDirectExchange.h \
+ qpid/management/ManagementTopicExchange.cpp \
+ qpid/management/ManagementTopicExchange.h \
+ qpid/sys/TCPIOPlugin.cpp
+
+QPIDBROKER_VERSION_INFO = 2:0:0
+libqpidbroker_la_LDFLAGS = -version-info $(QPIDBROKER_VERSION_INFO)
+
+libqpidclient_la_LIBADD = libqpidcommon.la -luuid
+
+libqpidclient_la_SOURCES = \
+ $(rgen_client_srcs) \
+ qpid/client/Bounds.cpp \
+ qpid/client/Bounds.h \
+ qpid/client/ChainableFrameHandler.h \
+ qpid/client/Completion.cpp \
+ qpid/client/CompletionImpl.h \
+ qpid/client/Connection.cpp \
+ qpid/client/ConnectionAccess.h \
+ qpid/client/ConnectionHandler.cpp \
+ qpid/client/ConnectionHandler.h \
+ qpid/client/ConnectionImpl.cpp \
+ qpid/client/ConnectionImpl.h \
+ qpid/client/ConnectionSettings.cpp \
+ qpid/client/Connector.cpp \
+ qpid/client/Connector.h \
+ qpid/client/Demux.cpp \
+ qpid/client/Demux.h \
+ qpid/client/Dispatcher.cpp \
+ qpid/client/Dispatcher.h \
+ qpid/client/Execution.h \
+ qpid/client/FailoverListener.cpp \
+ qpid/client/FailoverManager.cpp \
+ qpid/client/Future.cpp \
+ qpid/client/FutureCompletion.cpp \
+ qpid/client/FutureResult.cpp \
+ qpid/client/LoadPlugins.h \
+ qpid/client/LoadPlugins.cpp \
+ qpid/client/LocalQueue.cpp \
+ qpid/client/LocalQueueImpl.cpp \
+ qpid/client/LocalQueueImpl.h \
+ qpid/client/Message.cpp \
+ qpid/client/MessageImpl.cpp \
+ qpid/client/MessageImpl.h \
+ qpid/client/MessageListener.cpp \
+ qpid/client/MessageReplayTracker.cpp \
+ qpid/client/PrivateImplRef.h \
+ qpid/client/QueueOptions.cpp \
+ qpid/client/Results.cpp \
+ qpid/client/Results.h \
+ qpid/client/SessionBase_0_10.cpp \
+ qpid/client/SessionBase_0_10Access.h \
+ qpid/client/SessionImpl.cpp \
+ qpid/client/SessionImpl.h \
+ qpid/client/StateManager.cpp \
+ qpid/client/StateManager.h \
+ qpid/client/Subscription.cpp \
+ qpid/client/SubscriptionImpl.cpp \
+ qpid/client/SubscriptionImpl.h \
+ qpid/client/SubscriptionManager.cpp \
+ qpid/client/SubscriptionManagerImpl.cpp \
+ qpid/client/SubscriptionManagerImpl.h \
+ qpid/client/TCPConnector.cpp \
+ qpid/client/TCPConnector.h
+
+QPIDCLIENT_VERSION_INFO = 2:0:0
+libqpidclient_la_LDFLAGS = -version-info $(QPIDCLIENT_VERSION_INFO)
+
+libqpidtypes_la_LIBADD= -luuid
+libqpidtypes_la_SOURCES= \
+ qpid/types/Exception.cpp \
+ qpid/types/Uuid.cpp \
+ qpid/types/Variant.cpp \
+ ../include/qpid/types/ImportExport.h
+
+QPIDTYPES_VERSION_INFO = 1:0:0
+libqpidtypes_la_LDFLAGS = -version-info $(QPIDTYPES_VERSION_INFO)
+
+libqpidmessaging_la_LIBADD = libqpidclient.la libqpidtypes.la
+libqpidmessaging_la_SOURCES = \
+ qpid/messaging/Address.cpp \
+ qpid/messaging/AddressParser.h \
+ qpid/messaging/AddressParser.cpp \
+ qpid/messaging/Connection.cpp \
+ qpid/messaging/Duration.cpp \
+ qpid/messaging/exceptions.cpp \
+ qpid/messaging/Message.cpp \
+ qpid/messaging/MessageImpl.h \
+ qpid/messaging/MessageImpl.cpp \
+ qpid/messaging/PrivateImplRef.h \
+ qpid/messaging/Sender.cpp \
+ qpid/messaging/Receiver.cpp \
+ qpid/messaging/Session.cpp \
+ qpid/messaging/ConnectionImpl.h \
+ qpid/messaging/SenderImpl.h \
+ qpid/messaging/ReceiverImpl.h \
+ qpid/messaging/SessionImpl.h \
+ qpid/messaging/FailoverUpdates.cpp \
+ qpid/client/amqp0_10/AcceptTracker.h \
+ qpid/client/amqp0_10/AcceptTracker.cpp \
+ qpid/client/amqp0_10/AddressResolution.h \
+ qpid/client/amqp0_10/AddressResolution.cpp \
+ qpid/client/amqp0_10/ConnectionImpl.h \
+ qpid/client/amqp0_10/ConnectionImpl.cpp \
+ qpid/client/amqp0_10/IncomingMessages.h \
+ qpid/client/amqp0_10/IncomingMessages.cpp \
+ qpid/client/amqp0_10/MessageSink.h \
+ qpid/client/amqp0_10/MessageSource.h \
+ qpid/client/amqp0_10/OutgoingMessage.h \
+ qpid/client/amqp0_10/OutgoingMessage.cpp \
+ qpid/client/amqp0_10/ReceiverImpl.h \
+ qpid/client/amqp0_10/ReceiverImpl.cpp \
+ qpid/client/amqp0_10/SessionImpl.h \
+ qpid/client/amqp0_10/SessionImpl.cpp \
+ qpid/client/amqp0_10/SenderImpl.h \
+ qpid/client/amqp0_10/SenderImpl.cpp \
+ qpid/client/amqp0_10/SimpleUrlParser.h \
+ qpid/client/amqp0_10/SimpleUrlParser.cpp
+
+QPIDMESSAGING_VERSION_INFO = 2:0:0
+libqpidmessaging_la_LDFLAGS = -version-info $(QPIDMESSAGING_VERSION_INFO)
+
+# NOTE: only public header files (which should be in ../include)
+# should go in this list. Private headers should go in the SOURCES
+# list for one of the libraries or executables that includes it.
+
+nobase_include_HEADERS += \
+ ../include/qpid/Address.h \
+ ../include/qpid/CommonImportExport.h \
+ ../include/qpid/Exception.h \
+ ../include/qpid/ImportExport.h \
+ ../include/qpid/InlineAllocator.h \
+ ../include/qpid/InlineVector.h \
+ ../include/qpid/Msg.h \
+ ../include/qpid/Options.h \
+ ../include/qpid/RangeSet.h \
+ ../include/qpid/SessionId.h \
+ ../include/qpid/Url.h \
+ ../include/qpid/amqp_0_10/Codecs.h \
+ ../include/qpid/client/AsyncSession.h \
+ ../include/qpid/client/ClientImportExport.h \
+ ../include/qpid/client/Completion.h \
+ ../include/qpid/client/Connection.h \
+ ../include/qpid/client/ConnectionSettings.h \
+ ../include/qpid/client/FailoverListener.h \
+ ../include/qpid/client/FailoverManager.h \
+ ../include/qpid/client/FlowControl.h \
+ ../include/qpid/client/Future.h \
+ ../include/qpid/client/FutureCompletion.h \
+ ../include/qpid/client/FutureResult.h \
+ ../include/qpid/client/Handle.h \
+ ../include/qpid/client/LocalQueue.h \
+ ../include/qpid/client/Message.h \
+ ../include/qpid/client/MessageListener.h \
+ ../include/qpid/client/MessageReplayTracker.h \
+ ../include/qpid/client/QueueOptions.h \
+ ../include/qpid/client/Session.h \
+ ../include/qpid/client/SessionBase_0_10.h \
+ ../include/qpid/client/Subscription.h \
+ ../include/qpid/client/SubscriptionManager.h \
+ ../include/qpid/client/SubscriptionSettings.h \
+ ../include/qpid/client/TypedResult.h \
+ ../include/qpid/framing/Array.h \
+ ../include/qpid/framing/Buffer.h \
+ ../include/qpid/framing/FieldTable.h \
+ ../include/qpid/framing/FieldValue.h \
+ ../include/qpid/framing/List.h \
+ ../include/qpid/framing/ProtocolVersion.h \
+ ../include/qpid/framing/SequenceNumber.h \
+ ../include/qpid/framing/SequenceSet.h \
+ ../include/qpid/framing/StructHelper.h \
+ ../include/qpid/framing/Uuid.h \
+ ../include/qpid/framing/amqp_types.h \
+ ../include/qpid/framing/amqp_types_full.h \
+ ../include/qpid/log/Logger.h \
+ ../include/qpid/log/Options.h \
+ ../include/qpid/log/Selector.h \
+ ../include/qpid/log/SinkOptions.h \
+ ../include/qpid/log/Statement.h \
+ ../include/qpid/management/Args.h \
+ ../include/qpid/management/Buffer.h \
+ ../include/qpid/management/ConnectionSettings.h \
+ ../include/qpid/management/Manageable.h \
+ ../include/qpid/management/ManagementEvent.h \
+ ../include/qpid/management/ManagementObject.h \
+ ../include/qpid/management/Mutex.h \
+ ../include/qpid/sys/Condition.h \
+ ../include/qpid/sys/ExceptionHolder.h \
+ ../include/qpid/sys/IOHandle.h \
+ ../include/qpid/sys/IntegerTypes.h \
+ ../include/qpid/sys/Monitor.h \
+ ../include/qpid/sys/Mutex.h \
+ ../include/qpid/sys/Runnable.h \
+ ../include/qpid/sys/StrError.h \
+ ../include/qpid/sys/SystemInfo.h \
+ ../include/qpid/sys/Thread.h \
+ ../include/qpid/sys/Time.h \
+ ../include/qpid/messaging/Address.h \
+ ../include/qpid/messaging/Connection.h \
+ ../include/qpid/messaging/Duration.h \
+ ../include/qpid/messaging/exceptions.h \
+ ../include/qpid/messaging/Handle.h \
+ ../include/qpid/messaging/ImportExport.h \
+ ../include/qpid/messaging/Message.h \
+ ../include/qpid/messaging/Receiver.h \
+ ../include/qpid/messaging/Sender.h \
+ ../include/qpid/messaging/Session.h \
+ ../include/qpid/messaging/FailoverUpdates.h \
+ ../include/qpid/types/Exception.h \
+ ../include/qpid/types/Uuid.h \
+ ../include/qpid/types/Variant.h \
+ ../include/qpid/types/ImportExport.h
+
+# Create the default data directory
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)/$(localstatedir)/lib/qpidd
+
+# Support for pkg-config
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = qpid.pc
diff --git a/qpid/cpp/src/acl.mk b/qpid/cpp/src/acl.mk
new file mode 100644
index 0000000000..b8e2ff0e13
--- /dev/null
+++ b/qpid/cpp/src/acl.mk
@@ -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.
+#
+#
+# acl library makefile fragment, to be included in Makefile.am
+#
+dmoduleexec_LTLIBRARIES += acl.la
+
+acl_la_SOURCES = \
+ qpid/acl/Acl.cpp \
+ qpid/acl/Acl.h \
+ qpid/acl/AclData.cpp \
+ qpid/acl/AclData.h \
+ qpid/acl/AclPlugin.cpp \
+ qpid/acl/AclReader.cpp \
+ qpid/acl/AclReader.h \
+ qpid/acl/AclValidator.cpp \
+ qpid/acl/AclValidator.h
+
+acl_la_LIBADD = libqpidbroker.la
+if SUNOS
+ acl_la_LIBADD += libqmfagent.la libqmfconsole.la libqpidcommon.la -lboost_program_options $(SUNCC_RUNTIME_LIBS)
+endif
+
+acl_la_LDFLAGS = $(PLUGINLDFLAGS)
+
diff --git a/qpid/cpp/src/cluster.cmake b/qpid/cpp/src/cluster.cmake
new file mode 100644
index 0000000000..a389f8f13f
--- /dev/null
+++ b/qpid/cpp/src/cluster.cmake
@@ -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.
+#
+#
+# Cluster library CMake fragment, to be included in CMakeLists.txt
+#
+
+# Optional cluster support. Requires CPG; if building it, can optionally
+# include CMAN support as well.
+
+include(CheckIncludeFiles)
+include(CheckLibraryExists)
+
+set(LIBCPG_PATH /usr/lib/openais /usr/lib64/openais /usr/lib/corosync /usr/lib64/corosync CACHE STRING "Default locations for libcpg (cluster library)" )
+find_library(LIBCPG cpg ${LIBCPG_PATH})
+if (LIBCPG)
+ CHECK_LIBRARY_EXISTS (${LIBCPG} cpg_local_get "" HAVE_LIBCPG)
+ CHECK_INCLUDE_FILES (openais/cpg.h HAVE_OPENAIS_CPG_H)
+ CHECK_INCLUDE_FILES (corosync/cpg.h HAVE_COROSYNC_CPG_H)
+endif (LIBCPG)
+
+set (cluster_default ${cluster_force})
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (HAVE_LIBCPG)
+ if (HAVE_OPENAIS_CPG_H OR HAVE_COROSYNC_CPG_H)
+ set (cluster_default ON)
+ endif (HAVE_OPENAIS_CPG_H OR HAVE_COROSYNC_CPG_H)
+ endif (HAVE_LIBCPG)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+option(BUILD_CLUSTER "Build with CPG support for clustering" ${cluster_default})
+if (BUILD_CLUSTER)
+
+ if (NOT HAVE_LIBCPG)
+ message(FATAL_ERROR "libcpg not found, install openais-devel or corosync-devel")
+ endif (NOT HAVE_LIBCPG)
+ if (NOT HAVE_OPENAIS_CPG_H AND NOT HAVE_COROSYNC_CPG_H)
+ message(FATAL_ERROR "cpg.h not found, install openais-devel or corosync-devel")
+ endif (NOT HAVE_OPENAIS_CPG_H AND NOT HAVE_COROSYNC_CPG_H)
+
+ CHECK_LIBRARY_EXISTS (cman cman_is_quorate "" HAVE_LIBCMAN)
+ CHECK_INCLUDE_FILES (libcman.h HAVE_LIBCMAN_H)
+
+ set(cluster_quorum_default ${cluster_quorum_force})
+ if (HAVE_LIBCMAN AND HAVE_LIBCMAN_H)
+ set(cluster_quorum_default ON)
+ endif (HAVE_LIBCMAN AND HAVE_LIBCMAN_H)
+
+ option(BUILD_CLUSTER_QUORUM "Include libcman quorum service integration" ${cluster_quorum_default})
+ if (BUILD_CLUSTER_QUORUM)
+ if (NOT HAVE_LIBCMAN)
+ message(FATAL_ERROR "libcman not found, install cman-devel or cmanlib-devel")
+ endif (NOT HAVE_LIBCMAN)
+ if (NOT HAVE_LIBCMAN_H)
+ message(FATAL_ERROR "libcman.h not found, install cman-devel or cmanlib-devel")
+ endif (NOT HAVE_LIBCMAN_H)
+
+ set (CMAN_SOURCES qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp)
+ set (CMAN_LIB cman)
+ else (BUILD_CLUSTER_QUORUM)
+ set (CMAN_SOURCES qpid/cluster/Quorum_null.h)
+ endif (BUILD_CLUSTER_QUORUM)
+
+ set (cluster_SOURCES
+ ${CMAN_SOURCES}
+ qpid/cluster/Cluster.cpp
+ qpid/cluster/Cluster.h
+ qpid/cluster/ClusterTimer.cpp
+ qpid/cluster/ClusterTimer.h
+ qpid/cluster/Decoder.cpp
+ qpid/cluster/Decoder.h
+ qpid/cluster/PollableQueue.h
+ qpid/cluster/ClusterMap.cpp
+ qpid/cluster/ClusterMap.h
+ qpid/cluster/ClusterPlugin.cpp
+ qpid/cluster/ClusterSettings.h
+ qpid/cluster/Connection.cpp
+ qpid/cluster/Connection.h
+ qpid/cluster/ConnectionCodec.cpp
+ qpid/cluster/ConnectionCodec.h
+ qpid/cluster/Cpg.cpp
+ qpid/cluster/Cpg.h
+ qpid/cluster/Dispatchable.h
+ qpid/cluster/UpdateClient.cpp
+ qpid/cluster/UpdateClient.h
+ qpid/cluster/RetractClient.cpp
+ qpid/cluster/RetractClient.h
+ qpid/cluster/ErrorCheck.cpp
+ qpid/cluster/ErrorCheck.h
+ qpid/cluster/Event.cpp
+ qpid/cluster/Event.h
+ qpid/cluster/EventFrame.h
+ qpid/cluster/EventFrame.cpp
+ qpid/cluster/ExpiryPolicy.h
+ qpid/cluster/ExpiryPolicy.cpp
+ qpid/cluster/FailoverExchange.cpp
+ qpid/cluster/FailoverExchange.h
+ qpid/cluster/UpdateExchange.cpp
+ qpid/cluster/UpdateExchange.h
+ qpid/cluster/UpdateReceiver.h
+ qpid/cluster/LockedConnectionMap.h
+ qpid/cluster/Multicaster.cpp
+ qpid/cluster/Multicaster.h
+ qpid/cluster/McastFrameHandler.h
+ qpid/cluster/NoOpConnectionOutputHandler.h
+ qpid/cluster/Numbering.h
+ qpid/cluster/OutputInterceptor.cpp
+ qpid/cluster/OutputInterceptor.h
+ qpid/cluster/PollerDispatch.cpp
+ qpid/cluster/PollerDispatch.h
+ qpid/cluster/ProxyInputHandler.h
+ qpid/cluster/Quorum.h
+ qpid/cluster/InitialStatusMap.h
+ qpid/cluster/InitialStatusMap.cpp
+ qpid/cluster/MemberSet.h
+ qpid/cluster/MemberSet.cpp
+ qpid/cluster/types.h
+ qpid/cluster/SecureConnectionFactory.h
+ qpid/cluster/SecureConnectionFactory.cpp
+ qpid/cluster/StoreStatus.h
+ qpid/cluster/StoreStatus.cpp
+ qpid/cluster/UpdateDataExchange.h
+ qpid/cluster/UpdateDataExchange.cpp
+ )
+
+ add_library (cluster MODULE ${cluster_SOURCES})
+ target_link_libraries (cluster ${LIBCPG} ${CMAN_LIB} qpidbroker qpidclient ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
+ set_target_properties (cluster PROPERTIES PREFIX "")
+
+ # Create a second shared library for linking with test executables,
+ # cmake will not allow a module to be linked with an executable.
+ add_library (cluster_shared SHARED ${cluster_SOURCES})
+ target_link_libraries (cluster_shared ${LIBCPG} ${CMAN_LIB} qpidbroker qpidclient ${Boost_FILESYSTEM_LIBRARY})
+
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(cluster PROPERTIES
+ LINK_FLAGS "-Wl,--no-undefined -pthread")
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS cluster
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+ add_library (watchdog MODULE qpid/cluster/WatchDogPlugin.cpp)
+ set_target_properties (watchdog PROPERTIES PREFIX "")
+
+ add_executable(qpidd_watchdog qpid/cluster/qpidd_watchdog.cpp)
+
+endif (BUILD_CLUSTER)
+
+# Distribute all sources.
+#EXTRA_DIST += qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp qpid/cluster/Quorum_null.h
diff --git a/qpid/cpp/src/cluster.mk b/qpid/cpp/src/cluster.mk
new file mode 100644
index 0000000000..3ce4ce25b3
--- /dev/null
+++ b/qpid/cpp/src/cluster.mk
@@ -0,0 +1,113 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Cluster library makefile fragment, to be included in Makefile.am
+#
+
+# Optional CMAN support
+
+# Distribute all sources.
+EXTRA_DIST += qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp qpid/cluster/Quorum_null.h
+
+if HAVE_LIBCMAN
+CMAN_SOURCES = qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp
+libcman = -lcman
+else
+CMAN_SOURCES = qpid/cluster/Quorum_null.h
+endif
+
+if HAVE_LIBCPG
+
+dmoduleexec_LTLIBRARIES += cluster.la
+
+cluster_la_SOURCES = \
+ $(CMAN_SOURCES) \
+ qpid/cluster/Cluster.cpp \
+ qpid/cluster/Cluster.h \
+ qpid/cluster/ClusterTimer.cpp \
+ qpid/cluster/ClusterTimer.h \
+ qpid/cluster/Decoder.cpp \
+ qpid/cluster/Decoder.h \
+ qpid/cluster/PollableQueue.h \
+ qpid/cluster/ClusterMap.cpp \
+ qpid/cluster/ClusterMap.h \
+ qpid/cluster/ClusterPlugin.cpp \
+ qpid/cluster/ClusterSettings.h \
+ qpid/cluster/Connection.cpp \
+ qpid/cluster/Connection.h \
+ qpid/cluster/ConnectionCodec.cpp \
+ qpid/cluster/ConnectionCodec.h \
+ qpid/cluster/Cpg.cpp \
+ qpid/cluster/Cpg.h \
+ qpid/cluster/Dispatchable.h \
+ qpid/cluster/UpdateClient.cpp \
+ qpid/cluster/UpdateClient.h \
+ qpid/cluster/RetractClient.cpp \
+ qpid/cluster/RetractClient.h \
+ qpid/cluster/ErrorCheck.cpp \
+ qpid/cluster/ErrorCheck.h \
+ qpid/cluster/Event.cpp \
+ qpid/cluster/Event.h \
+ qpid/cluster/EventFrame.h \
+ qpid/cluster/EventFrame.cpp \
+ qpid/cluster/ExpiryPolicy.h \
+ qpid/cluster/ExpiryPolicy.cpp \
+ qpid/cluster/FailoverExchange.cpp \
+ qpid/cluster/FailoverExchange.h \
+ qpid/cluster/UpdateExchange.h \
+ qpid/cluster/UpdateExchange.cpp \
+ qpid/cluster/UpdateReceiver.h \
+ qpid/cluster/LockedConnectionMap.h \
+ qpid/cluster/Multicaster.cpp \
+ qpid/cluster/Multicaster.h \
+ qpid/cluster/McastFrameHandler.h \
+ qpid/cluster/NoOpConnectionOutputHandler.h \
+ qpid/cluster/Numbering.h \
+ qpid/cluster/OutputInterceptor.cpp \
+ qpid/cluster/OutputInterceptor.h \
+ qpid/cluster/PollerDispatch.cpp \
+ qpid/cluster/PollerDispatch.h \
+ qpid/cluster/ProxyInputHandler.h \
+ qpid/cluster/Quorum.h \
+ qpid/cluster/InitialStatusMap.h \
+ qpid/cluster/InitialStatusMap.cpp \
+ qpid/cluster/MemberSet.h \
+ qpid/cluster/MemberSet.cpp \
+ qpid/cluster/types.h \
+ qpid/cluster/SecureConnectionFactory.h \
+ qpid/cluster/SecureConnectionFactory.cpp \
+ qpid/cluster/StoreStatus.h \
+ qpid/cluster/StoreStatus.cpp \
+ qpid/cluster/UpdateDataExchange.h \
+ qpid/cluster/UpdateDataExchange.cpp
+
+cluster_la_LIBADD= -lcpg $(libcman) libqpidbroker.la libqpidclient.la
+cluster_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
+cluster_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+# The watchdog plugin and helper executable
+dmoduleexec_LTLIBRARIES += watchdog.la
+watchdog_la_SOURCES = qpid/cluster/WatchDogPlugin.cpp
+watchdog_la_LIBADD = libqpidbroker.la
+watchdog_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+qpidexec_PROGRAMS += qpidd_watchdog
+qpidd_watchdog_SOURCES = qpid/cluster/qpidd_watchdog.cpp
+
+endif # HAVE_LIBCPG
diff --git a/qpid/cpp/src/config.h.cmake b/qpid/cpp/src/config.h.cmake
new file mode 100644
index 0000000000..2bb84c6e47
--- /dev/null
+++ b/qpid/cpp/src/config.h.cmake
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is automatically generated and will be overwritten by the
+ * next CMake invocation.
+ */
+
+#ifndef QPID_CONFIG_H
+#define QPID_CONFIG_H
+
+// PACKAGE_NAME and PACKAGE_VERSION are carry-overs from the autoconf world.
+// They tend to cause confusion and problems when mixing headers from multiple
+// autoconf-configured packages, so it's best to remove these in favor of
+// Qpid-specific names as soon as the autoconf stuff is removed.
+#define PACKAGE_NAME "${CMAKE_PROJECT_NAME}"
+#define PACKAGE_VERSION "${qpidc_version}"
+
+#cmakedefine QPIDC_CONF_FILE "${QPIDC_CONF_FILE}"
+#cmakedefine QPIDD_CONF_FILE "${QPIDD_CONF_FILE}"
+
+#cmakedefine QPIDC_MODULE_DIR "${QPIDC_MODULE_DIR}"
+#cmakedefine QPIDD_MODULE_DIR "${QPIDD_MODULE_DIR}"
+
+#cmakedefine QPID_LIBEXEC_DIR "${QPID_LIBEXEC_DIR}"
+
+#define QPID_SHLIB_PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}"
+#define QPID_MODULE_PREFIX
+#cmakedefine QPID_DEBUG_POSTFIX "${QPID_DEBUG_POSTFIX}"
+#if defined(QPID_DEBUG_POSTFIX) && defined (_DEBUG)
+# define QPID_SHLIB_POSTFIX QPID_DEBUG_POSTFIX
+# define QPID_MODULE_POSTFIX QPID_DEBUG_POSTFIX
+#else
+# define QPID_SHLIB_POSTFIX
+# define QPID_MODULE_POSTFIX
+#endif
+#define QPID_SHLIB_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}"
+#define QPID_MODULE_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}"
+
+#cmakedefine QPID_HAS_CLOCK_GETTIME
+
+#cmakedefine BROKER_SASL_NAME "${BROKER_SASL_NAME}"
+#cmakedefine HAVE_SASL ${HAVE_SASL}
+
+#cmakedefine HAVE_OPENAIS_CPG_H ${HAVE_OPENAIS_CPG_H}
+#cmakedefine HAVE_COROSYNC_CPG_H ${HAVE_COROSYNC_CPG_H}
+#cmakedefine HAVE_LIBCMAN_H ${HAVE_LIBCMAN_H}
+#cmakedefine HAVE_LOG_AUTHPRIV
+#cmakedefine HAVE_LOG_FTP
+
+#endif /* QPID_CONFIG_H */
diff --git a/qpid/cpp/src/generate.sh b/qpid/cpp/src/generate.sh
new file mode 100755
index 0000000000..581a45ff7f
--- /dev/null
+++ b/qpid/cpp/src/generate.sh
@@ -0,0 +1,67 @@
+# !/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.
+#
+
+# Generate code from AMQP specification.
+# specs and gentools_dir are set by Makefile
+#
+set -e
+
+test -z "$JAVA" && JAVA=java ;
+test -z "$JAVAC" && JAVAC=javac ;
+
+srcdir=`dirname $0`
+checkspecs() {
+ for s in $specs; do test -f $s || return 1; done
+ return 0
+}
+
+# Can we generate code?
+if { test -d $gentools_dir && checkspecs &&
+ which $JAVA && which $JAVAC; } > /dev/null;
+then
+ echo "Generating code."
+ mkdir -p gen/qpid/framing
+ ( cd $gentools_dir/src && $JAVAC `find -name '*.java' -print` ; )
+ $JAVA -cp $gentools_dir/src org.apache.qpid.gentools.Main \
+ -c -o gen/qpid/framing -t $gentools_dir/templ.cpp $specs
+ GENERATED=yes
+fi
+
+# Print a Makefile variable assignment.
+make_assign() {
+ echo -n "$1 = "; shift
+ prefix=$1; shift
+ for f in $*; do echo "\\" ; echo -n " $prefix$f "; done
+ echo
+}
+
+# Generate a Makefile fragment
+(
+ make_assign "generated_cpp" "" `find gen -name '*.cpp' -print`
+ make_assign "generated_h" "" `find gen -name '*.h' -print`
+ if test x$GENERATED = xyes; then
+ make_assign "generator" "" $specs \
+ `find ../gentools \( -name '*.java' -o -name '*.tmpl' \) -print`
+ fi
+) > generate.mk-t
+mv generate.mk-t $srcdir/generate.mk
+
+
+
diff --git a/qpid/cpp/src/posix/QpiddBroker.cpp b/qpid/cpp/src/posix/QpiddBroker.cpp
new file mode 100644
index 0000000000..879935462e
--- /dev/null
+++ b/qpid/cpp/src/posix/QpiddBroker.cpp
@@ -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.
+ *
+ */
+
+#include "config.h"
+#include "qpidd.h"
+#include "qpid/Exception.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Daemon.h"
+#include "qpid/broker/SignalHandler.h"
+#include "qpid/log/Logger.h"
+
+#include <iostream>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+using namespace std;
+using namespace qpid;
+using qpid::broker::Broker;
+using qpid::broker::Daemon;
+
+BootstrapOptions::BootstrapOptions(const char* argv0)
+ : qpid::Options("Options"),
+ common("", QPIDD_CONF_FILE),
+ module(QPIDD_MODULE_DIR),
+ log(argv0)
+{
+ add(common);
+ add(module);
+ add(log);
+}
+
+namespace {
+const std::string TCP = "tcp";
+}
+
+struct DaemonOptions : public qpid::Options {
+ bool daemon;
+ bool quit;
+ bool check;
+ int wait;
+ std::string piddir;
+ std::string transport;
+
+ DaemonOptions() : qpid::Options("Daemon options"), daemon(false), quit(false), check(false), wait(600), transport(TCP)
+ {
+ char *home = ::getenv("HOME");
+
+ if (home == 0)
+ piddir += "/tmp";
+ else
+ piddir += home;
+ piddir += "/.qpidd";
+
+ addOptions()
+ ("daemon,d", optValue(daemon), "Run as a daemon. Logs to syslog by default in this mode.")
+ ("transport", optValue(transport, "TRANSPORT"), "The transport for which to return the port")
+ ("pid-dir", optValue(piddir, "DIR"), "Directory where port-specific PID file is stored")
+ ("wait,w", optValue(wait, "SECONDS"), "Sets the maximum wait time to initialize the daemon. If the daemon fails to initialize, prints an error and returns 1")
+ ("check,c", optValue(check), "Prints the daemon's process ID to stdout and returns 0 if the daemon is running, otherwise returns 1")
+ ("quit,q", optValue(quit), "Tells the daemon to shut down");
+ }
+};
+
+struct QpiddPosixOptions : public QpiddOptionsPrivate {
+ DaemonOptions daemon;
+ QpiddOptions *parent;
+
+ QpiddPosixOptions(QpiddOptions *parent_) : parent(parent_) {
+ parent->add(daemon);
+ }
+};
+
+QpiddOptions::QpiddOptions(const char* argv0)
+ : qpid::Options("Options"),
+ common("", QPIDD_CONF_FILE),
+ module(QPIDD_MODULE_DIR),
+ log(argv0)
+{
+ add(common);
+ add(module);
+ add(broker);
+ add(log);
+
+ platform.reset(new QpiddPosixOptions(this));
+ qpid::Plugin::addOptions(*this);
+}
+
+void QpiddOptions::usage() const {
+ cout << "Usage: qpidd [OPTIONS]" << endl << endl << *this << endl;
+}
+
+// Set the broker pointer on the signal handler, then reset at end of scope.
+// This is to ensure that the signal handler doesn't keep a broker
+// reference after main() has returned.
+//
+struct ScopedSetBroker {
+ ScopedSetBroker(const boost::intrusive_ptr<Broker>& broker) {
+ qpid::broker::SignalHandler::setBroker(broker.get());
+ }
+ ~ScopedSetBroker() { qpid::broker::SignalHandler::setBroker(0); }
+};
+
+struct QpiddDaemon : public Daemon {
+ QpiddPosixOptions *options;
+
+ QpiddDaemon(std::string pidDir, QpiddPosixOptions *opts)
+ : Daemon(pidDir), options(opts) {}
+
+ /** Code for parent process */
+ void parent() {
+ uint16_t port = wait(options->daemon.wait);
+ if (options->parent->broker.port == 0 || options->daemon.transport != TCP)
+ cout << port << endl;
+ }
+
+ /** Code for forked child process */
+ void child() {
+ boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->parent->broker));
+ ScopedSetBroker ssb(brokerPtr);
+ brokerPtr->accept();
+ uint16_t port=brokerPtr->getPort(options->daemon.transport);
+ ready(port); // Notify parent.
+ brokerPtr->run();
+ }
+};
+
+int QpiddBroker::execute (QpiddOptions *options) {
+ // Options that affect a running daemon.
+ QpiddPosixOptions *myOptions =
+ static_cast<QpiddPosixOptions *>(options->platform.get());
+ if (myOptions == 0)
+ throw Exception("Internal error obtaining platform options");
+
+ if (myOptions->daemon.check || myOptions->daemon.quit) {
+ pid_t pid = Daemon::getPid(myOptions->daemon.piddir,
+ options->broker.port);
+ if (pid < 0)
+ return 1;
+ if (myOptions->daemon.check)
+ cout << pid << endl;
+ if (myOptions->daemon.quit) {
+ if (kill(pid, SIGINT) < 0)
+ throw Exception("Failed to stop daemon: " + qpid::sys::strError(errno));
+ // Wait for the process to die before returning
+ int retry=10000; // Try up to 10 seconds
+ while (kill(pid,0) == 0 && --retry)
+ sys::usleep(1000);
+ if (retry == 0)
+ throw Exception("Gave up waiting for daemon process to exit");
+ }
+ return 0;
+ }
+
+ // Starting the broker.
+ if (myOptions->daemon.daemon) {
+ // For daemon mode replace default stderr with syslog.
+ options->log.sinkOptions->detached();
+ qpid::log::Logger::instance().configure(options->log);
+ // Fork the daemon
+ QpiddDaemon d(myOptions->daemon.piddir, myOptions);
+ d.fork(); // Broker is stared in QpiddDaemon::child()
+ }
+ else { // Non-daemon broker.
+ boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker));
+ ScopedSetBroker ssb(brokerPtr);
+ brokerPtr->accept();
+ if (options->broker.port == 0 || myOptions->daemon.transport != TCP)
+ cout << uint16_t(brokerPtr->getPort(myOptions->daemon.transport)) << endl;
+ brokerPtr->run();
+ }
+ return 0;
+}
diff --git a/qpid/cpp/src/prof b/qpid/cpp/src/prof
new file mode 100755
index 0000000000..acfbaff2d4
--- /dev/null
+++ b/qpid/cpp/src/prof
@@ -0,0 +1,39 @@
+#!/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.
+#
+#
+
+
+rm /var/lib/oprofile/oprofiled.log
+
+opcontrol --reset
+opcontrol --setup --no-vmlinux --separate=library
+opcontrol --start
+# -- Do stuff here --
+./qpidd
+# -- End of stuff --
+opcontrol --stop
+opcontrol --dump
+opcontrol --shutdown
+opreport -l ./.libs/lt-qpidd > stats.txt
+opannotate --source --output-dir=qpidd-prof ./.libs/lt-qpidd
+
+# clear the relusts
+#opcontrol --reset
diff --git a/qpid/cpp/src/qmf.mk b/qpid/cpp/src/qmf.mk
new file mode 100644
index 0000000000..f3462f1a93
--- /dev/null
+++ b/qpid/cpp/src/qmf.mk
@@ -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.
+#
+
+#
+# qmf library makefile fragment, to be included in Makefile.am
+#
+lib_LTLIBRARIES += \
+ libqmf.la \
+ libqmfengine.la \
+ libqmf2.la
+
+#
+# Public headers for the QMF API
+#
+QMF_API = \
+ ../include/qpid/agent/ManagementAgent.h \
+ ../include/qpid/agent/QmfAgentImportExport.h
+
+#
+# Public headers for the QMF2 API
+#
+QMF2_API = \
+ ../include/qmf/AgentEvent.h \
+ ../include/qmf/Agent.h \
+ ../include/qmf/AgentSession.h \
+ ../include/qmf/ConsoleEvent.h \
+ ../include/qmf/ConsoleSession.h \
+ ../include/qmf/DataAddr.h \
+ ../include/qmf/Data.h \
+ ../include/qmf/exceptions.h \
+ ../include/qmf/Handle.h \
+ ../include/qmf/ImportExport.h \
+ ../include/qmf/Query.h \
+ ../include/qmf/Schema.h \
+ ../include/qmf/SchemaId.h \
+ ../include/qmf/SchemaMethod.h \
+ ../include/qmf/SchemaProperty.h \
+ ../include/qmf/SchemaTypes.h \
+ ../include/qmf/Subscription.h
+
+
+#
+# Public headers for the QMF Engine API
+#
+QMF_ENGINE_API = \
+ ../include/qmf/engine/Agent.h \
+ ../include/qmf/engine/ConnectionSettings.h \
+ ../include/qmf/engine/Console.h \
+ ../include/qmf/engine/Event.h \
+ ../include/qmf/engine/Message.h \
+ ../include/qmf/engine/Object.h \
+ ../include/qmf/engine/ObjectId.h \
+ ../include/qmf/engine/QmfEngineImportExport.h \
+ ../include/qmf/engine/Query.h \
+ ../include/qmf/engine/ResilientConnection.h \
+ ../include/qmf/engine/Schema.h \
+ ../include/qmf/engine/Typecode.h \
+ ../include/qmf/engine/Value.h
+
+# Public header files
+nobase_include_HEADERS += \
+ $(QMF_API) \
+ $(QMF_ENGINE_API) \
+ $(QMF2_API)
+
+libqmf_la_SOURCES = \
+ $(QMF_API) \
+ qpid/agent/ManagementAgentImpl.cpp \
+ qpid/agent/ManagementAgentImpl.h
+
+libqmf2_la_SOURCES = \
+ $(QMF2_API) \
+ qmf/agentCapability.h \
+ qmf/Agent.cpp \
+ qmf/AgentEvent.cpp \
+ qmf/AgentEventImpl.h \
+ qmf/AgentImpl.h \
+ qmf/AgentSession.cpp \
+ qmf/AgentSubscription.cpp \
+ qmf/AgentSubscription.h \
+ qmf/ConsoleEvent.cpp \
+ qmf/ConsoleEventImpl.h \
+ qmf/ConsoleSession.cpp \
+ qmf/ConsoleSessionImpl.h \
+ qmf/constants.cpp \
+ qmf/constants.h \
+ qmf/DataAddr.cpp \
+ qmf/DataAddrImpl.h \
+ qmf/Data.cpp \
+ qmf/DataImpl.h \
+ qmf/exceptions.cpp \
+ qmf/Expression.cpp \
+ qmf/Expression.h \
+ qmf/Hash.cpp \
+ qmf/Hash.h \
+ qmf/PrivateImplRef.h \
+ qmf/Query.cpp \
+ qmf/QueryImpl.h \
+ qmf/Schema.cpp \
+ qmf/SchemaCache.cpp \
+ qmf/SchemaCache.h \
+ qmf/SchemaId.cpp \
+ qmf/SchemaIdImpl.h \
+ qmf/SchemaImpl.h \
+ qmf/SchemaMethod.cpp \
+ qmf/SchemaMethodImpl.h \
+ qmf/SchemaProperty.cpp \
+ qmf/SchemaPropertyImpl.h \
+ qmf/Subscription.cpp \
+ qmf/SubscriptionImpl.h
+
+libqmfengine_la_SOURCES = \
+ $(QMF_ENGINE_API) \
+ qmf/engine/Agent.cpp \
+ qmf/engine/BrokerProxyImpl.cpp \
+ qmf/engine/BrokerProxyImpl.h \
+ qmf/engine/ConnectionSettingsImpl.cpp \
+ qmf/engine/ConnectionSettingsImpl.h \
+ qmf/engine/ConsoleImpl.cpp \
+ qmf/engine/ConsoleImpl.h \
+ qmf/engine/EventImpl.cpp \
+ qmf/engine/EventImpl.h \
+ qmf/engine/MessageImpl.cpp \
+ qmf/engine/MessageImpl.h \
+ qmf/engine/ObjectIdImpl.cpp \
+ qmf/engine/ObjectIdImpl.h \
+ qmf/engine/ObjectImpl.cpp \
+ qmf/engine/ObjectImpl.h \
+ qmf/engine/Protocol.cpp \
+ qmf/engine/Protocol.h \
+ qmf/engine/QueryImpl.cpp \
+ qmf/engine/QueryImpl.h \
+ qmf/engine/ResilientConnection.cpp \
+ qmf/engine/SequenceManager.cpp \
+ qmf/engine/SequenceManager.h \
+ qmf/engine/SchemaImpl.cpp \
+ qmf/engine/SchemaImpl.h \
+ qmf/engine/ValueImpl.cpp \
+ qmf/engine/ValueImpl.h
+
+libqmf_la_LIBADD = libqmfengine.la
+libqmf2_la_LIBADD = libqpidmessaging.la libqpidtypes.la
+libqmfengine_la_LIBADD = libqpidclient.la
+
+QMF_VERSION_INFO = 1:0:0
+QMF2_VERSION_INFO = 1:0:0
+QMFENGINE_VERSION_INFO = 1:1:0
+
+libqmf_la_LDFLAGS = -version-info $(QMF_VERSION_INFO)
+libqmf2_la_LDFLAGS = -version-info $(QMF2_VERSION_INFO)
+libqmfengine_la_LDFLAGS = -version-info $(QMFENGINE_VERSION_INFO)
diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp
new file mode 100644
index 0000000000..915f2a1c88
--- /dev/null
+++ b/qpid/cpp/src/qmf/Agent.cpp
@@ -0,0 +1,657 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/AgentImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/ConsoleEventImpl.h"
+#include "qmf/ConsoleSession.h"
+#include "qmf/DataImpl.h"
+#include "qmf/Query.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/agentCapability.h"
+#include "qmf/constants.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/management/Buffer.h"
+#include "qpid/log/Statement.h"
+#include <boost/lexical_cast.hpp>
+
+using qpid::types::Variant;
+using qpid::messaging::Duration;
+using qpid::messaging::Message;
+using qpid::messaging::Sender;
+using namespace std;
+using namespace qmf;
+
+typedef PrivateImplRef<Agent> PI;
+
+Agent::Agent(AgentImpl* impl) { PI::ctor(*this, impl); }
+Agent::Agent(const Agent& s) : qmf::Handle<AgentImpl>() { PI::copy(*this, s); }
+Agent::~Agent() { PI::dtor(*this); }
+Agent& Agent::operator=(const Agent& s) { return PI::assign(*this, s); }
+string Agent::getName() const { return isValid() ? impl->getName() : ""; }
+uint32_t Agent::getEpoch() const { return isValid() ? impl->getEpoch() : 0; }
+string Agent::getVendor() const { return isValid() ? impl->getVendor() : ""; }
+string Agent::getProduct() const { return isValid() ? impl->getProduct() : ""; }
+string Agent::getInstance() const { return isValid() ? impl->getInstance() : ""; }
+const Variant& Agent::getAttribute(const string& k) const { return impl->getAttribute(k); }
+const Variant::Map& Agent::getAttributes() const { return impl->getAttributes(); }
+ConsoleEvent Agent::querySchema(Duration t) { return impl->querySchema(t); }
+uint32_t Agent::querySchemaAsync() { return impl->querySchemaAsync(); }
+ConsoleEvent Agent::query(const Query& q, Duration t) { return impl->query(q, t); }
+ConsoleEvent Agent::query(const string& q, Duration t) { return impl->query(q, t); }
+uint32_t Agent::queryAsync(const Query& q) { return impl->queryAsync(q); }
+uint32_t Agent::queryAsync(const string& q) { return impl->queryAsync(q); }
+ConsoleEvent Agent::callMethod(const string& m, const Variant::Map& a, const DataAddr& d, Duration t) { return impl->callMethod(m, a, d, t); }
+uint32_t Agent::callMethodAsync(const string& m, const Variant::Map& a, const DataAddr& d) { return impl->callMethodAsync(m, a, d); }
+uint32_t Agent::getPackageCount() const { return impl->getPackageCount(); }
+const string& Agent::getPackage(uint32_t i) const { return impl->getPackage(i); }
+uint32_t Agent::getSchemaIdCount(const string& p) const { return impl->getSchemaIdCount(p); }
+SchemaId Agent::getSchemaId(const string& p, uint32_t i) const { return impl->getSchemaId(p, i); }
+Schema Agent::getSchema(const SchemaId& s, Duration t) { return impl->getSchema(s, t); }
+
+
+
+AgentImpl::AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s) :
+ name(n), directSubject(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0),
+ sender(session.directSender), nextCorrelator(1), schemaCache(s.schemaCache)
+{
+}
+
+void AgentImpl::setAttribute(const std::string& k, const qpid::types::Variant& v)
+{
+ attributes[k] = v;
+ if (k == "qmf.agent_capability")
+ try {
+ capability = v.asUint32();
+ } catch (std::exception&) {}
+ if (k == "_direct_subject")
+ try {
+ directSubject = v.asString();
+ sender = session.topicSender;
+ } catch (std::exception&) {}
+}
+
+const Variant& AgentImpl::getAttribute(const string& k) const
+{
+ Variant::Map::const_iterator iter = attributes.find(k);
+ if (iter == attributes.end())
+ throw KeyNotFound(k);
+ return iter->second;
+}
+
+
+ConsoleEvent AgentImpl::query(const Query& query, Duration timeout)
+{
+ boost::shared_ptr<SyncContext> context(new SyncContext());
+ uint32_t correlator;
+ ConsoleEvent result;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ correlator = nextCorrelator++;
+ contextMap[correlator] = context;
+ }
+ try {
+ sendQuery(query, correlator);
+ {
+ uint64_t milliseconds = timeout.getMilliseconds();
+ qpid::sys::Mutex::ScopedLock cl(context->lock);
+ if (!context->response.isValid() || !context->response.isFinal())
+ context->cond.wait(context->lock,
+ qpid::sys::AbsTime(qpid::sys::now(),
+ qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+ if (context->response.isValid() &&
+ ((context->response.getType() == CONSOLE_QUERY_RESPONSE && context->response.isFinal()) ||
+ (context->response.getType() == CONSOLE_EXCEPTION)))
+ result = context->response;
+ else {
+ auto_ptr<ConsoleEventImpl> impl(new ConsoleEventImpl(CONSOLE_EXCEPTION));
+ Data exception(new DataImpl());
+ exception.setProperty("error_text", "Timed out waiting for the agent to respond");
+ impl->addData(exception);
+ result = ConsoleEvent(impl.release());
+ }
+ }
+ } catch (qpid::types::Exception&) {
+ }
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ contextMap.erase(correlator);
+ }
+
+ return result;
+}
+
+
+ConsoleEvent AgentImpl::query(const string& text, Duration timeout)
+{
+ return query(stringToQuery(text), timeout);
+}
+
+
+uint32_t AgentImpl::queryAsync(const Query& query)
+{
+ uint32_t correlator;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ correlator = nextCorrelator++;
+ }
+
+ sendQuery(query, correlator);
+ return correlator;
+}
+
+
+uint32_t AgentImpl::queryAsync(const string& text)
+{
+ return queryAsync(stringToQuery(text));
+}
+
+
+ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& args, const DataAddr& addr, Duration timeout)
+{
+ boost::shared_ptr<SyncContext> context(new SyncContext());
+ uint32_t correlator;
+ ConsoleEvent result;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ correlator = nextCorrelator++;
+ contextMap[correlator] = context;
+ }
+ try {
+ sendMethod(method, args, addr, correlator);
+ {
+ uint64_t milliseconds = timeout.getMilliseconds();
+ qpid::sys::Mutex::ScopedLock cl(context->lock);
+ if (!context->response.isValid())
+ context->cond.wait(context->lock,
+ qpid::sys::AbsTime(qpid::sys::now(),
+ qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+ if (context->response.isValid())
+ result = context->response;
+ else {
+ auto_ptr<ConsoleEventImpl> impl(new ConsoleEventImpl(CONSOLE_EXCEPTION));
+ Data exception(new DataImpl());
+ exception.setProperty("error_text", "Timed out waiting for the agent to respond");
+ impl->addData(exception);
+ result = ConsoleEvent(impl.release());
+ }
+ }
+ } catch (qpid::types::Exception&) {
+ }
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ contextMap.erase(correlator);
+ }
+
+ return result;
+}
+
+
+uint32_t AgentImpl::callMethodAsync(const string& method, const Variant::Map& args, const DataAddr& addr)
+{
+ uint32_t correlator;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ correlator = nextCorrelator++;
+ }
+
+ sendMethod(method, args, addr, correlator);
+ return correlator;
+}
+
+
+uint32_t AgentImpl::getPackageCount() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ //
+ // Populate the package set.
+ //
+ for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++)
+ packageSet.insert(iter->getPackageName());
+
+ return packageSet.size();
+}
+
+
+const string& AgentImpl::getPackage(uint32_t idx) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ uint32_t count(0);
+ for (set<string>::const_iterator iter = packageSet.begin(); iter != packageSet.end(); iter++) {
+ if (idx == count)
+ return *iter;
+ count++;
+ }
+ throw IndexOutOfRange();
+}
+
+
+uint32_t AgentImpl::getSchemaIdCount(const string& pname) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ uint32_t count(0);
+ for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++)
+ if (iter->getPackageName() == pname)
+ count++;
+ return count;
+}
+
+
+SchemaId AgentImpl::getSchemaId(const string& pname, uint32_t idx) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ uint32_t count(0);
+ for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++) {
+ if (iter->getPackageName() == pname) {
+ if (idx == count)
+ return *iter;
+ count++;
+ }
+ }
+ throw IndexOutOfRange();
+}
+
+
+Schema AgentImpl::getSchema(const SchemaId& id, Duration timeout)
+{
+ if (!schemaCache->haveSchema(id))
+ //
+ // The desired schema is not in the cache. We need to asynchronously query the remote
+ // agent for the information. The call to schemaCache->getSchema will block waiting for
+ // the response to be received.
+ //
+ sendSchemaRequest(id);
+
+ return schemaCache->getSchema(id, timeout);
+}
+
+
+void AgentImpl::handleException(const Variant::Map& content, const Message& msg)
+{
+ const string& cid(msg.getCorrelationId());
+ Variant::Map::const_iterator aIter;
+ uint32_t correlator;
+ boost::shared_ptr<SyncContext> context;
+
+ try { correlator = boost::lexical_cast<uint32_t>(cid); }
+ catch(const boost::bad_lexical_cast&) { correlator = 0; }
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator);
+ if (iter != contextMap.end())
+ context = iter->second;
+ }
+
+ if (context.get() != 0) {
+ //
+ // This exception is associated with a synchronous request.
+ //
+ qpid::sys::Mutex::ScopedLock cl(context->lock);
+ context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_EXCEPTION));
+ ConsoleEventImplAccess::get(context->response).addData(new DataImpl(content, this));
+ ConsoleEventImplAccess::get(context->response).setAgent(this);
+ context->cond.notify();
+ } else {
+ //
+ // This exception is associated with an asynchronous request.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_EXCEPTION));
+ eventImpl->setCorrelator(correlator);
+ eventImpl->setAgent(this);
+ eventImpl->addData(new DataImpl(content, this));
+ session.enqueueEvent(eventImpl.release());
+ }
+}
+
+
+void AgentImpl::handleMethodResponse(const Variant::Map& response, const Message& msg)
+{
+ const string& cid(msg.getCorrelationId());
+ Variant::Map::const_iterator aIter;
+ Variant::Map argMap;
+ uint32_t correlator;
+ boost::shared_ptr<SyncContext> context;
+
+ QPID_LOG(trace, "RCVD MethodResponse cid=" << cid << " map=" << response);
+
+ aIter = response.find("_arguments");
+ if (aIter != response.end())
+ argMap = aIter->second.asMap();
+
+ try { correlator = boost::lexical_cast<uint32_t>(cid); }
+ catch(const boost::bad_lexical_cast&) { correlator = 0; }
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator);
+ if (iter != contextMap.end())
+ context = iter->second;
+ }
+
+ if (context.get() != 0) {
+ //
+ // This response is associated with a synchronous request.
+ //
+ qpid::sys::Mutex::ScopedLock cl(context->lock);
+ context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_METHOD_RESPONSE));
+ ConsoleEventImplAccess::get(context->response).setArguments(argMap);
+ ConsoleEventImplAccess::get(context->response).setAgent(this);
+ context->cond.notify();
+ } else {
+ //
+ // This response is associated with an asynchronous request.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_METHOD_RESPONSE));
+ eventImpl->setCorrelator(correlator);
+ eventImpl->setAgent(this);
+ eventImpl->setArguments(argMap);
+ session.enqueueEvent(eventImpl.release());
+ }
+}
+
+
+void AgentImpl::handleDataIndication(const Variant::List& list, const Message& msg)
+{
+ Variant::Map::const_iterator aIter;
+ const Variant::Map& props(msg.getProperties());
+ boost::shared_ptr<SyncContext> context;
+
+ aIter = props.find("qmf.content");
+ if (aIter == props.end())
+ return;
+
+ string content_type(aIter->second.asString());
+ if (content_type != "_event")
+ return;
+
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ const Variant::Map& eventMap(lIter->asMap());
+ Data data(new DataImpl(eventMap, this));
+ int severity(SEV_NOTICE);
+ uint64_t timestamp(0);
+
+ aIter = eventMap.find("_severity");
+ if (aIter != eventMap.end())
+ severity = int(aIter->second.asInt8());
+
+ aIter = eventMap.find("_timestamp");
+ if (aIter != eventMap.end())
+ timestamp = aIter->second.asUint64();
+
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_EVENT));
+ eventImpl->setAgent(this);
+ eventImpl->addData(data);
+ eventImpl->setSeverity(severity);
+ eventImpl->setTimestamp(timestamp);
+ if (data.hasSchema())
+ learnSchemaId(data.getSchemaId());
+ session.enqueueEvent(eventImpl.release());
+ }
+}
+
+
+void AgentImpl::handleQueryResponse(const Variant::List& list, const Message& msg)
+{
+ const string& cid(msg.getCorrelationId());
+ Variant::Map::const_iterator aIter;
+ const Variant::Map& props(msg.getProperties());
+ uint32_t correlator;
+ bool final(false);
+ boost::shared_ptr<SyncContext> context;
+
+ aIter = props.find("partial");
+ if (aIter == props.end())
+ final = true;
+
+ aIter = props.find("qmf.content");
+ if (aIter == props.end())
+ return;
+
+ string content_type(aIter->second.asString());
+ if (content_type != "_schema" && content_type != "_schema_id" && content_type != "_data")
+ return;
+
+ try { correlator = boost::lexical_cast<uint32_t>(cid); }
+ catch(const boost::bad_lexical_cast&) { correlator = 0; }
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator);
+ if (iter != contextMap.end())
+ context = iter->second;
+ }
+
+ if (context.get() != 0) {
+ //
+ // This response is associated with a synchronous request.
+ //
+ qpid::sys::Mutex::ScopedLock cl(context->lock);
+ if (!context->response.isValid())
+ context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_QUERY_RESPONSE));
+
+ if (content_type == "_data")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ Data data(new DataImpl(lIter->asMap(), this));
+ ConsoleEventImplAccess::get(context->response).addData(data);
+ if (data.hasSchema())
+ learnSchemaId(data.getSchemaId());
+ }
+ else if (content_type == "_schema_id")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ SchemaId schemaId(new SchemaIdImpl(lIter->asMap()));
+ ConsoleEventImplAccess::get(context->response).addSchemaId(schemaId);
+ learnSchemaId(schemaId);
+ }
+ else if (content_type == "_schema")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ Schema schema(new SchemaImpl(lIter->asMap()));
+ schemaCache->declareSchema(schema);
+ }
+
+ if (final) {
+ ConsoleEventImplAccess::get(context->response).setFinal();
+ ConsoleEventImplAccess::get(context->response).setAgent(this);
+ context->cond.notify();
+ }
+ } else {
+ //
+ // This response is associated with an asynchronous request.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_QUERY_RESPONSE));
+ eventImpl->setCorrelator(correlator);
+ eventImpl->setAgent(this);
+
+ if (content_type == "_data")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ Data data(new DataImpl(lIter->asMap(), this));
+ eventImpl->addData(data);
+ if (data.hasSchema())
+ learnSchemaId(data.getSchemaId());
+ }
+ else if (content_type == "_schema_id")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ SchemaId schemaId(new SchemaIdImpl(lIter->asMap()));
+ eventImpl->addSchemaId(schemaId);
+ learnSchemaId(schemaId);
+ }
+ else if (content_type == "_schema")
+ for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) {
+ Schema schema(new SchemaImpl(lIter->asMap()));
+ schemaCache->declareSchema(schema);
+ }
+
+ if (final)
+ eventImpl->setFinal();
+ if (content_type != "_schema")
+ session.enqueueEvent(eventImpl.release());
+ }
+}
+
+
+Query AgentImpl::stringToQuery(const std::string& text)
+{
+ qpid::messaging::AddressParser parser(text);
+ Variant::Map map;
+ Variant::Map::const_iterator iter;
+ string className;
+ string packageName;
+
+ parser.parseMap(map);
+
+ iter = map.find("class");
+ if (iter != map.end())
+ className = iter->second.asString();
+
+ iter = map.find("package");
+ if (iter != map.end())
+ packageName = iter->second.asString();
+
+ Query query(QUERY_OBJECT, className, packageName);
+
+ iter = map.find("where");
+ if (iter != map.end())
+ query.setPredicate(iter->second.asList());
+
+ return query;
+}
+
+
+void AgentImpl::sendQuery(const Query& query, uint32_t correlator)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_REQUEST;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ msg.setReplyTo(session.replyAddress);
+ msg.setCorrelationId(boost::lexical_cast<string>(correlator));
+ msg.setSubject(directSubject);
+ string userId(session.connection.getAuthenticatedUsername());
+ if (!userId.empty())
+ msg.setUserId(userId);
+ encode(QueryImplAccess::get(query).asMap(), msg);
+ if (sender.isValid()) {
+ sender.send(msg);
+ QPID_LOG(trace, "SENT QueryRequest to=" << sender.getName() << "/" << directSubject << " cid=" << correlator);
+ }
+}
+
+
+void AgentImpl::sendMethod(const string& method, const Variant::Map& args, const DataAddr& addr, uint32_t correlator)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_REQUEST;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ map["_method_name"] = method;
+ map["_object_id"] = addr.asMap();
+ map["_arguments"] = args;
+
+ msg.setReplyTo(session.replyAddress);
+ msg.setCorrelationId(boost::lexical_cast<string>(correlator));
+ msg.setSubject(directSubject);
+ string userId(session.connection.getAuthenticatedUsername());
+ if (!userId.empty())
+ msg.setUserId(userId);
+ encode(map, msg);
+ if (sender.isValid()) {
+ sender.send(msg);
+ QPID_LOG(trace, "SENT MethodRequest method=" << method << " to=" << sender.getName() << "/" << directSubject << " content=" << map << " cid=" << correlator);
+ }
+}
+
+void AgentImpl::sendSchemaRequest(const SchemaId& id)
+{
+ uint32_t correlator;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ correlator = nextCorrelator++;
+ }
+
+ if (capability >= AGENT_CAPABILITY_V2_SCHEMA) {
+ Query query(QUERY_SCHEMA, id);
+ sendQuery(query, correlator);
+ return;
+ }
+
+#define RAW_BUFFER_SIZE 1024
+ char rawBuffer[RAW_BUFFER_SIZE];
+ qpid::management::Buffer buffer(rawBuffer, RAW_BUFFER_SIZE);
+
+ buffer.putOctet('A');
+ buffer.putOctet('M');
+ buffer.putOctet('2');
+ buffer.putOctet('S');
+ buffer.putLong(correlator);
+ buffer.putShortString(id.getPackageName());
+ buffer.putShortString(id.getName());
+ buffer.putBin128(id.getHash().data());
+
+ string content(rawBuffer, buffer.getPosition());
+
+ Message msg;
+ msg.setReplyTo(session.replyAddress);
+ msg.setContent(content);
+ msg.setSubject(directSubject);
+ string userId(session.connection.getAuthenticatedUsername());
+ if (!userId.empty())
+ msg.setUserId(userId);
+ if (sender.isValid()) {
+ sender.send(msg);
+ QPID_LOG(trace, "SENT V1SchemaRequest to=" << sender.getName() << "/" << directSubject);
+ }
+}
+
+
+void AgentImpl::learnSchemaId(const SchemaId& id)
+{
+ schemaCache->declareSchemaId(id);
+ schemaIdSet.insert(id);
+}
+
+
+AgentImpl& AgentImplAccess::get(Agent& item)
+{
+ return *item.impl;
+}
+
+
+const AgentImpl& AgentImplAccess::get(const Agent& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/AgentEvent.cpp b/qpid/cpp/src/qmf/AgentEvent.cpp
new file mode 100644
index 0000000000..2dc24ecac1
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentEvent.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/AgentEventImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/SchemaImpl.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<AgentEvent> PI;
+
+AgentEvent::AgentEvent(AgentEventImpl* impl) { PI::ctor(*this, impl); }
+AgentEvent::AgentEvent(const AgentEvent& s) : qmf::Handle<AgentEventImpl>() { PI::copy(*this, s); }
+AgentEvent::~AgentEvent() { PI::dtor(*this); }
+AgentEvent& AgentEvent::operator=(const AgentEvent& s) { return PI::assign(*this, s); }
+
+AgentEventCode AgentEvent::getType() const { return impl->getType(); }
+const string& AgentEvent::getUserId() const { return impl->getUserId(); }
+Query AgentEvent::getQuery() const { return impl->getQuery(); }
+bool AgentEvent::hasDataAddr() const { return impl->hasDataAddr(); }
+DataAddr AgentEvent::getDataAddr() const { return impl->getDataAddr(); }
+const string& AgentEvent::getMethodName() const { return impl->getMethodName(); }
+qpid::types::Variant::Map& AgentEvent::getArguments() { return impl->getArguments(); }
+qpid::types::Variant::Map& AgentEvent::getArgumentSubtypes() { return impl->getArgumentSubtypes(); }
+void AgentEvent::addReturnArgument(const std::string& k, const qpid::types::Variant& v, const std::string& s) { impl->addReturnArgument(k, v, s); }
+
+uint32_t AgentEventImpl::enqueueData(const Data& data)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ dataQueue.push(data);
+ return dataQueue.size();
+}
+
+
+Data AgentEventImpl::dequeueData()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (dataQueue.empty())
+ return Data();
+ Data data(dataQueue.front());
+ dataQueue.pop();
+ return data;
+}
+
+
+void AgentEventImpl::addReturnArgument(const string& key, const Variant& val, const string& subtype)
+{
+ if (schema.isValid() && !SchemaImplAccess::get(schema).isValidMethodOutArg(methodName, key, val))
+ throw QmfException("Output argument is unknown or the type is incompatible");
+ outArguments[key] = val;
+ if (!subtype.empty())
+ outArgumentSubtypes[key] = subtype;
+}
+
+
+AgentEventImpl& AgentEventImplAccess::get(AgentEvent& item)
+{
+ return *item.impl;
+}
+
+
+const AgentEventImpl& AgentEventImplAccess::get(const AgentEvent& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/AgentEventImpl.h b/qpid/cpp/src/qmf/AgentEventImpl.h
new file mode 100644
index 0000000000..1ecb41775a
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentEventImpl.h
@@ -0,0 +1,96 @@
+#ifndef _QMF_AGENT_EVENT_IMPL_H_
+#define _QMF_AGENT_EVENT_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/messaging/Address.h"
+#include "qmf/AgentEvent.h"
+#include "qmf/Query.h"
+#include "qmf/DataAddr.h"
+#include "qmf/Data.h"
+#include "qmf/Schema.h"
+#include <queue>
+
+namespace qmf {
+ class AgentEventImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Impl-only methods
+ //
+ AgentEventImpl(AgentEventCode e) : eventType(e) {}
+ void setUserId(const std::string& u) { userId = u; }
+ void setQuery(const Query& q) { query = q; }
+ void setDataAddr(const DataAddr& d) { dataAddr = d; }
+ void setMethodName(const std::string& m) { methodName = m; }
+ void setArguments(const qpid::types::Variant::Map& a) { arguments = a; }
+ void setArgumentSubtypes(const qpid::types::Variant::Map& a) { argumentSubtypes = a; }
+ void setReplyTo(const qpid::messaging::Address& r) { replyTo = r; }
+ void setSchema(const Schema& s) { schema = s; }
+ const qpid::messaging::Address& getReplyTo() { return replyTo; }
+ void setCorrelationId(const std::string& c) { correlationId = c; }
+ const std::string& getCorrelationId() { return correlationId; }
+ const qpid::types::Variant::Map& getReturnArguments() const { return outArguments; }
+ const qpid::types::Variant::Map& getReturnArgumentSubtypes() const { return outArgumentSubtypes; }
+ uint32_t enqueueData(const Data&);
+ Data dequeueData();
+
+ //
+ // Methods from API handle
+ //
+ AgentEventCode getType() const { return eventType; }
+ const std::string& getUserId() const { return userId; }
+ Query getQuery() const { return query; }
+ bool hasDataAddr() const { return dataAddr.isValid(); }
+ DataAddr getDataAddr() const { return dataAddr; }
+ const std::string& getMethodName() const { return methodName; }
+ qpid::types::Variant::Map& getArguments() { return arguments; }
+ qpid::types::Variant::Map& getArgumentSubtypes() { return argumentSubtypes; }
+ void addReturnArgument(const std::string&, const qpid::types::Variant&, const std::string&);
+
+ private:
+ const AgentEventCode eventType;
+ std::string userId;
+ qpid::messaging::Address replyTo;
+ std::string correlationId;
+ Query query;
+ DataAddr dataAddr;
+ Schema schema;
+ std::string methodName;
+ qpid::types::Variant::Map arguments;
+ qpid::types::Variant::Map argumentSubtypes;
+ qpid::types::Variant::Map outArguments;
+ qpid::types::Variant::Map outArgumentSubtypes;
+
+ qpid::sys::Mutex lock;
+ std::queue<Data> dataQueue;
+ };
+
+ struct AgentEventImplAccess
+ {
+ static AgentEventImpl& get(AgentEvent&);
+ static const AgentEventImpl& get(const AgentEvent&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/AgentImpl.h b/qpid/cpp/src/qmf/AgentImpl.h
new file mode 100644
index 0000000000..7fa4f4373a
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentImpl.h
@@ -0,0 +1,123 @@
+#ifndef _QMF_AGENT_IMPL_H_
+#define _QMF_AGENT_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/Agent.h"
+#include "qmf/ConsoleEventImpl.h"
+#include "qmf/ConsoleSessionImpl.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/SchemaCache.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Condition.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <set>
+
+namespace qmf {
+ class AgentImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Impl-only methods
+ //
+ AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s);
+ void setAttribute(const std::string& k, const qpid::types::Variant& v);
+ void setAttribute(const std::string& k, const std::string& v) { attributes[k] = v; }
+ void touch() { touched = true; }
+ uint32_t age() { untouchedCount = touched ? 0 : untouchedCount + 1; touched = false; return untouchedCount; }
+ uint32_t getCapability() const { return capability; }
+ void handleException(const qpid::types::Variant::Map&, const qpid::messaging::Message&);
+ void handleMethodResponse(const qpid::types::Variant::Map&, const qpid::messaging::Message&);
+ void handleDataIndication(const qpid::types::Variant::List&, const qpid::messaging::Message&);
+ void handleQueryResponse(const qpid::types::Variant::List&, const qpid::messaging::Message&);
+
+ //
+ // Methods from API handle
+ //
+ const std::string& getName() const { return name; }
+ uint32_t getEpoch() const { return epoch; }
+ void setEpoch(uint32_t e) { epoch = e; }
+ std::string getVendor() const { return getAttribute("_vendor").asString(); }
+ std::string getProduct() const { return getAttribute("_product").asString(); }
+ std::string getInstance() const { return getAttribute("_instance").asString(); }
+ const qpid::types::Variant& getAttribute(const std::string& k) const;
+ const qpid::types::Variant::Map& getAttributes() const { return attributes; }
+
+ ConsoleEvent querySchema(qpid::messaging::Duration t) { return query(Query(QUERY_SCHEMA_ID), t); }
+ uint32_t querySchemaAsync() { return queryAsync(Query(QUERY_SCHEMA_ID)); }
+
+ ConsoleEvent query(const Query& q, qpid::messaging::Duration t);
+ ConsoleEvent query(const std::string& q, qpid::messaging::Duration t);
+ uint32_t queryAsync(const Query& q);
+ uint32_t queryAsync(const std::string& q);
+
+ ConsoleEvent callMethod(const std::string& m, const qpid::types::Variant::Map& a, const DataAddr&, qpid::messaging::Duration t);
+ uint32_t callMethodAsync(const std::string& m, const qpid::types::Variant::Map& a, const DataAddr&);
+
+ uint32_t getPackageCount() const;
+ const std::string& getPackage(uint32_t i) const;
+ uint32_t getSchemaIdCount(const std::string& p) const;
+ SchemaId getSchemaId(const std::string& p, uint32_t i) const;
+ Schema getSchema(const SchemaId& s, qpid::messaging::Duration t);
+
+ private:
+ struct SyncContext {
+ qpid::sys::Mutex lock;
+ qpid::sys::Condition cond;
+ ConsoleEvent response;
+ };
+
+ mutable qpid::sys::Mutex lock;
+ std::string name;
+ std::string directSubject;
+ uint32_t epoch;
+ ConsoleSessionImpl& session;
+ bool touched;
+ uint32_t untouchedCount;
+ uint32_t capability;
+ qpid::messaging::Sender sender;
+ qpid::types::Variant::Map attributes;
+ uint32_t nextCorrelator;
+ std::map<uint32_t, boost::shared_ptr<SyncContext> > contextMap;
+ boost::shared_ptr<SchemaCache> schemaCache;
+ mutable std::set<std::string> packageSet;
+ std::set<SchemaId, SchemaIdCompare> schemaIdSet;
+
+ Query stringToQuery(const std::string&);
+ void sendQuery(const Query&, uint32_t);
+ void sendSchemaIdQuery(uint32_t);
+ void sendMethod(const std::string&, const qpid::types::Variant::Map&, const DataAddr&, uint32_t);
+ void sendSchemaRequest(const SchemaId&);
+ void learnSchemaId(const SchemaId&);
+ };
+
+ struct AgentImplAccess
+ {
+ static AgentImpl& get(Agent&);
+ static const AgentImpl& get(const Agent&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp
new file mode 100644
index 0000000000..71d369325f
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentSession.cpp
@@ -0,0 +1,1072 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/AgentSession.h"
+#include "qmf/AgentEventImpl.h"
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/DataAddrImpl.h"
+#include "qmf/DataImpl.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/agentCapability.h"
+#include "qmf/constants.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Condition.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/management/Buffer.h"
+#include <queue>
+#include <map>
+#include <set>
+#include <iostream>
+#include <memory>
+
+using namespace std;
+using namespace qpid::messaging;
+using namespace qmf;
+using qpid::types::Variant;
+
+namespace qmf {
+ class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
+ public:
+ ~AgentSessionImpl();
+
+ //
+ // Methods from API handle
+ //
+ AgentSessionImpl(Connection& c, const string& o);
+ void setDomain(const string& d) { checkOpen(); domain = d; }
+ void setVendor(const string& v) { checkOpen(); attributes["_vendor"] = v; }
+ void setProduct(const string& p) { checkOpen(); attributes["_product"] = p; }
+ void setInstance(const string& i) { checkOpen(); attributes["_instance"] = i; }
+ void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
+ const string& getName() const { return agentName; }
+ void open();
+ void close();
+ bool nextEvent(AgentEvent& e, Duration t);
+ int pendingEvents() const;
+
+ void registerSchema(Schema& s);
+ DataAddr addData(Data& d, const string& n, bool persist);
+ void delData(const DataAddr&);
+
+ void authAccept(AgentEvent& e);
+ void authReject(AgentEvent& e, const string& m);
+ void raiseException(AgentEvent& e, const string& s);
+ void raiseException(AgentEvent& e, const Data& d);
+ void response(AgentEvent& e, const Data& d);
+ void complete(AgentEvent& e);
+ void methodSuccess(AgentEvent& e);
+ void raiseEvent(const Data& d);
+ void raiseEvent(const Data& d, int s);
+
+ private:
+ typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
+ typedef map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
+
+ mutable qpid::sys::Mutex lock;
+ qpid::sys::Condition cond;
+ Connection connection;
+ Session session;
+ Sender directSender;
+ Sender topicSender;
+ string domain;
+ Variant::Map attributes;
+ Variant::Map options;
+ string agentName;
+ bool opened;
+ queue<AgentEvent> eventQueue;
+ qpid::sys::Thread* thread;
+ bool threadCanceled;
+ uint32_t bootSequence;
+ uint32_t interval;
+ uint64_t lastHeartbeat;
+ uint64_t lastVisit;
+ bool forceHeartbeat;
+ bool externalStorage;
+ bool autoAllowQueries;
+ bool autoAllowMethods;
+ uint32_t maxSubscriptions;
+ uint32_t minSubInterval;
+ uint32_t subLifetime;
+ bool publicEvents;
+ bool listenOnDirect;
+ bool strictSecurity;
+ uint64_t schemaUpdateTime;
+ string directBase;
+ string topicBase;
+
+ SchemaMap schemata;
+ DataIndex globalIndex;
+ map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
+
+ void checkOpen();
+ void setAgentName();
+ void enqueueEvent(const AgentEvent&);
+ void handleLocateRequest(const Variant::List& content, const Message& msg);
+ void handleMethodRequest(const Variant::Map& content, const Message& msg);
+ void handleQueryRequest(const Variant::Map& content, const Message& msg);
+ void handleSchemaRequest(AgentEvent&);
+ void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&);
+ void dispatch(Message);
+ void sendHeartbeat();
+ void send(Message, const Address&);
+ void flushResponses(AgentEvent&, bool);
+ void periodicProcessing(uint64_t);
+ void run();
+ };
+}
+
+typedef qmf::PrivateImplRef<AgentSession> PI;
+
+AgentSession::AgentSession(AgentSessionImpl* impl) { PI::ctor(*this, impl); }
+AgentSession::AgentSession(const AgentSession& s) : qmf::Handle<AgentSessionImpl>() { PI::copy(*this, s); }
+AgentSession::~AgentSession() { PI::dtor(*this); }
+AgentSession& AgentSession::operator=(const AgentSession& s) { return PI::assign(*this, s); }
+
+AgentSession::AgentSession(Connection& c, const string& o) { PI::ctor(*this, new AgentSessionImpl(c, o)); }
+void AgentSession::setDomain(const string& d) { impl->setDomain(d); }
+void AgentSession::setVendor(const string& v) { impl->setVendor(v); }
+void AgentSession::setProduct(const string& p) { impl->setProduct(p); }
+void AgentSession::setInstance(const string& i) { impl->setInstance(i); }
+void AgentSession::setAttribute(const string& k, const qpid::types::Variant& v) { impl->setAttribute(k, v); }
+const string& AgentSession::getName() const { return impl->getName(); }
+void AgentSession::open() { impl->open(); }
+void AgentSession::close() { impl->close(); }
+bool AgentSession::nextEvent(AgentEvent& e, Duration t) { return impl->nextEvent(e, t); }
+int AgentSession::pendingEvents() const { return impl->pendingEvents(); }
+void AgentSession::registerSchema(Schema& s) { impl->registerSchema(s); }
+DataAddr AgentSession::addData(Data& d, const string& n, bool p) { return impl->addData(d, n, p); }
+void AgentSession::delData(const DataAddr& a) { impl->delData(a); }
+void AgentSession::authAccept(AgentEvent& e) { impl->authAccept(e); }
+void AgentSession::authReject(AgentEvent& e, const string& m) { impl->authReject(e, m); }
+void AgentSession::raiseException(AgentEvent& e, const string& s) { impl->raiseException(e, s); }
+void AgentSession::raiseException(AgentEvent& e, const Data& d) { impl->raiseException(e, d); }
+void AgentSession::response(AgentEvent& e, const Data& d) { impl->response(e, d); }
+void AgentSession::complete(AgentEvent& e) { impl->complete(e); }
+void AgentSession::methodSuccess(AgentEvent& e) { impl->methodSuccess(e); }
+void AgentSession::raiseEvent(const Data& d) { impl->raiseEvent(d); }
+void AgentSession::raiseEvent(const Data& d, int s) { impl->raiseEvent(d, s); }
+
+//========================================================================================
+// Impl Method Bodies
+//========================================================================================
+
+AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
+ connection(c), domain("default"), opened(false), thread(0), threadCanceled(false),
+ bootSequence(1), interval(60), lastHeartbeat(0), lastVisit(0), forceHeartbeat(false),
+ externalStorage(false), autoAllowQueries(true), autoAllowMethods(true),
+ maxSubscriptions(64), minSubInterval(3000), subLifetime(300), publicEvents(true),
+ listenOnDirect(true), strictSecurity(false),
+ schemaUpdateTime(uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())))
+{
+ //
+ // Set Agent Capability Level
+ //
+ attributes["qmf.agent_capability"] = AGENT_CAPABILITY_0_8;
+
+ if (!options.empty()) {
+ qpid::messaging::AddressParser parser(options);
+ Variant::Map optMap;
+ Variant::Map::const_iterator iter;
+
+ parser.parseMap(optMap);
+
+ iter = optMap.find("domain");
+ if (iter != optMap.end())
+ domain = iter->second.asString();
+
+ iter = optMap.find("interval");
+ if (iter != optMap.end()) {
+ interval = iter->second.asUint32();
+ if (interval < 1)
+ interval = 1;
+ }
+
+ iter = optMap.find("external");
+ if (iter != optMap.end())
+ externalStorage = iter->second.asBool();
+
+ iter = optMap.find("allow-queries");
+ if (iter != optMap.end())
+ autoAllowQueries = iter->second.asBool();
+
+ iter = optMap.find("allow-methods");
+ if (iter != optMap.end())
+ autoAllowMethods = iter->second.asBool();
+
+ iter = optMap.find("max-subscriptions");
+ if (iter != optMap.end())
+ maxSubscriptions = iter->second.asUint32();
+
+ iter = optMap.find("min-sub-interval");
+ if (iter != optMap.end())
+ minSubInterval = iter->second.asUint32();
+
+ iter = optMap.find("sub-lifetime");
+ if (iter != optMap.end())
+ subLifetime = iter->second.asUint32();
+
+ iter = optMap.find("public-events");
+ if (iter != optMap.end())
+ publicEvents = iter->second.asBool();
+
+ iter = optMap.find("listen-on-direct");
+ if (iter != optMap.end())
+ listenOnDirect = iter->second.asBool();
+
+ iter = optMap.find("strict-security");
+ if (iter != optMap.end())
+ strictSecurity = iter->second.asBool();
+ }
+}
+
+
+AgentSessionImpl::~AgentSessionImpl()
+{
+ if (opened)
+ close();
+}
+
+
+void AgentSessionImpl::open()
+{
+ if (opened)
+ throw QmfException("The session is already open");
+
+ const string addrArgs(";{create:never,node:{type:topic}}");
+ const string routableAddr("direct-agent.route." + qpid::types::Uuid(true).str());
+ attributes["_direct_subject"] = routableAddr;
+
+ // Establish messaging addresses
+ setAgentName();
+ directBase = "qmf." + domain + ".direct";
+ topicBase = "qmf." + domain + ".topic";
+
+ // Create AMQP session, receivers, and senders
+ session = connection.createSession();
+ Receiver directRx;
+ Receiver routableDirectRx = session.createReceiver(topicBase + "/" + routableAddr + addrArgs);
+ Receiver topicRx = session.createReceiver(topicBase + "/console.#" + addrArgs);
+
+ if (listenOnDirect && !strictSecurity) {
+ directRx = session.createReceiver(directBase + "/" + agentName + addrArgs);
+ directRx.setCapacity(64);
+ }
+
+ routableDirectRx.setCapacity(64);
+ topicRx.setCapacity(64);
+
+ if (!strictSecurity)
+ directSender = session.createSender(directBase + addrArgs);
+ topicSender = session.createSender(topicBase + addrArgs);
+
+ // Start the receiver thread
+ threadCanceled = false;
+ opened = true;
+ thread = new qpid::sys::Thread(*this);
+
+ // Send an initial agent heartbeat message
+ sendHeartbeat();
+}
+
+
+void AgentSessionImpl::close()
+{
+ if (!opened)
+ return;
+
+ // Stop and join the receiver thread
+ threadCanceled = true;
+ thread->join();
+ delete thread;
+
+ // Close the AMQP session
+ session.close();
+ opened = false;
+}
+
+
+bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
+{
+ uint64_t milliseconds = timeout.getMilliseconds();
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ if (eventQueue.empty() && milliseconds > 0)
+ cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
+ qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+
+ if (!eventQueue.empty()) {
+ event = eventQueue.front();
+ eventQueue.pop();
+ return true;
+ }
+
+ return false;
+}
+
+
+int AgentSessionImpl::pendingEvents() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventQueue.size();
+}
+
+
+void AgentSessionImpl::registerSchema(Schema& schema)
+{
+ if (!schema.isFinalized())
+ schema.finalize();
+ const SchemaId& schemaId(schema.getSchemaId());
+
+ qpid::sys::Mutex::ScopedLock l(lock);
+ schemata[schemaId] = schema;
+ schemaIndex[schemaId] = DataIndex();
+
+ //
+ // Get the news out at the next periodic interval that there is new schema information.
+ //
+ schemaUpdateTime = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+ forceHeartbeat = true;
+}
+
+
+DataAddr AgentSessionImpl::addData(Data& data, const string& name, bool persistent)
+{
+ if (externalStorage)
+ throw QmfException("addData() must not be called when the 'external' option is enabled.");
+
+ string dataName;
+ if (name.empty())
+ dataName = qpid::types::Uuid(true).str();
+ else
+ dataName = name;
+
+ DataAddr addr(dataName, agentName, persistent ? 0 : bootSequence);
+ data.setAddr(addr);
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ DataIndex::const_iterator iter = globalIndex.find(addr);
+ if (iter != globalIndex.end())
+ throw QmfException("Duplicate Data Address");
+
+ globalIndex[addr] = data;
+ if (data.hasSchema())
+ schemaIndex[data.getSchemaId()][addr] = data;
+ }
+
+ //
+ // TODO: Survey active subscriptions to see if they need to hear about this new data.
+ //
+
+ return addr;
+}
+
+
+void AgentSessionImpl::delData(const DataAddr& addr)
+{
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ DataIndex::iterator iter = globalIndex.find(addr);
+ if (iter == globalIndex.end())
+ return;
+ if (iter->second.hasSchema()) {
+ const SchemaId& schemaId(iter->second.getSchemaId());
+ schemaIndex[schemaId].erase(addr);
+ }
+ globalIndex.erase(iter);
+ }
+
+ //
+ // TODO: Survey active subscriptions to see if they need to hear about this deleted data.
+ //
+}
+
+
+void AgentSessionImpl::authAccept(AgentEvent& authEvent)
+{
+ auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_QUERY));
+ eventImpl->setQuery(authEvent.getQuery());
+ eventImpl->setUserId(authEvent.getUserId());
+ eventImpl->setReplyTo(AgentEventImplAccess::get(authEvent).getReplyTo());
+ eventImpl->setCorrelationId(AgentEventImplAccess::get(authEvent).getCorrelationId());
+ AgentEvent event(eventImpl.release());
+
+ if (externalStorage) {
+ enqueueEvent(event);
+ return;
+ }
+
+ const Query& query(authEvent.getQuery());
+ if (query.getDataAddr().isValid()) {
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ DataIndex::const_iterator iter = globalIndex.find(query.getDataAddr());
+ if (iter != globalIndex.end())
+ response(event, iter->second);
+ }
+ complete(event);
+ return;
+ }
+
+ if (query.getSchemaId().isValid()) {
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<SchemaId, DataIndex>::const_iterator iter = schemaIndex.find(query.getSchemaId());
+ if (iter != schemaIndex.end())
+ for (DataIndex::const_iterator dIter = iter->second.begin(); dIter != iter->second.end(); dIter++)
+ if (query.matchesPredicate(dIter->second.getProperties()))
+ response(event, dIter->second);
+ }
+ complete(event);
+ return;
+ }
+
+ raiseException(event, "Query is Invalid");
+}
+
+
+void AgentSessionImpl::authReject(AgentEvent& event, const string& error)
+{
+ raiseException(event, "Action Forbidden - " + error);
+}
+
+
+void AgentSessionImpl::raiseException(AgentEvent& event, const string& error)
+{
+ Data exception(new DataImpl());
+ exception.setProperty("error_text", error);
+ raiseException(event, exception);
+}
+
+
+void AgentSessionImpl::raiseException(AgentEvent& event, const Data& data)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_EXCEPTION;
+ headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
+ const DataImpl& dataImpl(DataImplAccess::get(data));
+
+ msg.setCorrelationId(eventImpl.getCorrelationId());
+ encode(dataImpl.asMap(), msg);
+ send(msg, eventImpl.getReplyTo());
+
+ QPID_LOG(trace, "SENT Exception to=" << eventImpl.getReplyTo());
+}
+
+
+void AgentSessionImpl::response(AgentEvent& event, const Data& data)
+{
+ AgentEventImpl& impl(AgentEventImplAccess::get(event));
+ uint32_t count = impl.enqueueData(data);
+ if (count >= 8)
+ flushResponses(event, false);
+}
+
+
+void AgentSessionImpl::complete(AgentEvent& event)
+{
+ flushResponses(event, true);
+}
+
+
+void AgentSessionImpl::methodSuccess(AgentEvent& event)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_RESPONSE;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
+
+ const Variant::Map& outArgs(eventImpl.getReturnArguments());
+ const Variant::Map& outSubtypes(eventImpl.getReturnArgumentSubtypes());
+
+ map["_arguments"] = outArgs;
+ if (!outSubtypes.empty())
+ map["_subtypes"] = outSubtypes;
+
+ msg.setCorrelationId(eventImpl.getCorrelationId());
+ encode(map, msg);
+ send(msg, eventImpl.getReplyTo());
+
+ QPID_LOG(trace, "SENT MethodResponse to=" << eventImpl.getReplyTo());
+}
+
+
+void AgentSessionImpl::raiseEvent(const Data& data)
+{
+ int severity(SEV_NOTICE);
+ if (data.hasSchema()) {
+ const Schema& schema(DataImplAccess::get(data).getSchema());
+ if (schema.isValid())
+ severity = schema.getDefaultSeverity();
+ }
+
+ raiseEvent(data, severity);
+}
+
+
+void AgentSessionImpl::raiseEvent(const Data& data, int severity)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+ string subject("agent.ind.event");
+
+ if (data.hasSchema()) {
+ const SchemaId& schemaId(data.getSchemaId());
+ if (schemaId.getType() != SCHEMA_TYPE_EVENT)
+ throw QmfException("Cannot call raiseEvent on data that is not an Event");
+ subject = subject + "." + schemaId.getPackageName() + "." + schemaId.getName();
+ }
+
+ if (severity < SEV_EMERG || severity > SEV_DEBUG)
+ throw QmfException("Invalid severity value");
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_DATA_INDICATION;
+ headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_EVENT;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+ msg.setSubject(subject);
+
+ Variant::List list;
+ Variant::Map dataAsMap(DataImplAccess::get(data).asMap());
+ dataAsMap["_severity"] = severity;
+ dataAsMap["_timestamp"] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+ list.push_back(dataAsMap);
+ encode(list, msg);
+ topicSender.send(msg);
+
+ QPID_LOG(trace, "SENT EventIndication to=" << topicSender.getName() << "/" << subject);
+}
+
+
+void AgentSessionImpl::checkOpen()
+{
+ if (opened)
+ throw QmfException("Operation must be performed before calling open()");
+}
+
+
+void AgentSessionImpl::enqueueEvent(const AgentEvent& event)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ bool notify = eventQueue.empty();
+ eventQueue.push(event);
+ if (notify)
+ cond.notify();
+}
+
+
+void AgentSessionImpl::setAgentName()
+{
+ Variant::Map::iterator iter;
+ string vendor;
+ string product;
+ string instance;
+
+ iter = attributes.find("_vendor");
+ if (iter == attributes.end())
+ attributes["_vendor"] = vendor;
+ else
+ vendor = iter->second.asString();
+
+ iter = attributes.find("_product");
+ if (iter == attributes.end())
+ attributes["_product"] = product;
+ else
+ product = iter->second.asString();
+
+ iter = attributes.find("_instance");
+ if (iter == attributes.end()) {
+ instance = qpid::types::Uuid(true).str();
+ attributes["_instance"] = instance;
+ } else
+ instance = iter->second.asString();
+
+ agentName = vendor + ":" + product + ":" + instance;
+ attributes["_name"] = agentName;
+}
+
+
+void AgentSessionImpl::handleLocateRequest(const Variant::List& predicate, const Message& msg)
+{
+ QPID_LOG(trace, "RCVD AgentLocateRequest from=" << msg.getReplyTo());
+
+ if (!predicate.empty()) {
+ Query agentQuery(QUERY_OBJECT);
+ agentQuery.setPredicate(predicate);
+ if (!agentQuery.matchesPredicate(attributes)) {
+ QPID_LOG(trace, "AgentLocate predicate does not match this agent, ignoring");
+ return;
+ }
+ }
+
+ Message reply;
+ Variant::Map map;
+ Variant::Map& headers(reply.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ map["_values"] = attributes;
+ map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+ map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval;
+ map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence;
+ map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime;
+
+ encode(map, reply);
+ send(reply, msg.getReplyTo());
+ QPID_LOG(trace, "SENT AgentLocateResponse to=" << msg.getReplyTo());
+}
+
+
+void AgentSessionImpl::handleMethodRequest(const Variant::Map& content, const Message& msg)
+{
+ QPID_LOG(trace, "RCVD MethodRequest map=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId());
+
+ //
+ // Construct an AgentEvent to be sent to the application.
+ //
+ auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_METHOD));
+ eventImpl->setUserId(msg.getUserId());
+ eventImpl->setReplyTo(msg.getReplyTo());
+ eventImpl->setCorrelationId(msg.getCorrelationId());
+
+ Variant::Map::const_iterator iter;
+
+ iter = content.find("_method_name");
+ if (iter == content.end()) {
+ AgentEvent event(eventImpl.release());
+ raiseException(event, "Malformed MethodRequest: missing _method_name field");
+ return;
+ }
+ eventImpl->setMethodName(iter->second.asString());
+
+ iter = content.find("_arguments");
+ if (iter != content.end())
+ eventImpl->setArguments(iter->second.asMap());
+
+ iter = content.find("_subtypes");
+ if (iter != content.end())
+ eventImpl->setArgumentSubtypes(iter->second.asMap());
+
+ iter = content.find("_object_id");
+ if (iter != content.end()) {
+ DataAddr addr(new DataAddrImpl(iter->second.asMap()));
+ eventImpl->setDataAddr(addr);
+ DataIndex::const_iterator iter(globalIndex.find(addr));
+ if (iter == globalIndex.end()) {
+ AgentEvent event(eventImpl.release());
+ raiseException(event, "No data object found with the specified address");
+ return;
+ }
+
+ const Schema& schema(DataImplAccess::get(iter->second).getSchema());
+ if (schema.isValid()) {
+ eventImpl->setSchema(schema);
+ for (Variant::Map::const_iterator aIter = eventImpl->getArguments().begin();
+ aIter != eventImpl->getArguments().end(); aIter++) {
+ const Schema& schema(DataImplAccess::get(iter->second).getSchema());
+ if (!SchemaImplAccess::get(schema).isValidMethodInArg(eventImpl->getMethodName(), aIter->first, aIter->second)) {
+ AgentEvent event(eventImpl.release());
+ raiseException(event, "Invalid argument: " + aIter->first);
+ return;
+ }
+ }
+ }
+ }
+
+ enqueueEvent(AgentEvent(eventImpl.release()));
+}
+
+
+void AgentSessionImpl::handleQueryRequest(const Variant::Map& content, const Message& msg)
+{
+ QPID_LOG(trace, "RCVD QueryRequest query=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId());
+
+ //
+ // Construct an AgentEvent to be sent to the application or directly handled by the agent.
+ //
+ auto_ptr<QueryImpl> queryImpl(new QueryImpl(content));
+ auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_AUTH_QUERY));
+ eventImpl->setUserId(msg.getUserId());
+ eventImpl->setReplyTo(msg.getReplyTo());
+ eventImpl->setCorrelationId(msg.getCorrelationId());
+ eventImpl->setQuery(queryImpl.release());
+ AgentEvent ae(eventImpl.release());
+
+ if (ae.getQuery().getTarget() == QUERY_SCHEMA_ID || ae.getQuery().getTarget() == QUERY_SCHEMA) {
+ handleSchemaRequest(ae);
+ return;
+ }
+
+ if (autoAllowQueries)
+ authAccept(ae);
+ else
+ enqueueEvent(ae);
+}
+
+
+void AgentSessionImpl::handleSchemaRequest(AgentEvent& event)
+{
+ SchemaMap::const_iterator iter;
+ string error;
+ const Query& query(event.getQuery());
+
+ Message msg;
+ Variant::List content;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (query.getTarget() == QUERY_SCHEMA_ID) {
+ headers[protocol::HEADER_KEY_CONTENT] = "_schema_id";
+ for (iter = schemata.begin(); iter != schemata.end(); iter++)
+ content.push_back(SchemaIdImplAccess::get(iter->first).asMap());
+ } else if (query.getSchemaId().isValid()) {
+ headers[protocol::HEADER_KEY_CONTENT] = "_schema";
+ iter = schemata.find(query.getSchemaId());
+ if (iter != schemata.end())
+ content.push_back(SchemaImplAccess::get(iter->second).asMap());
+ } else {
+ error = "Invalid Schema Query: Requests for SCHEMA must supply a valid schema ID.";
+ }
+ }
+
+ if (!error.empty()) {
+ raiseException(event, error);
+ return;
+ }
+
+ AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
+
+ msg.setCorrelationId(eventImpl.getCorrelationId());
+ encode(content, msg);
+ send(msg, eventImpl.getReplyTo());
+
+ QPID_LOG(trace, "SENT QueryResponse(Schema) to=" << eventImpl.getReplyTo());
+}
+
+
+void AgentSessionImpl::handleV1SchemaRequest(qpid::management::Buffer& buffer, uint32_t seq, const Message& msg)
+{
+ string packageName;
+ string className;
+ uint8_t hashBits[16];
+
+ buffer.getShortString(packageName);
+ buffer.getShortString(className);
+ buffer.getBin128(hashBits);
+
+ QPID_LOG(trace, "RCVD QMFv1 SchemaRequest for " << packageName << ":" << className);
+
+ qpid::types::Uuid hash(hashBits);
+ map<SchemaId, Schema>::const_iterator iter;
+ string replyContent;
+
+ SchemaId dataId(SCHEMA_TYPE_DATA, packageName, className);
+ dataId.setHash(hash);
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ iter = schemata.find(dataId);
+ if (iter != schemata.end())
+ replyContent = SchemaImplAccess::get(iter->second).asV1Content(seq);
+ else {
+ SchemaId eventId(SCHEMA_TYPE_EVENT, packageName, className);
+ eventId.setHash(hash);
+ iter = schemata.find(dataId);
+ if (iter != schemata.end())
+ replyContent = SchemaImplAccess::get(iter->second).asV1Content(seq);
+ else
+ return;
+ }
+ }
+
+ Message reply;
+ Variant::Map& headers(reply.getProperties());
+
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ reply.setContent(replyContent);
+
+ send(reply, msg.getReplyTo());
+ QPID_LOG(trace, "SENT QMFv1 SchemaResponse to=" << msg.getReplyTo());
+}
+
+
+void AgentSessionImpl::dispatch(Message msg)
+{
+ const Variant::Map& properties(msg.getProperties());
+ Variant::Map::const_iterator iter;
+
+ //
+ // If strict-security is enabled, make sure that reply-to address complies with the
+ // strict-security addressing pattern (i.e. start with 'qmf.<domain>.topic/direct-console.').
+ //
+ if (strictSecurity && msg.getReplyTo()) {
+ if (msg.getReplyTo().getName() != topicBase || msg.getReplyTo().getSubject().find("direct-console.") != 0) {
+ QPID_LOG(warning, "Reply-to violates strict-security policy: " << msg.getReplyTo().str());
+ return;
+ }
+ }
+
+ iter = properties.find(protocol::HEADER_KEY_APP_ID);
+ if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF) {
+ //
+ // Dispatch a QMFv2 formatted message
+ //
+ iter = properties.find(protocol::HEADER_KEY_OPCODE);
+ if (iter == properties.end()) {
+ QPID_LOG(trace, "Message received with no 'qmf.opcode' header");
+ return;
+ }
+
+ const string& opcode = iter->second.asString();
+
+ if (msg.getContentType() == "amqp/list") {
+ Variant::List content;
+ decode(msg, content);
+
+ if (opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST) handleLocateRequest(content, msg);
+ else {
+ QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/list' content: " << opcode);
+ }
+
+ } else if (msg.getContentType() == "amqp/map") {
+ Variant::Map content;
+ decode(msg, content);
+
+ if (opcode == protocol::HEADER_OPCODE_METHOD_REQUEST) handleMethodRequest(content, msg);
+ else if (opcode == protocol::HEADER_OPCODE_QUERY_REQUEST) handleQueryRequest(content, msg);
+ else {
+ QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/map' content: " << opcode);
+ }
+ } else {
+ QPID_LOG(trace, "Unexpected QMFv2 content type. Expected amqp/list or amqp/map");
+ }
+
+ } else {
+ //
+ // Dispatch a QMFv1 formatted message
+ //
+ const string& body(msg.getContent());
+ if (body.size() < 8)
+ return;
+ qpid::management::Buffer buffer(const_cast<char*>(body.c_str()), body.size());
+
+ if (buffer.getOctet() != 'A') return;
+ if (buffer.getOctet() != 'M') return;
+ if (buffer.getOctet() != '2') return;
+ char v1Opcode(buffer.getOctet());
+ uint32_t seq(buffer.getLong());
+
+ if (v1Opcode == 'S') handleV1SchemaRequest(buffer, seq, msg);
+ else {
+ QPID_LOG(trace, "Unknown or Unsupported QMFv1 opcode: " << v1Opcode);
+ }
+ }
+}
+
+
+void AgentSessionImpl::sendHeartbeat()
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+ std::stringstream address;
+
+ address << "agent.ind.heartbeat";
+
+ // append .<vendor>.<product> to address key if present.
+ Variant::Map::const_iterator v;
+ if ((v = attributes.find("_vendor")) != attributes.end() && !v->second.getString().empty()) {
+ address << "." << v->second.getString();
+ if ((v = attributes.find("_product")) != attributes.end() && !v->second.getString().empty()) {
+ address << "." << v->second.getString();
+ }
+ }
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+ msg.setSubject(address.str());
+
+ map["_values"] = attributes;
+ map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+ map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval;
+ map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence;
+ map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime;
+
+ encode(map, msg);
+ topicSender.send(msg);
+ QPID_LOG(trace, "SENT AgentHeartbeat name=" << agentName);
+}
+
+
+void AgentSessionImpl::send(Message msg, const Address& to)
+{
+ Sender sender;
+
+ if (strictSecurity && to.getName() != topicBase) {
+ QPID_LOG(warning, "Address violates strict-security policy: " << to);
+ return;
+ }
+
+ if (to.getName() == directBase) {
+ msg.setSubject(to.getSubject());
+ sender = directSender;
+ } else if (to.getName() == topicBase) {
+ msg.setSubject(to.getSubject());
+ sender = topicSender;
+ } else
+ sender = session.createSender(to);
+
+ sender.send(msg);
+}
+
+
+void AgentSessionImpl::flushResponses(AgentEvent& event, bool final)
+{
+ Message msg;
+ Variant::Map map;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE;
+ headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA;
+ headers[protocol::HEADER_KEY_AGENT] = agentName;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+ if (!final)
+ headers[protocol::HEADER_KEY_PARTIAL] = Variant();
+
+ Variant::List body;
+ AgentEventImpl& eventImpl(AgentEventImplAccess::get(event));
+ Data data(eventImpl.dequeueData());
+ while (data.isValid()) {
+ DataImpl& dataImpl(DataImplAccess::get(data));
+ body.push_back(dataImpl.asMap());
+ data = eventImpl.dequeueData();
+ }
+
+ msg.setCorrelationId(eventImpl.getCorrelationId());
+ encode(body, msg);
+ send(msg, eventImpl.getReplyTo());
+
+ QPID_LOG(trace, "SENT QueryResponse to=" << eventImpl.getReplyTo());
+}
+
+
+void AgentSessionImpl::periodicProcessing(uint64_t seconds)
+{
+ //
+ // The granularity of this timer is seconds. Don't waste time looking for work if
+ // it's been less than a second since we last visited.
+ //
+ if (seconds == lastVisit)
+ return;
+ //uint64_t thisInterval(seconds - lastVisit);
+ lastVisit = seconds;
+
+ //
+ // First time through, set lastHeartbeat to the current time.
+ //
+ if (lastHeartbeat == 0)
+ lastHeartbeat = seconds;
+
+ //
+ // If the hearbeat interval has elapsed, send a heartbeat.
+ //
+ if (forceHeartbeat || (seconds - lastHeartbeat >= interval)) {
+ lastHeartbeat = seconds;
+ forceHeartbeat = false;
+ sendHeartbeat();
+ }
+
+ //
+ // TODO: process any active subscriptions on their intervals.
+ //
+}
+
+
+void AgentSessionImpl::run()
+{
+ QPID_LOG(debug, "AgentSession thread started for agent " << agentName);
+
+ try {
+ while (!threadCanceled) {
+ periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) / qpid::sys::TIME_SEC);
+
+ Receiver rx;
+ bool valid = session.nextReceiver(rx, Duration::SECOND);
+ if (threadCanceled)
+ break;
+ if (valid) {
+ try {
+ dispatch(rx.fetch());
+ } catch (qpid::types::Exception& e) {
+ QPID_LOG(error, "Exception caught in message dispatch: " << e.what());
+ }
+ session.acknowledge();
+ }
+ }
+ } catch (qpid::types::Exception& e) {
+ QPID_LOG(error, "Exception caught in message thread - exiting: " << e.what());
+ enqueueEvent(AgentEvent(new AgentEventImpl(AGENT_THREAD_FAILED)));
+ }
+
+ QPID_LOG(debug, "AgentSession thread exiting for agent " << agentName);
+}
+
diff --git a/qpid/cpp/src/qmf/AgentSubscription.cpp b/qpid/cpp/src/qmf/AgentSubscription.cpp
new file mode 100644
index 0000000000..4dc5cb74a4
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentSubscription.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/AgentSubscription.h"
+
+using namespace qmf;
+
+AgentSubscription::AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life,
+ const std::string& _replyTo, const std::string& _cid, Query _query) :
+ id(_id), interval(_interval), lifetime(_life), timeSincePublish(0), timeSinceKeepalive(0),
+ replyTo(_replyTo), cid(_cid), query(_query)
+{
+}
+
+
+AgentSubscription::~AgentSubscription()
+{
+}
+
+
+bool AgentSubscription::tick(uint64_t seconds)
+{
+ timeSinceKeepalive += seconds;
+ if (timeSinceKeepalive >= lifetime)
+ return false;
+
+ timeSincePublish += seconds;
+ if (timeSincePublish >= interval) {
+ }
+
+ return true;
+}
+
diff --git a/qpid/cpp/src/qmf/AgentSubscription.h b/qpid/cpp/src/qmf/AgentSubscription.h
new file mode 100644
index 0000000000..01e8f43e9f
--- /dev/null
+++ b/qpid/cpp/src/qmf/AgentSubscription.h
@@ -0,0 +1,52 @@
+#ifndef _QMF_AGENT_SUBSCRIPTION_H_
+#define _QMF_AGENT_SUBSCRIPTION_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/types/Variant.h"
+#include "qmf/Query.h"
+#include "qmf/Data.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+ class AgentSubscription {
+ public:
+ AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life,
+ const std::string& _replyTo, const std::string& _cid, Query _query);
+ ~AgentSubscription();
+ bool tick(uint64_t seconds);
+ void keepalive() { timeSinceKeepalive = 0; }
+
+ private:
+ uint64_t id;
+ uint64_t interval;
+ uint64_t lifetime;
+ uint64_t timeSincePublish;
+ uint64_t timeSinceKeepalive;
+ const std::string replyTo;
+ const std::string cid;
+ Query query;
+ };
+
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/ConsoleEvent.cpp b/qpid/cpp/src/qmf/ConsoleEvent.cpp
new file mode 100644
index 0000000000..b2a5e321c7
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConsoleEvent.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/ConsoleEventImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<ConsoleEvent> PI;
+
+ConsoleEvent::ConsoleEvent(ConsoleEventImpl* impl) { PI::ctor(*this, impl); }
+ConsoleEvent::ConsoleEvent(const ConsoleEvent& s) : qmf::Handle<ConsoleEventImpl>() { PI::copy(*this, s); }
+ConsoleEvent::~ConsoleEvent() { PI::dtor(*this); }
+ConsoleEvent& ConsoleEvent::operator=(const ConsoleEvent& s) { return PI::assign(*this, s); }
+
+ConsoleEventCode ConsoleEvent::getType() const { return impl->getType(); }
+uint32_t ConsoleEvent::getCorrelator() const { return impl->getCorrelator(); }
+Agent ConsoleEvent::getAgent() const { return impl->getAgent(); }
+AgentDelReason ConsoleEvent::getAgentDelReason() const { return impl->getAgentDelReason(); }
+uint32_t ConsoleEvent::getSchemaIdCount() const { return impl->getSchemaIdCount(); }
+SchemaId ConsoleEvent::getSchemaId(uint32_t i) const { return impl->getSchemaId(i); }
+uint32_t ConsoleEvent::getDataCount() const { return impl->getDataCount(); }
+Data ConsoleEvent::getData(uint32_t i) const { return impl->getData(i); }
+bool ConsoleEvent::isFinal() const { return impl->isFinal(); }
+const Variant::Map& ConsoleEvent::getArguments() const { return impl->getArguments(); }
+int ConsoleEvent::getSeverity() const { return impl->getSeverity(); }
+uint64_t ConsoleEvent::getTimestamp() const { return impl->getTimestamp(); }
+
+
+SchemaId ConsoleEventImpl::getSchemaId(uint32_t i) const
+{
+ uint32_t count = 0;
+ for (list<SchemaId>::const_iterator iter = newSchemaIds.begin(); iter != newSchemaIds.end(); iter++) {
+ if (count++ == i)
+ return *iter;
+ }
+ throw IndexOutOfRange();
+}
+
+
+Data ConsoleEventImpl::getData(uint32_t i) const
+{
+ uint32_t count = 0;
+ for (list<Data>::const_iterator iter = dataList.begin(); iter != dataList.end(); iter++) {
+ if (count++ == i)
+ return *iter;
+ }
+ throw IndexOutOfRange();
+}
+
+
+ConsoleEventImpl& ConsoleEventImplAccess::get(ConsoleEvent& item)
+{
+ return *item.impl;
+}
+
+
+const ConsoleEventImpl& ConsoleEventImplAccess::get(const ConsoleEvent& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/ConsoleEventImpl.h b/qpid/cpp/src/qmf/ConsoleEventImpl.h
new file mode 100644
index 0000000000..9843971456
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConsoleEventImpl.h
@@ -0,0 +1,84 @@
+#ifndef _QMF_CONSOLE_EVENT_IMPL_H_
+#define _QMF_CONSOLE_EVENT_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/ConsoleEvent.h"
+#include "qmf/Agent.h"
+#include "qmf/Data.h"
+#include "qpid/types/Variant.h"
+#include <list>
+
+namespace qmf {
+ class ConsoleEventImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Impl-only methods
+ //
+ ConsoleEventImpl(ConsoleEventCode e, AgentDelReason r = AGENT_DEL_AGED) :
+ eventType(e), delReason(r), correlator(0), final(false) {}
+ void setCorrelator(uint32_t c) { correlator = c; }
+ void setAgent(const Agent& a) { agent = a; }
+ void addData(const Data& d) { dataList.push_back(Data(d)); }
+ void addSchemaId(const SchemaId& s) { newSchemaIds.push_back(SchemaId(s)); }
+ void setFinal() { final = true; }
+ void setArguments(const qpid::types::Variant::Map& a) { arguments = a; }
+ void setSeverity(int s) { severity = s; }
+ void setTimestamp(uint64_t t) { timestamp = t; }
+
+ //
+ // Methods from API handle
+ //
+ ConsoleEventCode getType() const { return eventType; }
+ uint32_t getCorrelator() const { return correlator; }
+ Agent getAgent() const { return agent; }
+ AgentDelReason getAgentDelReason() const { return delReason; }
+ uint32_t getSchemaIdCount() const { return newSchemaIds.size(); }
+ SchemaId getSchemaId(uint32_t) const;
+ uint32_t getDataCount() const { return dataList.size(); }
+ Data getData(uint32_t i) const;
+ bool isFinal() const { return final; }
+ const qpid::types::Variant::Map& getArguments() const { return arguments; }
+ int getSeverity() const { return severity; }
+ uint64_t getTimestamp() const { return timestamp; }
+
+ private:
+ const ConsoleEventCode eventType;
+ const AgentDelReason delReason;
+ uint32_t correlator;
+ Agent agent;
+ bool final;
+ std::list<Data> dataList;
+ std::list<SchemaId> newSchemaIds;
+ qpid::types::Variant::Map arguments;
+ int severity;
+ uint64_t timestamp;
+ };
+
+ struct ConsoleEventImplAccess
+ {
+ static ConsoleEventImpl& get(ConsoleEvent&);
+ static const ConsoleEventImpl& get(const ConsoleEvent&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp
new file mode 100644
index 0000000000..7b839930e1
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConsoleSession.cpp
@@ -0,0 +1,618 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/PrivateImplRef.h"
+#include "qmf/ConsoleSessionImpl.h"
+#include "qmf/AgentImpl.h"
+#include "qmf/SchemaId.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/ConsoleEventImpl.h"
+#include "qmf/constants.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::messaging::Address;
+using qpid::messaging::Connection;
+using qpid::messaging::Receiver;
+using qpid::messaging::Sender;
+using qpid::messaging::Duration;
+using qpid::messaging::Message;
+using qpid::types::Variant;
+
+typedef qmf::PrivateImplRef<ConsoleSession> PI;
+
+ConsoleSession::ConsoleSession(ConsoleSessionImpl* impl) { PI::ctor(*this, impl); }
+ConsoleSession::ConsoleSession(const ConsoleSession& s) : qmf::Handle<ConsoleSessionImpl>() { PI::copy(*this, s); }
+ConsoleSession::~ConsoleSession() { PI::dtor(*this); }
+ConsoleSession& ConsoleSession::operator=(const ConsoleSession& s) { return PI::assign(*this, s); }
+
+ConsoleSession::ConsoleSession(Connection& c, const string& o) { PI::ctor(*this, new ConsoleSessionImpl(c, o)); }
+void ConsoleSession::setDomain(const string& d) { impl->setDomain(d); }
+void ConsoleSession::setAgentFilter(const string& f) { impl->setAgentFilter(f); }
+void ConsoleSession::open() { impl->open(); }
+void ConsoleSession::close() { impl->close(); }
+bool ConsoleSession::nextEvent(ConsoleEvent& e, Duration t) { return impl->nextEvent(e, t); }
+int ConsoleSession::pendingEvents() const { return impl->pendingEvents(); }
+uint32_t ConsoleSession::getAgentCount() const { return impl->getAgentCount(); }
+Agent ConsoleSession::getAgent(uint32_t i) const { return impl->getAgent(i); }
+Agent ConsoleSession::getConnectedBrokerAgent() const { return impl->getConnectedBrokerAgent(); }
+Subscription ConsoleSession::subscribe(const Query& q, const string& f, const string& o) { return impl->subscribe(q, f, o); }
+Subscription ConsoleSession::subscribe(const string& q, const string& f, const string& o) { return impl->subscribe(q, f, o); }
+
+//========================================================================================
+// Impl Method Bodies
+//========================================================================================
+
+ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
+ connection(c), domain("default"), maxAgentAgeMinutes(5),
+ opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+ connectedBrokerInAgentList(false), schemaCache(new SchemaCache())
+{
+ if (!options.empty()) {
+ qpid::messaging::AddressParser parser(options);
+ Variant::Map optMap;
+ Variant::Map::const_iterator iter;
+
+ parser.parseMap(optMap);
+
+ iter = optMap.find("domain");
+ if (iter != optMap.end())
+ domain = iter->second.asString();
+
+ iter = optMap.find("max-agent-age");
+ if (iter != optMap.end())
+ maxAgentAgeMinutes = iter->second.asUint32();
+
+ iter = optMap.find("listen-on-direct");
+ if (iter != optMap.end())
+ listenOnDirect = iter->second.asBool();
+
+ iter = optMap.find("strict-security");
+ if (iter != optMap.end())
+ strictSecurity = iter->second.asBool();
+ }
+}
+
+
+ConsoleSessionImpl::~ConsoleSessionImpl()
+{
+ if (opened)
+ close();
+}
+
+
+void ConsoleSessionImpl::setAgentFilter(const string& predicate)
+{
+ agentQuery = Query(QUERY_OBJECT, predicate);
+
+ //
+ // Purge the agent list of any agents that don't match the filter.
+ //
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<string, Agent> toDelete;
+ for (map<string, Agent>::iterator iter = agents.begin(); iter != agents.end(); iter++)
+ if (!agentQuery.matchesPredicate(iter->second.getAttributes())) {
+ toDelete[iter->first] = iter->second;
+ if (iter->second.getName() == connectedBrokerAgent.getName())
+ connectedBrokerInAgentList = false;
+ }
+
+ for (map<string, Agent>::iterator iter = toDelete.begin(); iter != toDelete.end(); iter++) {
+ agents.erase(iter->first);
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_DEL, AGENT_DEL_FILTER));
+ eventImpl->setAgent(iter->second);
+ enqueueEventLH(eventImpl.release());
+ }
+
+ if (!connectedBrokerInAgentList && connectedBrokerAgent.isValid() &&
+ agentQuery.matchesPredicate(connectedBrokerAgent.getAttributes())) {
+ agents[connectedBrokerAgent.getName()] = connectedBrokerAgent;
+ connectedBrokerInAgentList = true;
+
+ //
+ // Enqueue a notification of the new agent.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD));
+ eventImpl->setAgent(connectedBrokerAgent);
+ enqueueEventLH(ConsoleEvent(eventImpl.release()));
+ }
+ }
+
+ //
+ // Broadcast an agent locate request with our new criteria.
+ //
+ if (opened)
+ sendAgentLocate();
+}
+
+
+void ConsoleSessionImpl::open()
+{
+ if (opened)
+ throw QmfException("The session is already open");
+
+ // Establish messaging addresses
+ directBase = "qmf." + domain + ".direct";
+ topicBase = "qmf." + domain + ".topic";
+
+ string myKey("direct-console." + qpid::types::Uuid(true).str());
+
+ replyAddress = Address(topicBase + "/" + myKey + ";{node:{type:topic}}");
+
+ // Create AMQP session, receivers, and senders
+ session = connection.createSession();
+ Receiver directRx = session.createReceiver(replyAddress);
+ Receiver topicRx = session.createReceiver(topicBase + "/agent.#"); // TODO: be more discriminating
+ if (!strictSecurity) {
+ Receiver legacyRx = session.createReceiver("amq.direct/" + myKey + ";{node:{type:topic}}");
+ legacyRx.setCapacity(64);
+ directSender = session.createSender(directBase + ";{create:never,node:{type:topic}}");
+ directSender.setCapacity(128);
+ }
+
+ directRx.setCapacity(64);
+ topicRx.setCapacity(128);
+
+ topicSender = session.createSender(topicBase + ";{create:never,node:{type:topic}}");
+
+ topicSender.setCapacity(128);
+
+ // Start the receiver thread
+ threadCanceled = false;
+ thread = new qpid::sys::Thread(*this);
+
+ // Send an agent_locate to direct address 'broker' to identify the connected-broker-agent.
+ sendBrokerLocate();
+ if (agentQuery)
+ sendAgentLocate();
+
+ opened = true;
+}
+
+
+void ConsoleSessionImpl::close()
+{
+ if (!opened)
+ throw QmfException("The session is already closed");
+
+ // Stop and join the receiver thread
+ threadCanceled = true;
+ thread->join();
+ delete thread;
+
+ // Close the AMQP session
+ session.close();
+ opened = false;
+}
+
+
+bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
+{
+ uint64_t milliseconds = timeout.getMilliseconds();
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ if (eventQueue.empty() && milliseconds > 0)
+ cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
+ qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+
+ if (!eventQueue.empty()) {
+ event = eventQueue.front();
+ eventQueue.pop();
+ return true;
+ }
+
+ return false;
+}
+
+
+int ConsoleSessionImpl::pendingEvents() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventQueue.size();
+}
+
+
+uint32_t ConsoleSessionImpl::getAgentCount() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return agents.size();
+}
+
+
+Agent ConsoleSessionImpl::getAgent(uint32_t i) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ uint32_t count = 0;
+ for (map<string, Agent>::const_iterator iter = agents.begin(); iter != agents.end(); iter++)
+ if (count++ == i)
+ return iter->second;
+ throw IndexOutOfRange();
+}
+
+
+Subscription ConsoleSessionImpl::subscribe(const Query&, const string&, const string&)
+{
+ return Subscription();
+}
+
+
+Subscription ConsoleSessionImpl::subscribe(const string&, const string&, const string&)
+{
+ return Subscription();
+}
+
+
+void ConsoleSessionImpl::enqueueEvent(const ConsoleEvent& event)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ enqueueEventLH(event);
+}
+
+
+void ConsoleSessionImpl::enqueueEventLH(const ConsoleEvent& event)
+{
+ bool notify = eventQueue.empty();
+ eventQueue.push(event);
+ if (notify)
+ cond.notify();
+}
+
+
+void ConsoleSessionImpl::dispatch(Message msg)
+{
+ const Variant::Map& properties(msg.getProperties());
+ Variant::Map::const_iterator iter;
+ Variant::Map::const_iterator oiter;
+
+ oiter = properties.find(protocol::HEADER_KEY_OPCODE);
+ iter = properties.find(protocol::HEADER_KEY_APP_ID);
+ if (iter == properties.end())
+ iter = properties.find("app_id");
+ if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF && oiter != properties.end()) {
+ //
+ // Dispatch a QMFv2 formatted message
+ //
+ const string& opcode = oiter->second.asString();
+
+ iter = properties.find(protocol::HEADER_KEY_AGENT);
+ if (iter == properties.end()) {
+ QPID_LOG(trace, "Message received with no 'qmf.agent' header");
+ return;
+ }
+ const string& agentName = iter->second.asString();
+
+ Agent agent;
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<string, Agent>::iterator aIter = agents.find(agentName);
+ if (aIter != agents.end()) {
+ agent = aIter->second;
+ AgentImplAccess::get(agent).touch();
+ }
+ }
+
+ if (msg.getContentType() == "amqp/map" &&
+ (opcode == protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION || opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE)) {
+ //
+ // This is the one case where it's ok (necessary actually) to receive a QMFv2
+ // message from an unknown agent (how else are they going to get known?)
+ //
+ Variant::Map content;
+ decode(msg, content);
+ handleAgentUpdate(agentName, content, msg);
+ return;
+ }
+
+ if (!agent.isValid())
+ return;
+
+ AgentImpl& agentImpl(AgentImplAccess::get(agent));
+
+ if (msg.getContentType() == "amqp/map") {
+ Variant::Map content;
+ decode(msg, content);
+
+ if (opcode == protocol::HEADER_OPCODE_EXCEPTION) agentImpl.handleException(content, msg);
+ else if (opcode == protocol::HEADER_OPCODE_METHOD_RESPONSE) agentImpl.handleMethodResponse(content, msg);
+ else
+ QPID_LOG(error, "Received a map-formatted QMFv2 message with opcode=" << opcode);
+
+ return;
+ }
+
+ if (msg.getContentType() == "amqp/list") {
+ Variant::List content;
+ decode(msg, content);
+
+ if (opcode == protocol::HEADER_OPCODE_QUERY_RESPONSE) agentImpl.handleQueryResponse(content, msg);
+ else if (opcode == protocol::HEADER_OPCODE_DATA_INDICATION) agentImpl.handleDataIndication(content, msg);
+ else
+ QPID_LOG(error, "Received a list-formatted QMFv2 message with opcode=" << opcode);
+
+ return;
+ }
+ } else {
+ //
+ // Dispatch a QMFv1 formatted message
+ //
+ const string& body(msg.getContent());
+ if (body.size() < 8)
+ return;
+ qpid::management::Buffer buffer(const_cast<char*>(body.c_str()), body.size());
+
+ if (buffer.getOctet() != 'A') return;
+ if (buffer.getOctet() != 'M') return;
+ if (buffer.getOctet() != '2') return;
+ char v1Opcode(buffer.getOctet());
+ uint32_t seq(buffer.getLong());
+
+ if (v1Opcode == 's') handleV1SchemaResponse(buffer, seq, msg);
+ else {
+ QPID_LOG(trace, "Unknown or Unsupported QMFv1 opcode: " << v1Opcode);
+ }
+ }
+}
+
+
+void ConsoleSessionImpl::sendBrokerLocate()
+{
+ Message msg;
+ Variant::Map& headers(msg.getProperties());
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ msg.setReplyTo(replyAddress);
+ msg.setCorrelationId("broker-locate");
+ msg.setSubject("broker");
+
+ Sender sender = session.createSender(directBase + ";{create:never,node:{type:topic}}");
+ sender.send(msg);
+ sender.close();
+
+ QPID_LOG(trace, "SENT AgentLocate to broker");
+}
+
+
+void ConsoleSessionImpl::sendAgentLocate()
+{
+ Message msg;
+ Variant::Map& headers(msg.getProperties());
+ static const string subject("console.request.agent_locate");
+
+ headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST;
+ headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+ headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF;
+
+ msg.setReplyTo(replyAddress);
+ msg.setCorrelationId("agent-locate");
+ msg.setSubject(subject);
+ encode(agentQuery.getPredicate(), msg);
+
+ topicSender.send(msg);
+
+ QPID_LOG(trace, "SENT AgentLocate to=" << topicSender.getName() << "/" << subject);
+}
+
+
+void ConsoleSessionImpl::handleAgentUpdate(const string& agentName, const Variant::Map& content, const Message& msg)
+{
+ Variant::Map::const_iterator iter;
+ Agent agent;
+ uint32_t epoch(0);
+ string cid(msg.getCorrelationId());
+
+ iter = content.find("_values");
+ if (iter == content.end())
+ return;
+ const Variant::Map& in_attrs(iter->second.asMap());
+ Variant::Map attrs;
+
+ //
+ // Copy the map from the message to "attrs". Translate any old-style
+ // keys to their new key values in the process.
+ //
+ for (iter = in_attrs.begin(); iter != in_attrs.end(); iter++) {
+ if (iter->first == "epoch")
+ attrs[protocol::AGENT_ATTR_EPOCH] = iter->second;
+ else if (iter->first == "timestamp")
+ attrs[protocol::AGENT_ATTR_TIMESTAMP] = iter->second;
+ else if (iter->first == "heartbeat_interval")
+ attrs[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = iter->second;
+ else
+ attrs[iter->first] = iter->second;
+ }
+
+ iter = attrs.find(protocol::AGENT_ATTR_EPOCH);
+ if (iter != attrs.end())
+ epoch = iter->second.asUint32();
+
+ if (cid == "broker-locate") {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this));
+ for (iter = attrs.begin(); iter != attrs.end(); iter++)
+ if (iter->first != protocol::AGENT_ATTR_EPOCH)
+ impl->setAttribute(iter->first, iter->second);
+ agent = Agent(impl.release());
+ connectedBrokerAgent = agent;
+ if (!agentQuery || agentQuery.matchesPredicate(attrs)) {
+ connectedBrokerInAgentList = true;
+ agents[agentName] = agent;
+
+ //
+ // Enqueue a notification of the new agent.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD));
+ eventImpl->setAgent(agent);
+ enqueueEventLH(ConsoleEvent(eventImpl.release()));
+ }
+ return;
+ }
+
+ //
+ // Check this agent against the agent filter. Exit if it doesn't match.
+ // (only if this isn't the connected broker agent)
+ //
+ if (agentQuery && (!agentQuery.matchesPredicate(attrs)))
+ return;
+
+ QPID_LOG(trace, "RCVD AgentHeartbeat from an agent matching our filter: " << agentName);
+
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ map<string, Agent>::iterator aIter = agents.find(agentName);
+ if (aIter == agents.end()) {
+ //
+ // This is a new agent. We have no current record of its existence.
+ //
+ auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this));
+ for (iter = attrs.begin(); iter != attrs.end(); iter++)
+ if (iter->first != protocol::AGENT_ATTR_EPOCH)
+ impl->setAttribute(iter->first, iter->second);
+ agent = Agent(impl.release());
+ agents[agentName] = agent;
+
+ //
+ // Enqueue a notification of the new agent.
+ //
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD));
+ eventImpl->setAgent(agent);
+ enqueueEventLH(ConsoleEvent(eventImpl.release()));
+ } else {
+ //
+ // This is a refresh of an agent we are already tracking.
+ //
+ bool detectedRestart(false);
+ agent = aIter->second;
+ AgentImpl& impl(AgentImplAccess::get(agent));
+ impl.touch();
+ if (impl.getEpoch() != epoch) {
+ //
+ // The agent has restarted since the last time we heard from it.
+ // Enqueue a notification.
+ //
+ impl.setEpoch(epoch);
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_RESTART));
+ eventImpl->setAgent(agent);
+ enqueueEventLH(ConsoleEvent(eventImpl.release()));
+ detectedRestart = true;
+ }
+
+ iter = attrs.find(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP);
+ if (iter != attrs.end()) {
+ uint64_t ts(iter->second.asUint64());
+ if (ts > impl.getAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP).asUint64()) {
+ //
+ // The agent has added new schema entries since we last heard from it.
+ // Update the attribute and, if this doesn't accompany a restart, enqueue a notification.
+ //
+ if (!detectedRestart) {
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_SCHEMA_UPDATE));
+ eventImpl->setAgent(agent);
+ enqueueEventLH(ConsoleEvent(eventImpl.release()));
+ }
+ impl.setAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP, iter->second);
+ }
+ }
+ }
+ }
+}
+
+
+void ConsoleSessionImpl::handleV1SchemaResponse(qpid::management::Buffer& buffer, uint32_t, const Message&)
+{
+ QPID_LOG(trace, "RCVD V1SchemaResponse");
+ Schema schema(new SchemaImpl(buffer));
+ schemaCache->declareSchema(schema);
+}
+
+
+void ConsoleSessionImpl::periodicProcessing(uint64_t seconds)
+{
+ //
+ // The granularity of this timer is seconds. Don't waste time looking for work if
+ // it's been less than a second since we last visited.
+ //
+ if (seconds == lastVisit)
+ return;
+ lastVisit = seconds;
+
+ //
+ // Handle the aging of agent records
+ //
+ if (lastAgePass == 0)
+ lastAgePass = seconds;
+ if (seconds - lastAgePass >= 60) {
+ lastAgePass = seconds;
+ map<string, Agent> toDelete;
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ for (map<string, Agent>::iterator iter = agents.begin(); iter != agents.end(); iter++)
+ if ((iter->second.getName() != connectedBrokerAgent.getName()) &&
+ (AgentImplAccess::get(iter->second).age() > maxAgentAgeMinutes))
+ toDelete[iter->first] = iter->second;
+
+ for (map<string, Agent>::iterator iter = toDelete.begin(); iter != toDelete.end(); iter++) {
+ agents.erase(iter->first);
+ auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_DEL, AGENT_DEL_AGED));
+ eventImpl->setAgent(iter->second);
+ enqueueEventLH(eventImpl.release());
+ }
+ }
+}
+
+
+void ConsoleSessionImpl::run()
+{
+ QPID_LOG(debug, "ConsoleSession thread started");
+
+ try {
+ while (!threadCanceled) {
+ periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) /
+ qpid::sys::TIME_SEC);
+
+ Receiver rx;
+ bool valid = session.nextReceiver(rx, Duration::SECOND);
+ if (threadCanceled)
+ break;
+ if (valid) {
+ try {
+ dispatch(rx.fetch());
+ } catch (qpid::types::Exception& e) {
+ QPID_LOG(error, "Exception caught in message dispatch: " << e.what());
+ }
+ session.acknowledge();
+ }
+ }
+ } catch (qpid::types::Exception& e) {
+ QPID_LOG(error, "Exception caught in message thread - exiting: " << e.what());
+ enqueueEvent(ConsoleEvent(new ConsoleEventImpl(CONSOLE_THREAD_FAILED)));
+ }
+
+ QPID_LOG(debug, "ConsoleSession thread exiting");
+}
+
diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
new file mode 100644
index 0000000000..411b3f016a
--- /dev/null
+++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
@@ -0,0 +1,108 @@
+#ifndef _QMF_CONSOLE_SESSION_IMPL_H_
+#define _QMF_CONSOLE_SESSION_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/ConsoleSession.h"
+#include "qmf/AgentImpl.h"
+#include "qmf/SchemaId.h"
+#include "qmf/Schema.h"
+#include "qmf/ConsoleEventImpl.h"
+#include "qmf/SchemaCache.h"
+#include "qmf/Query.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Condition.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/management/Buffer.h"
+#include "qpid/types/Variant.h"
+#include <map>
+#include <queue>
+
+namespace qmf {
+ class ConsoleSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
+ public:
+ ~ConsoleSessionImpl();
+
+ //
+ // Methods from API handle
+ //
+ ConsoleSessionImpl(qpid::messaging::Connection& c, const std::string& o);
+ void setDomain(const std::string& d) { domain = d; }
+ void setAgentFilter(const std::string& f);
+ void open();
+ void close();
+ bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t);
+ int pendingEvents() const;
+ uint32_t getAgentCount() const;
+ Agent getAgent(uint32_t i) const;
+ Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; }
+ Subscription subscribe(const Query&, const std::string& agentFilter, const std::string& options);
+ Subscription subscribe(const std::string&, const std::string& agentFilter, const std::string& options);
+
+ protected:
+ mutable qpid::sys::Mutex lock;
+ qpid::sys::Condition cond;
+ qpid::messaging::Connection connection;
+ qpid::messaging::Session session;
+ qpid::messaging::Sender directSender;
+ qpid::messaging::Sender topicSender;
+ std::string domain;
+ uint32_t maxAgentAgeMinutes;
+ bool listenOnDirect;
+ bool strictSecurity;
+ Query agentQuery;
+ bool opened;
+ std::queue<ConsoleEvent> eventQueue;
+ qpid::sys::Thread* thread;
+ bool threadCanceled;
+ uint64_t lastVisit;
+ uint64_t lastAgePass;
+ std::map<std::string, Agent> agents;
+ Agent connectedBrokerAgent;
+ bool connectedBrokerInAgentList;
+ qpid::messaging::Address replyAddress;
+ std::string directBase;
+ std::string topicBase;
+ boost::shared_ptr<SchemaCache> schemaCache;
+
+ void enqueueEvent(const ConsoleEvent&);
+ void enqueueEventLH(const ConsoleEvent&);
+ void dispatch(qpid::messaging::Message);
+ void sendBrokerLocate();
+ void sendAgentLocate();
+ void handleAgentUpdate(const std::string&, const qpid::types::Variant::Map&, const qpid::messaging::Message&);
+ void handleV1SchemaResponse(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&);
+ void periodicProcessing(uint64_t);
+ void run();
+
+ friend class AgentImpl;
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/Data.cpp b/qpid/cpp/src/qmf/Data.cpp
new file mode 100644
index 0000000000..c503bab445
--- /dev/null
+++ b/qpid/cpp/src/qmf/Data.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/DataImpl.h"
+#include "qmf/DataAddrImpl.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/SchemaProperty.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<Data> PI;
+
+Data::Data(DataImpl* impl) { PI::ctor(*this, impl); }
+Data::Data(const Data& s) : qmf::Handle<DataImpl>() { PI::copy(*this, s); }
+Data::~Data() { PI::dtor(*this); }
+Data& Data::operator=(const Data& s) { return PI::assign(*this, s); }
+
+Data::Data(const Schema& s) { PI::ctor(*this, new DataImpl(s)); }
+void Data::setAddr(const DataAddr& a) { impl->setAddr(a); }
+void Data::setProperty(const string& k, const qpid::types::Variant& v) { impl->setProperty(k, v); }
+void Data::overwriteProperties(const qpid::types::Variant::Map& m) { impl->overwriteProperties(m); }
+bool Data::hasSchema() const { return impl->hasSchema(); }
+bool Data::hasAddr() const { return impl->hasAddr(); }
+const SchemaId& Data::getSchemaId() const { return impl->getSchemaId(); }
+const DataAddr& Data::getAddr() const { return impl->getAddr(); }
+const Variant& Data::getProperty(const string& k) const { return impl->getProperty(k); }
+const Variant::Map& Data::getProperties() const { return impl->getProperties(); }
+bool Data::hasAgent() const { return impl->hasAgent(); }
+const Agent& Data::getAgent() const { return impl->getAgent(); }
+
+
+void DataImpl::overwriteProperties(const Variant::Map& m) {
+ for (Variant::Map::const_iterator iter = m.begin(); iter != m.end(); iter++)
+ properties[iter->first] = iter->second;
+}
+
+const Variant& DataImpl::getProperty(const string& k) const {
+ Variant::Map::const_iterator iter = properties.find(k);
+ if (iter == properties.end())
+ throw KeyNotFound(k);
+ return iter->second;
+}
+
+
+DataImpl::DataImpl(const qpid::types::Variant::Map& map, const Agent& a)
+{
+ Variant::Map::const_iterator iter;
+
+ agent = a;
+
+ iter = map.find("_values");
+ if (iter != map.end())
+ properties = iter->second.asMap();
+
+ iter = map.find("_object_id");
+ if (iter != map.end())
+ dataAddr = DataAddr(new DataAddrImpl(iter->second.asMap()));
+
+ iter = map.find("_schema_id");
+ if (iter != map.end())
+ schemaId = SchemaId(new SchemaIdImpl(iter->second.asMap()));
+}
+
+
+Variant::Map DataImpl::asMap() const
+{
+ Variant::Map result;
+
+ result["_values"] = properties;
+
+ if (hasAddr()) {
+ const DataAddrImpl& aImpl(DataAddrImplAccess::get(getAddr()));
+ result["_object_id"] = aImpl.asMap();
+ }
+
+ if (hasSchema()) {
+ const SchemaIdImpl& sImpl(SchemaIdImplAccess::get(getSchemaId()));
+ result["_schema_id"] = sImpl.asMap();
+ }
+
+ return result;
+}
+
+
+void DataImpl::setProperty(const std::string& k, const qpid::types::Variant& v)
+{
+ if (schema.isValid()) {
+ //
+ // If we have a valid schema, make sure that the property is included in the
+ // schema and that the variant type is compatible with the schema type.
+ //
+ if (!SchemaImplAccess::get(schema).isValidProperty(k, v))
+ throw QmfException("Property '" + k + "' either not in the schema or value is of incompatible type");
+ }
+ properties[k] = v;
+}
+
+
+DataImpl& DataImplAccess::get(Data& item)
+{
+ return *item.impl;
+}
+
+
+const DataImpl& DataImplAccess::get(const Data& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/DataAddr.cpp b/qpid/cpp/src/qmf/DataAddr.cpp
new file mode 100644
index 0000000000..fb51d5787f
--- /dev/null
+++ b/qpid/cpp/src/qmf/DataAddr.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/DataAddrImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/DataAddr.h"
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<DataAddr> PI;
+
+DataAddr::DataAddr(DataAddrImpl* impl) { PI::ctor(*this, impl); }
+DataAddr::DataAddr(const DataAddr& s) : qmf::Handle<DataAddrImpl>() { PI::copy(*this, s); }
+DataAddr::~DataAddr() { PI::dtor(*this); }
+DataAddr& DataAddr::operator=(const DataAddr& s) { return PI::assign(*this, s); }
+
+bool DataAddr::operator==(const DataAddr& o) { return *impl == *o.impl; }
+bool DataAddr::operator<(const DataAddr& o) { return *impl < *o.impl; }
+
+DataAddr::DataAddr(const qpid::types::Variant::Map& m) { PI::ctor(*this, new DataAddrImpl(m)); }
+DataAddr::DataAddr(const string& n, const string& a, uint32_t e) { PI::ctor(*this, new DataAddrImpl(n, a, e)); }
+const string& DataAddr::getName() const { return impl->getName(); }
+const string& DataAddr::getAgentName() const { return impl->getAgentName(); }
+uint32_t DataAddr::getAgentEpoch() const { return impl->getAgentEpoch(); }
+Variant::Map DataAddr::asMap() const { return impl->asMap(); }
+
+bool DataAddrImpl::operator==(const DataAddrImpl& other)
+{
+ return
+ agentName == other.agentName &&
+ name == other.name &&
+ agentEpoch == other.agentEpoch;
+}
+
+
+bool DataAddrImpl::operator<(const DataAddrImpl& other)
+{
+ if (agentName < other.agentName) return true;
+ if (agentName > other.agentName) return false;
+ if (name < other.name) return true;
+ if (name > other.name) return false;
+ return agentEpoch < other.agentEpoch;
+}
+
+
+DataAddrImpl::DataAddrImpl(const Variant::Map& map)
+{
+ Variant::Map::const_iterator iter;
+
+ iter = map.find("_agent_name");
+ if (iter != map.end())
+ agentName = iter->second.asString();
+
+ iter = map.find("_object_name");
+ if (iter != map.end())
+ name = iter->second.asString();
+
+ iter = map.find("_agent_epoch");
+ if (iter != map.end())
+ agentEpoch = (uint32_t) iter->second.asUint64();
+}
+
+
+Variant::Map DataAddrImpl::asMap() const
+{
+ Variant::Map result;
+
+ result["_agent_name"] = agentName;
+ result["_object_name"] = name;
+ if (agentEpoch > 0)
+ result["_agent_epoch"] = agentEpoch;
+ return result;
+}
+
+
+DataAddrImpl& DataAddrImplAccess::get(DataAddr& item)
+{
+ return *item.impl;
+}
+
+
+const DataAddrImpl& DataAddrImplAccess::get(const DataAddr& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/DataAddrImpl.h b/qpid/cpp/src/qmf/DataAddrImpl.h
new file mode 100644
index 0000000000..3f9cae9453
--- /dev/null
+++ b/qpid/cpp/src/qmf/DataAddrImpl.h
@@ -0,0 +1,73 @@
+#ifndef _QMF_DATA_ADDR_IMPL_H_
+#define _QMF_DATA_ADDR_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/types/Variant.h"
+#include "qmf/DataAddr.h"
+
+namespace qmf {
+ class DataAddrImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Impl-only methods
+ //
+ void setName(const std::string& n) { name = n; }
+ void setAgent(const std::string& n, uint32_t e=0) { agentName = n; agentEpoch = e; }
+
+ //
+ // Methods from API handle
+ //
+ bool operator==(const DataAddrImpl&);
+ bool operator<(const DataAddrImpl&);
+ DataAddrImpl(const qpid::types::Variant::Map&);
+ DataAddrImpl(const std::string& _name, const std::string& _agentName, uint32_t _agentEpoch=0) :
+ agentName(_agentName), name(_name), agentEpoch(_agentEpoch) {}
+ const std::string& getName() const { return name; }
+ const std::string& getAgentName() const { return agentName; }
+ uint32_t getAgentEpoch() const { return agentEpoch; }
+ qpid::types::Variant::Map asMap() const;
+
+ private:
+ std::string agentName;
+ std::string name;
+ uint32_t agentEpoch;
+ };
+
+ struct DataAddrImplAccess
+ {
+ static DataAddrImpl& get(DataAddr&);
+ static const DataAddrImpl& get(const DataAddr&);
+ };
+
+ struct DataAddrCompare {
+ bool operator() (const DataAddr& lhs, const DataAddr& rhs) const
+ {
+ if (lhs.getName() != rhs.getName())
+ return lhs.getName() < rhs.getName();
+ return lhs.getAgentName() < rhs.getAgentName();
+ }
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/DataImpl.h b/qpid/cpp/src/qmf/DataImpl.h
new file mode 100644
index 0000000000..4ac3197da0
--- /dev/null
+++ b/qpid/cpp/src/qmf/DataImpl.h
@@ -0,0 +1,84 @@
+#ifndef _QMF_DATA_IMPL_H_
+#define _QMF_DATA_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/Data.h"
+#include "qmf/SchemaId.h"
+#include "qmf/Schema.h"
+#include "qmf/DataAddr.h"
+#include "qmf/Agent.h"
+#include "qmf/AgentSubscription.h"
+#include "qpid/types/Variant.h"
+
+namespace qmf {
+ class DataImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ DataImpl(const qpid::types::Variant::Map&, const Agent&);
+ qpid::types::Variant::Map asMap() const;
+ DataImpl() {}
+ void addSubscription(boost::shared_ptr<AgentSubscription>);
+ void delSubscription(uint64_t);
+ qpid::types::Variant::Map publishSubscription(uint64_t);
+ const Schema& getSchema() const { return schema; }
+
+ //
+ // Methods from API handle
+ //
+ DataImpl(const Schema& s) : schema(s) {}
+ void setAddr(const DataAddr& a) { dataAddr = a; }
+ void setProperty(const std::string& k, const qpid::types::Variant& v);
+ void overwriteProperties(const qpid::types::Variant::Map& m);
+ bool hasSchema() const { return schemaId.isValid() || schema.isValid(); }
+ bool hasAddr() const { return dataAddr.isValid(); }
+ const SchemaId& getSchemaId() const { if (schema.isValid()) return schema.getSchemaId(); else return schemaId; }
+ const DataAddr& getAddr() const { return dataAddr; }
+ const qpid::types::Variant& getProperty(const std::string& k) const;
+ const qpid::types::Variant::Map& getProperties() const { return properties; }
+ bool hasAgent() const { return agent.isValid(); }
+ const Agent& getAgent() const { return agent; }
+
+ private:
+ struct Subscr {
+ boost::shared_ptr<AgentSubscription> subscription;
+ qpid::types::Variant::Map deltas;
+ };
+ std::map<uint64_t, boost::shared_ptr<Subscr> > subscriptions;
+
+ SchemaId schemaId;
+ Schema schema;
+ DataAddr dataAddr;
+ qpid::types::Variant::Map properties;
+ Agent agent;
+ };
+
+ struct DataImplAccess
+ {
+ static DataImpl& get(Data&);
+ static const DataImpl& get(const Data&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/Expression.cpp b/qpid/cpp/src/qmf/Expression.cpp
new file mode 100644
index 0000000000..7d48678c15
--- /dev/null
+++ b/qpid/cpp/src/qmf/Expression.cpp
@@ -0,0 +1,441 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/exceptions.h"
+#include "qmf/Expression.h"
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using namespace qpid::types;
+
+Expression::Expression(const Variant::List& expr)
+{
+ static int level(0);
+ level++;
+ Variant::List::const_iterator iter(expr.begin());
+ string op(iter->asString());
+ iter++;
+
+ if (op == "not") logicalOp = LOGICAL_NOT;
+ else if (op == "and") logicalOp = LOGICAL_AND;
+ else if (op == "or") logicalOp = LOGICAL_OR;
+ else {
+ logicalOp = LOGICAL_ID;
+ if (op == "eq") boolOp = BOOL_EQ;
+ else if (op == "ne") boolOp = BOOL_NE;
+ else if (op == "lt") boolOp = BOOL_LT;
+ else if (op == "le") boolOp = BOOL_LE;
+ else if (op == "gt") boolOp = BOOL_GT;
+ else if (op == "ge") boolOp = BOOL_GE;
+ else if (op == "re_match") boolOp = BOOL_RE_MATCH;
+ else if (op == "exists") boolOp = BOOL_EXISTS;
+ else if (op == "true") boolOp = BOOL_TRUE;
+ else if (op == "false") boolOp = BOOL_FALSE;
+ else
+ throw QmfException("Invalid operator in predicate expression");
+ }
+
+ if (logicalOp == LOGICAL_ID) {
+ switch (boolOp) {
+ case BOOL_EQ:
+ case BOOL_NE:
+ case BOOL_LT:
+ case BOOL_LE:
+ case BOOL_GT:
+ case BOOL_GE:
+ case BOOL_RE_MATCH:
+ //
+ // Binary operator: get two operands.
+ //
+ operandCount = 2;
+ break;
+
+ case BOOL_EXISTS:
+ //
+ // Unary operator: get one operand.
+ //
+ operandCount = 1;
+ break;
+
+ case BOOL_TRUE:
+ case BOOL_FALSE:
+ //
+ // Literal operator: no operands.
+ //
+ operandCount = 0;
+ break;
+ }
+
+ for (int idx = 0; idx < operandCount; idx++) {
+ if (iter == expr.end())
+ throw QmfException("Too few operands for operation: " + op);
+ if (iter->getType() == VAR_STRING) {
+ quoted[idx] = false;
+ operands[idx] = *iter;
+ } else if (iter->getType() == VAR_LIST) {
+ const Variant::List& sublist(iter->asList());
+ Variant::List::const_iterator subIter(sublist.begin());
+ if (subIter != sublist.end() && subIter->asString() == "quote") {
+ quoted[idx] = true;
+ subIter++;
+ if (subIter != sublist.end()) {
+ operands[idx] = *subIter;
+ subIter++;
+ if (subIter != sublist.end())
+ throw QmfException("Extra tokens at end of 'quote'");
+ }
+ } else
+ throw QmfException("Expected '[quote, <token>]'");
+ } else
+ throw QmfException("Expected string or list as operand for: " + op);
+ iter++;
+ }
+
+ if (iter != expr.end())
+ throw QmfException("Too many operands for operation: " + op);
+
+ } else {
+ //
+ // This is a logical expression, collect sub-expressions
+ //
+ while (iter != expr.end()) {
+ if (iter->getType() != VAR_LIST)
+ throw QmfException("Operands of " + op + " must be lists");
+ expressionList.push_back(boost::shared_ptr<Expression>(new Expression(iter->asList())));
+ iter++;
+ }
+ }
+ level--;
+}
+
+
+bool Expression::evaluate(const Variant::Map& data) const
+{
+ list<boost::shared_ptr<Expression> >::const_iterator iter;
+
+ switch (logicalOp) {
+ case LOGICAL_ID:
+ return boolEval(data);
+
+ case LOGICAL_NOT:
+ for (iter = expressionList.begin(); iter != expressionList.end(); iter++)
+ if ((*iter)->evaluate(data))
+ return false;
+ return true;
+
+ case LOGICAL_AND:
+ for (iter = expressionList.begin(); iter != expressionList.end(); iter++)
+ if (!(*iter)->evaluate(data))
+ return false;
+ return true;
+
+ case LOGICAL_OR:
+ for (iter = expressionList.begin(); iter != expressionList.end(); iter++)
+ if ((*iter)->evaluate(data))
+ return true;
+ return false;
+ }
+
+ return false;
+}
+
+
+bool Expression::boolEval(const Variant::Map& data) const
+{
+ Variant val[2];
+ bool exists[2];
+
+ for (int idx = 0; idx < operandCount; idx++) {
+ if (quoted[idx]) {
+ exists[idx] = true;
+ val[idx] = operands[idx];
+ } else {
+ Variant::Map::const_iterator mIter(data.find(operands[idx].asString()));
+ if (mIter == data.end()) {
+ exists[idx] = false;
+ } else {
+ exists[idx] = true;
+ val[idx] = mIter->second;
+ }
+ }
+ }
+
+ switch (boolOp) {
+ case BOOL_EQ: return (exists[0] && exists[1] && (val[0].asString() == val[1].asString()));
+ case BOOL_NE: return (exists[0] && exists[1] && (val[0].asString() != val[1].asString()));
+ case BOOL_LT: return (exists[0] && exists[1] && lessThan(val[0], val[1]));
+ case BOOL_LE: return (exists[0] && exists[1] && lessEqual(val[0], val[1]));
+ case BOOL_GT: return (exists[0] && exists[1] && greaterThan(val[0], val[1]));
+ case BOOL_GE: return (exists[0] && exists[1] && greaterEqual(val[0], val[1]));
+ case BOOL_RE_MATCH: return false; // TODO
+ case BOOL_EXISTS: return exists[0];
+ case BOOL_TRUE: return true;
+ case BOOL_FALSE: return false;
+ }
+
+ return false;
+}
+
+bool Expression::lessThan(const Variant& left, const Variant& right) const
+{
+ switch (left.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ return left.asInt64() < right.asInt64();
+ case VAR_STRING:
+ try {
+ return left.asInt64() < right.asInt64();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ switch (right.getType()) {
+ case VAR_FLOAT: case VAR_DOUBLE:
+ return left.asDouble() < right.asDouble();
+ case VAR_STRING:
+ try {
+ return left.asDouble() < right.asDouble();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_STRING:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ try {
+ return left.asInt64() < right.asInt64();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ try {
+ return left.asDouble() < right.asDouble();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_STRING:
+ return left.asString() < right.asString();
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+bool Expression::lessEqual(const Variant& left, const Variant& right) const
+{
+ switch (left.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ return left.asInt64() <= right.asInt64();
+ case VAR_STRING:
+ try {
+ return left.asInt64() <= right.asInt64();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ switch (right.getType()) {
+ case VAR_FLOAT: case VAR_DOUBLE:
+ return left.asDouble() <= right.asDouble();
+ case VAR_STRING:
+ try {
+ return left.asDouble() <= right.asDouble();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_STRING:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ try {
+ return left.asInt64() <= right.asInt64();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ try {
+ return left.asDouble() <= right.asDouble();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_STRING:
+ return left.asString() <= right.asString();
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+bool Expression::greaterThan(const Variant& left, const Variant& right) const
+{
+ switch (left.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ return left.asInt64() > right.asInt64();
+ case VAR_STRING:
+ try {
+ return left.asInt64() > right.asInt64();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ switch (right.getType()) {
+ case VAR_FLOAT: case VAR_DOUBLE:
+ return left.asDouble() > right.asDouble();
+ case VAR_STRING:
+ try {
+ return left.asDouble() > right.asDouble();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_STRING:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ try {
+ return left.asInt64() > right.asInt64();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ try {
+ return left.asDouble() > right.asDouble();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_STRING:
+ return left.asString() > right.asString();
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+bool Expression::greaterEqual(const Variant& left, const Variant& right) const
+{
+ switch (left.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ return left.asInt64() >= right.asInt64();
+ case VAR_STRING:
+ try {
+ return left.asInt64() >= right.asInt64();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ switch (right.getType()) {
+ case VAR_FLOAT: case VAR_DOUBLE:
+ return left.asDouble() >= right.asDouble();
+ case VAR_STRING:
+ try {
+ return left.asDouble() >= right.asDouble();
+ } catch (std::exception&) {}
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case VAR_STRING:
+ switch (right.getType()) {
+ case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64:
+ case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64:
+ try {
+ return left.asInt64() >= right.asInt64();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_FLOAT: case VAR_DOUBLE:
+ try {
+ return left.asDouble() >= right.asDouble();
+ } catch (std::exception&) {}
+ break;
+
+ case VAR_STRING:
+ return left.asString() >= right.asString();
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
diff --git a/qpid/cpp/src/qmf/Expression.h b/qpid/cpp/src/qmf/Expression.h
new file mode 100644
index 0000000000..6fbfdbc4ba
--- /dev/null
+++ b/qpid/cpp/src/qmf/Expression.h
@@ -0,0 +1,73 @@
+#ifndef _QMF_EXPRESSION_H_
+#define _QMF_EXPRESSION_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/types/Variant.h"
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+
+ enum LogicalOp {
+ LOGICAL_ID = 1,
+ LOGICAL_NOT = 2,
+ LOGICAL_AND = 3,
+ LOGICAL_OR = 4
+ };
+
+ enum BooleanOp {
+ BOOL_EQ = 1,
+ BOOL_NE = 2,
+ BOOL_LT = 3,
+ BOOL_LE = 4,
+ BOOL_GT = 5,
+ BOOL_GE = 6,
+ BOOL_RE_MATCH = 7,
+ BOOL_EXISTS = 8,
+ BOOL_TRUE = 9,
+ BOOL_FALSE = 10
+ };
+
+ class Expression {
+ public:
+ Expression(const qpid::types::Variant::List& expr);
+ bool evaluate(const qpid::types::Variant::Map& data) const;
+ private:
+ LogicalOp logicalOp;
+ BooleanOp boolOp;
+ int operandCount;
+ qpid::types::Variant operands[2];
+ bool quoted[2];
+ std::list<boost::shared_ptr<Expression> > expressionList;
+
+ bool boolEval(const qpid::types::Variant::Map& data) const;
+ bool lessThan(const qpid::types::Variant& left, const qpid::types::Variant& right) const;
+ bool lessEqual(const qpid::types::Variant& left, const qpid::types::Variant& right) const;
+ bool greaterThan(const qpid::types::Variant& left, const qpid::types::Variant& right) const;
+ bool greaterEqual(const qpid::types::Variant& left, const qpid::types::Variant& right) const;
+ };
+
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/Hash.cpp b/qpid/cpp/src/qmf/Hash.cpp
new file mode 100644
index 0000000000..86738dda2f
--- /dev/null
+++ b/qpid/cpp/src/qmf/Hash.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/Hash.h"
+
+using namespace qmf;
+
+Hash::Hash()
+{
+ data[0] = 0x5A5A5A5A5A5A5A5ALL;
+ data[1] = 0x5A5A5A5A5A5A5A5ALL;
+}
+
+void Hash::update(const char* s, uint32_t len)
+{
+ uint64_t* first = &data[0];
+ uint64_t* second = &data[1];
+
+ for (uint32_t idx = 0; idx < len; idx++) {
+ uint64_t recycle = ((*second & 0xff00000000000000LL) >> 56);
+ *second = *second << 8;
+ *second |= ((*first & 0xFF00000000000000LL) >> 56);
+ *first = *first << 8;
+ *first = *first + (uint64_t) s[idx] + recycle;
+ }
+}
+
diff --git a/qpid/cpp/src/qmf/Hash.h b/qpid/cpp/src/qmf/Hash.h
new file mode 100644
index 0000000000..e1eff84117
--- /dev/null
+++ b/qpid/cpp/src/qmf/Hash.h
@@ -0,0 +1,44 @@
+#ifndef QMF_HASH_H
+#define QMF_HASH_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/types/Uuid.h"
+#include <string>
+
+namespace qmf {
+ class Hash {
+ public:
+ Hash();
+ qpid::types::Uuid asUuid() const { return qpid::types::Uuid((unsigned char*) data); }
+ void update(const char* s, uint32_t len);
+ void update(uint8_t v) { update((char*) &v, sizeof(v)); }
+ void update(uint32_t v) { update((char*) &v, sizeof(v)); }
+ void update(const std::string& v) { update(const_cast<char*>(v.c_str()), v.size()); }
+ void update(bool v) { update(uint8_t(v ? 1 : 0)); }
+
+ private:
+ uint64_t data[2];
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/PrivateImplRef.h b/qpid/cpp/src/qmf/PrivateImplRef.h
new file mode 100644
index 0000000000..960cbb2e09
--- /dev/null
+++ b/qpid/cpp/src/qmf/PrivateImplRef.h
@@ -0,0 +1,93 @@
+#ifndef QMF_PRIVATEIMPL_H
+#define QMF_PRIVATEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/ImportExport.h"
+#include "qpid/RefCounted.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qmf {
+
+/**
+ * Helper class to implement a class with a private, reference counted
+ * implementation and reference semantics.
+ *
+ * Such classes are used in the public API to hide implementation, they
+ * should. Example of use:
+ *
+ * === Foo.h
+ *
+ * template <class T> PrivateImplRef;
+ * class FooImpl;
+ *
+ * Foo : public Handle<FooImpl> {
+ * public:
+ * Foo(FooImpl* = 0);
+ * Foo(const Foo&);
+ * ~Foo();
+ * Foo& operator=(const Foo&);
+ *
+ * int fooDo(); // and other Foo functions...
+ *
+ * private:
+ * typedef FooImpl Impl;
+ * Impl* impl;
+ * friend class PrivateImplRef<Foo>;
+ *
+ * === Foo.cpp
+ *
+ * typedef PrivateImplRef<Foo> PI;
+ * Foo::Foo(FooImpl* p) { PI::ctor(*this, p); }
+ * Foo::Foo(const Foo& c) : Handle<FooImpl>() { PI::copy(*this, c); }
+ * Foo::~Foo() { PI::dtor(*this); }
+ * Foo& Foo::operator=(const Foo& c) { return PI::assign(*this, c); }
+ *
+ * int foo::fooDo() { return impl->fooDo(); }
+ *
+ */
+template <class T> class PrivateImplRef {
+ public:
+ typedef typename T::Impl Impl;
+ typedef boost::intrusive_ptr<Impl> intrusive_ptr;
+
+ /** Get the implementation pointer from a handle */
+ static intrusive_ptr get(const T& t) { return intrusive_ptr(t.impl); }
+
+ /** Set the implementation pointer in a handle */
+ static void set(T& t, const intrusive_ptr& p) {
+ if (t.impl == p) return;
+ if (t.impl) boost::intrusive_ptr_release(t.impl);
+ t.impl = p.get();
+ if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ }
+
+ // Helper functions to implement the ctor, dtor, copy, assign
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
+ static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
+};
+
+} // namespace qmf
+
+#endif /*!QMF_PRIVATEIMPL_H*/
diff --git a/qpid/cpp/src/qmf/Query.cpp b/qpid/cpp/src/qmf/Query.cpp
new file mode 100644
index 0000000000..ee8ca38e59
--- /dev/null
+++ b/qpid/cpp/src/qmf/Query.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/DataAddrImpl.h"
+#include "qmf/SchemaIdImpl.h"
+#include "qpid/messaging/AddressParser.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<Query> PI;
+
+Query::Query(QueryImpl* impl) { PI::ctor(*this, impl); }
+Query::Query(const Query& s) : qmf::Handle<QueryImpl>() { PI::copy(*this, s); }
+Query::~Query() { PI::dtor(*this); }
+Query& Query::operator=(const Query& s) { return PI::assign(*this, s); }
+
+Query::Query(QueryTarget t, const string& pr) { PI::ctor(*this, new QueryImpl(t, pr)); }
+Query::Query(QueryTarget t, const string& c, const string& p, const string& pr) { PI::ctor(*this, new QueryImpl(t, c, p, pr)); }
+Query::Query(QueryTarget t, const SchemaId& s, const string& pr) { PI::ctor(*this, new QueryImpl(t, s, pr)); }
+Query::Query(const DataAddr& a) { PI::ctor(*this, new QueryImpl(a)); }
+
+QueryTarget Query::getTarget() const { return impl->getTarget(); }
+const DataAddr& Query::getDataAddr() const { return impl->getDataAddr(); }
+const SchemaId& Query::getSchemaId() const { return impl->getSchemaId(); }
+void Query::setPredicate(const Variant::List& pr) { impl->setPredicate(pr); }
+const Variant::List& Query::getPredicate() const { return impl->getPredicate(); }
+bool Query::matchesPredicate(const qpid::types::Variant::Map& map) const { return impl->matchesPredicate(map); }
+
+
+QueryImpl::QueryImpl(const Variant::Map& map) : predicateCompiled(false)
+{
+ Variant::Map::const_iterator iter;
+
+ iter = map.find("_what");
+ if (iter == map.end())
+ throw QmfException("Query missing _what element");
+
+ const string& targetString(iter->second.asString());
+ if (targetString == "OBJECT") target = QUERY_OBJECT;
+ else if (targetString == "OBJECT_ID") target = QUERY_OBJECT_ID;
+ else if (targetString == "SCHEMA") target = QUERY_SCHEMA;
+ else if (targetString == "SCHEMA_ID") target = QUERY_SCHEMA_ID;
+ else
+ throw QmfException("Query with invalid _what value: " + targetString);
+
+ iter = map.find("_object_id");
+ if (iter != map.end()) {
+ auto_ptr<DataAddrImpl> addrImpl(new DataAddrImpl(iter->second.asMap()));
+ dataAddr = DataAddr(addrImpl.release());
+ }
+
+ iter = map.find("_schema_id");
+ if (iter != map.end()) {
+ auto_ptr<SchemaIdImpl> sidImpl(new SchemaIdImpl(iter->second.asMap()));
+ schemaId = SchemaId(sidImpl.release());
+ }
+
+ iter = map.find("_where");
+ if (iter != map.end())
+ predicate = iter->second.asList();
+}
+
+
+Variant::Map QueryImpl::asMap() const
+{
+ Variant::Map map;
+ string targetString;
+
+ switch (target) {
+ case QUERY_OBJECT : targetString = "OBJECT"; break;
+ case QUERY_OBJECT_ID : targetString = "OBJECT_ID"; break;
+ case QUERY_SCHEMA : targetString = "SCHEMA"; break;
+ case QUERY_SCHEMA_ID : targetString = "SCHEMA_ID"; break;
+ }
+
+ map["_what"] = targetString;
+
+ if (dataAddr.isValid())
+ map["_object_id"] = DataAddrImplAccess::get(dataAddr).asMap();
+
+ if (schemaId.isValid())
+ map["_schema_id"] = SchemaIdImplAccess::get(schemaId).asMap();
+
+ if (!predicate.empty())
+ map["_where"] = predicate;
+
+ return map;
+}
+
+
+bool QueryImpl::matchesPredicate(const qpid::types::Variant::Map& data) const
+{
+ if (predicate.empty())
+ return true;
+
+ if (!predicateCompiled) {
+ expression.reset(new Expression(predicate));
+ predicateCompiled = true;
+ }
+
+ return expression->evaluate(data);
+}
+
+
+void QueryImpl::parsePredicate(const string& pred)
+{
+ if (pred.empty())
+ return;
+
+ if (pred[0] == '[') {
+ //
+ // Parse this as an AddressParser list.
+ //
+ qpid::messaging::AddressParser parser(pred);
+ parser.parseList(predicate);
+ } else
+ throw QmfException("Invalid predicate format");
+}
+
+
+QueryImpl& QueryImplAccess::get(Query& item)
+{
+ return *item.impl;
+}
+
+
+const QueryImpl& QueryImplAccess::get(const Query& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/QueryImpl.h b/qpid/cpp/src/qmf/QueryImpl.h
new file mode 100644
index 0000000000..27ec427684
--- /dev/null
+++ b/qpid/cpp/src/qmf/QueryImpl.h
@@ -0,0 +1,77 @@
+#ifndef _QMF_QUERY_IMPL_H_
+#define _QMF_QUERY_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/Query.h"
+#include "qmf/DataAddr.h"
+#include "qmf/SchemaId.h"
+#include "qmf/Expression.h"
+#include "qpid/types/Variant.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+ class QueryImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ QueryImpl(const qpid::types::Variant::Map&);
+ qpid::types::Variant::Map asMap() const;
+
+ //
+ // Methods from API handle
+ //
+ QueryImpl(QueryTarget t, const std::string& pr) : target(t), predicateCompiled(false) { parsePredicate(pr); }
+ QueryImpl(QueryTarget t, const std::string& c, const std::string& p, const std::string& pr) :
+ target(t), schemaId(SCHEMA_TYPE_DATA, p, c), predicateCompiled(false) { parsePredicate(pr); }
+ QueryImpl(QueryTarget t, const SchemaId& s, const std::string& pr) :
+ target(t), schemaId(s), predicateCompiled(false) { parsePredicate(pr); }
+ QueryImpl(const DataAddr& a) : target(QUERY_OBJECT), dataAddr(a), predicateCompiled(false) {}
+
+ QueryTarget getTarget() const { return target; }
+ const DataAddr& getDataAddr() const { return dataAddr; }
+ const SchemaId& getSchemaId() const { return schemaId; }
+ void setPredicate(const qpid::types::Variant::List& pr) { predicate = pr; }
+ const qpid::types::Variant::List& getPredicate() const { return predicate; }
+ bool matchesPredicate(const qpid::types::Variant::Map& map) const;
+
+ private:
+ QueryTarget target;
+ SchemaId schemaId;
+ DataAddr dataAddr;
+ qpid::types::Variant::List predicate;
+ mutable bool predicateCompiled;
+ mutable boost::shared_ptr<Expression> expression;
+
+ void parsePredicate(const std::string& s);
+ };
+
+ struct QueryImplAccess
+ {
+ static QueryImpl& get(Query&);
+ static const QueryImpl& get(const Query&);
+ };
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/Schema.cpp b/qpid/cpp/src/qmf/Schema.cpp
new file mode 100644
index 0000000000..872aad724c
--- /dev/null
+++ b/qpid/cpp/src/qmf/Schema.cpp
@@ -0,0 +1,358 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/SchemaImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/SchemaTypes.h"
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/SchemaPropertyImpl.h"
+#include "qmf/SchemaMethodImpl.h"
+#include "qmf/Hash.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/Buffer.h"
+#include <list>
+
+using namespace std;
+using qpid::types::Variant;
+using namespace qmf;
+
+typedef PrivateImplRef<Schema> PI;
+
+Schema::Schema(SchemaImpl* impl) { PI::ctor(*this, impl); }
+Schema::Schema(const Schema& s) : qmf::Handle<SchemaImpl>() { PI::copy(*this, s); }
+Schema::~Schema() { PI::dtor(*this); }
+Schema& Schema::operator=(const Schema& s) { return PI::assign(*this, s); }
+
+Schema::Schema(int t, const string& p, const string& c) { PI::ctor(*this, new SchemaImpl(t, p, c)); }
+const SchemaId& Schema::getSchemaId() const { return impl->getSchemaId(); }
+void Schema::finalize() { impl->finalize(); }
+bool Schema::isFinalized() const { return impl->isFinalized(); }
+void Schema::addProperty(const SchemaProperty& p) { impl->addProperty(p); }
+void Schema::addMethod(const SchemaMethod& m) { impl->addMethod(m); }
+void Schema::setDesc(const string& d) { impl->setDesc(d); }
+const string& Schema::getDesc() const { return impl->getDesc(); }
+void Schema::setDefaultSeverity(int s) { impl->setDefaultSeverity(s); }
+int Schema::getDefaultSeverity() const { return impl->getDefaultSeverity(); }
+uint32_t Schema::getPropertyCount() const { return impl->getPropertyCount(); }
+SchemaProperty Schema::getProperty(uint32_t i) const { return impl->getProperty(i); }
+uint32_t Schema::getMethodCount() const { return impl->getMethodCount(); }
+SchemaMethod Schema::getMethod(uint32_t i) const { return impl->getMethod(i); }
+
+//========================================================================================
+// Impl Method Bodies
+//========================================================================================
+
+SchemaImpl::SchemaImpl(const Variant::Map& map) : finalized(false)
+{
+ Variant::Map::const_iterator iter;
+ Variant::List::const_iterator lIter;
+
+ iter = map.find("_schema_id");
+ if (iter == map.end())
+ throw QmfException("Schema map missing _schema_id element");
+ schemaId = SchemaId(new SchemaIdImpl(iter->second.asMap()));
+
+ iter = map.find("_desc");
+ if (iter != map.end())
+ description = iter->second.asString();
+
+ iter = map.find("_default_severity");
+ if (iter != map.end())
+ defaultSeverity = int(iter->second.asUint32());
+
+ iter = map.find("_properties");
+ if (iter != map.end()) {
+ const Variant::List& props(iter->second.asList());
+ for (lIter = props.begin(); lIter != props.end(); lIter++)
+ addProperty(SchemaProperty(new SchemaPropertyImpl(lIter->asMap())));
+ }
+
+ iter = map.find("_methods");
+ if (iter != map.end()) {
+ const Variant::List& meths(iter->second.asList());
+ for (lIter = meths.begin(); lIter != meths.end(); lIter++)
+ addMethod(SchemaMethod(new SchemaMethodImpl(lIter->asMap())));
+ }
+
+ finalized = true;
+}
+
+
+Variant::Map SchemaImpl::asMap() const
+{
+ Variant::Map map;
+ Variant::List propList;
+ Variant::List methList;
+
+ checkNotFinal();
+
+ map["_schema_id"] = SchemaIdImplAccess::get(schemaId).asMap();
+ if (!description.empty())
+ map["_desc"] = description;
+ if (schemaId.getType() == SCHEMA_TYPE_EVENT)
+ map["_default_severity"] = uint32_t(defaultSeverity);
+
+ for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++)
+ propList.push_back(SchemaPropertyImplAccess::get(*pIter).asMap());
+
+ for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++)
+ methList.push_back(SchemaMethodImplAccess::get(*mIter).asMap());
+
+ map["_properties"] = propList;
+ map["_methods"] = methList;
+ return map;
+}
+
+
+SchemaImpl::SchemaImpl(qpid::management::Buffer& buffer) : finalized(false)
+{
+ int schemaType;
+ string packageName;
+ string className;
+ uint8_t hash[16];
+
+ schemaType = int(buffer.getOctet());
+ buffer.getShortString(packageName);
+ buffer.getShortString(className);
+ buffer.getBin128(hash);
+ schemaId = SchemaId(schemaType, packageName, className);
+ schemaId.setHash(qpid::types::Uuid(hash));
+
+ if (schemaType == SCHEMA_TYPE_DATA) {
+ uint16_t propCount(buffer.getShort());
+ uint16_t statCount(buffer.getShort());
+ uint16_t methCount(buffer.getShort());
+ for (uint16_t idx = 0; idx < propCount + statCount; idx++)
+ addProperty(new SchemaPropertyImpl(buffer));
+ for (uint16_t idx = 0; idx < methCount; idx++)
+ addMethod(new SchemaMethodImpl(buffer));
+ }
+
+ finalized = true;
+}
+
+
+string SchemaImpl::asV1Content(uint32_t sequence) const
+{
+#define RAW_BUF_SIZE 65536
+ char rawBuf[RAW_BUF_SIZE];
+ qpid::management::Buffer buffer(rawBuf, RAW_BUF_SIZE);
+
+ //
+ // Encode the QMFv1 Header
+ //
+ buffer.putOctet('A');
+ buffer.putOctet('M');
+ buffer.putOctet('2');
+ buffer.putOctet('s');
+ buffer.putLong(sequence);
+
+ //
+ // Encode the common schema information
+ //
+ buffer.putOctet(uint8_t(schemaId.getType()));
+ buffer.putShortString(schemaId.getPackageName());
+ buffer.putShortString(schemaId.getName());
+ buffer.putBin128(schemaId.getHash().data());
+
+ if (schemaId.getType() == SCHEMA_TYPE_DATA) {
+ buffer.putShort(properties.size());
+ buffer.putShort(0);
+ buffer.putShort(methods.size());
+ for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++)
+ SchemaPropertyImplAccess::get(*pIter).encodeV1(buffer, false, false);
+ for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++)
+ SchemaMethodImplAccess::get(*mIter).encodeV1(buffer);
+ } else {
+ buffer.putShort(properties.size());
+ for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++)
+ SchemaPropertyImplAccess::get(*pIter).encodeV1(buffer, true, false);
+ }
+
+ return string(rawBuf, buffer.getPosition());
+}
+
+
+bool SchemaImpl::isValidProperty(const std::string& k, const Variant& v) const
+{
+ for (list<SchemaProperty>::const_iterator iter = properties.begin(); iter != properties.end(); iter++)
+ if (iter->getName() == k)
+ return (isCompatibleType(iter->getType(), v.getType()));
+ return false;
+}
+
+
+bool SchemaImpl::isValidMethodInArg(const std::string& m, const std::string& k, const Variant& v) const
+{
+ for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) {
+ if (mIter->getName() == m) {
+ uint32_t count(mIter->getArgumentCount());
+ for (uint32_t i = 0; i < count; i++) {
+ const SchemaProperty prop(mIter->getArgument(i));
+ if (prop.getName() == k) {
+ if (prop.getDirection() == DIR_IN || prop.getDirection() == DIR_IN_OUT)
+ return (isCompatibleType(prop.getType(), v.getType()));
+ else
+ return false;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+bool SchemaImpl::isValidMethodOutArg(const std::string& m, const std::string& k, const Variant& v) const
+{
+ for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) {
+ if (mIter->getName() == m) {
+ uint32_t count(mIter->getArgumentCount());
+ for (uint32_t i = 0; i < count; i++) {
+ const SchemaProperty prop(mIter->getArgument(i));
+ if (prop.getName() == k) {
+ if (prop.getDirection() == DIR_OUT || prop.getDirection() == DIR_IN_OUT)
+ return (isCompatibleType(prop.getType(), v.getType()));
+ else
+ return false;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+void SchemaImpl::finalize()
+{
+ Hash hash;
+
+ hash.update((uint8_t) schemaId.getType());
+ hash.update(schemaId.getPackageName());
+ hash.update(schemaId.getName());
+
+ for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++)
+ SchemaPropertyImplAccess::get(*pIter).updateHash(hash);
+ for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++)
+ SchemaMethodImplAccess::get(*mIter).updateHash(hash);
+
+ schemaId.setHash(hash.asUuid());
+ QPID_LOG(debug, "Schema Finalized: " << schemaId.getPackageName() << ":" << schemaId.getName() << ":" <<
+ schemaId.getHash());
+
+ finalized = true;
+}
+
+
+SchemaProperty SchemaImpl::getProperty(uint32_t i) const
+{
+ uint32_t count = 0;
+ for (list<SchemaProperty>::const_iterator iter = properties.begin(); iter != properties.end(); iter++)
+ if (count++ == i)
+ return *iter;
+ throw IndexOutOfRange();
+}
+
+
+SchemaMethod SchemaImpl::getMethod(uint32_t i) const
+{
+ uint32_t count = 0;
+ for (list<SchemaMethod>::const_iterator iter = methods.begin(); iter != methods.end(); iter++)
+ if (count++ == i)
+ return *iter;
+ throw IndexOutOfRange();
+}
+
+void SchemaImpl::checkFinal() const
+{
+ if (finalized)
+ throw QmfException("Modification of a finalized schema is forbidden");
+}
+
+
+void SchemaImpl::checkNotFinal() const
+{
+ if (!finalized)
+ throw QmfException("Schema is not yet finalized/registered");
+}
+
+
+bool SchemaImpl::isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const
+{
+ bool typeValid(false);
+
+ switch (qpidType) {
+ case qpid::types::VAR_VOID:
+ if (qmfType == SCHEMA_DATA_VOID)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_BOOL:
+ if (qmfType == SCHEMA_DATA_BOOL)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_UINT8:
+ case qpid::types::VAR_UINT16:
+ case qpid::types::VAR_UINT32:
+ case qpid::types::VAR_UINT64:
+ case qpid::types::VAR_INT8:
+ case qpid::types::VAR_INT16:
+ case qpid::types::VAR_INT32:
+ case qpid::types::VAR_INT64:
+ if (qmfType == SCHEMA_DATA_INT)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_FLOAT:
+ case qpid::types::VAR_DOUBLE:
+ if (qmfType == SCHEMA_DATA_FLOAT)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_STRING:
+ if (qmfType == SCHEMA_DATA_STRING)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_MAP:
+ if (qmfType == SCHEMA_DATA_MAP)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_LIST:
+ if (qmfType == SCHEMA_DATA_LIST)
+ typeValid = true;
+ break;
+ case qpid::types::VAR_UUID:
+ if (qmfType == SCHEMA_DATA_UUID)
+ typeValid = true;
+ break;
+ }
+
+ return typeValid;
+}
+
+
+SchemaImpl& SchemaImplAccess::get(Schema& item)
+{
+ return *item.impl;
+}
+
+
+const SchemaImpl& SchemaImplAccess::get(const Schema& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/SchemaCache.cpp b/qpid/cpp/src/qmf/SchemaCache.cpp
new file mode 100644
index 0000000000..74ca4044fd
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaCache.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/SchemaCache.h"
+#include "qmf/exceptions.h"
+
+using namespace std;
+using namespace qmf;
+
+bool SchemaCache::declareSchemaId(const SchemaId& id)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ SchemaMap::const_iterator iter = schemata.find(id);
+ if (iter == schemata.end()) {
+ schemata[id] = Schema();
+ return false;
+ }
+ return true;
+}
+
+
+void SchemaCache::declareSchema(const Schema& schema)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ SchemaMap::const_iterator iter = schemata.find(schema.getSchemaId());
+ if (iter == schemata.end() || !iter->second.isValid()) {
+ schemata[schema.getSchemaId()] = schema;
+
+ //
+ // If there are any threads blocking in SchemaCache::getSchema waiting for
+ // this schema, unblock them all now.
+ //
+ CondMap::iterator cIter = conditions.find(schema.getSchemaId());
+ if (cIter != conditions.end())
+ cIter->second->notifyAll();
+ }
+}
+
+
+bool SchemaCache::haveSchema(const SchemaId& id) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ SchemaMap::const_iterator iter = schemata.find(id);
+ return iter != schemata.end() && iter->second.isValid();
+}
+
+
+const Schema& SchemaCache::getSchema(const SchemaId& id, qpid::messaging::Duration timeout) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ SchemaMap::const_iterator iter = schemata.find(id);
+ if (iter != schemata.end() && iter->second.isValid())
+ return iter->second;
+
+ //
+ // The desired schema is not in the cache. Assume that the caller knows this and has
+ // sent a schema request to the remote agent and now wishes to wait until the schema
+ // information arrives.
+ //
+ CondMap::iterator cIter = conditions.find(id);
+ if (cIter == conditions.end())
+ conditions[id] = boost::shared_ptr<qpid::sys::Condition>(new qpid::sys::Condition());
+
+ uint64_t milliseconds = timeout.getMilliseconds();
+ conditions[id]->wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
+ qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+ iter = schemata.find(id);
+ if (iter != schemata.end() && iter->second.isValid())
+ return iter->second;
+
+ throw QmfException("Schema lookup timed out");
+}
+
diff --git a/qpid/cpp/src/qmf/SchemaCache.h b/qpid/cpp/src/qmf/SchemaCache.h
new file mode 100644
index 0000000000..a1f104233f
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaCache.h
@@ -0,0 +1,56 @@
+#ifndef QMF_SCHEMA_CACHE_H
+#define QMF_SCHEMA_CACHE_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/Schema.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Condition.h"
+#include "qpid/messaging/Duration.h"
+#include <string>
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+
+ class SchemaCache {
+ public:
+ SchemaCache() {}
+ ~SchemaCache() {}
+
+ bool declareSchemaId(const SchemaId&);
+ void declareSchema(const Schema&);
+ bool haveSchema(const SchemaId&) const;
+ const Schema& getSchema(const SchemaId&, qpid::messaging::Duration) const;
+
+ private:
+ mutable qpid::sys::Mutex lock;
+ typedef std::map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
+ typedef std::map<SchemaId, boost::shared_ptr<qpid::sys::Condition>, SchemaIdCompare> CondMap;
+ SchemaMap schemata;
+ mutable CondMap conditions;
+ };
+
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/SchemaId.cpp b/qpid/cpp/src/qmf/SchemaId.cpp
new file mode 100644
index 0000000000..25fa9915ae
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaId.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/PrivateImplRef.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<SchemaId> PI;
+
+SchemaId::SchemaId(SchemaIdImpl* impl) { PI::ctor(*this, impl); }
+SchemaId::SchemaId(const SchemaId& s) : qmf::Handle<SchemaIdImpl>() { PI::copy(*this, s); }
+SchemaId::~SchemaId() { PI::dtor(*this); }
+SchemaId& SchemaId::operator=(const SchemaId& s) { return PI::assign(*this, s); }
+
+SchemaId::SchemaId(int t, const string& p, const string& n) { PI::ctor(*this, new SchemaIdImpl(t, p, n)); }
+void SchemaId::setHash(const qpid::types::Uuid& h) { impl->setHash(h); }
+int SchemaId::getType() const { return impl->getType(); }
+const string& SchemaId::getPackageName() const { return impl->getPackageName(); }
+const string& SchemaId::getName() const { return impl->getName(); }
+const qpid::types::Uuid& SchemaId::getHash() const { return impl->getHash(); }
+
+
+SchemaIdImpl::SchemaIdImpl(const Variant::Map& map)
+{
+ Variant::Map::const_iterator iter;
+
+ iter = map.find("_package_name");
+ if (iter != map.end())
+ package = iter->second.asString();
+
+ iter = map.find("_class_name");
+ if (iter != map.end())
+ name = iter->second.asString();
+
+ iter = map.find("_type");
+ if (iter != map.end()) {
+ const string& stype = iter->second.asString();
+ if (stype == "_data")
+ sType = SCHEMA_TYPE_DATA;
+ else if (stype == "_event")
+ sType = SCHEMA_TYPE_EVENT;
+ }
+
+ iter = map.find("_hash");
+ if (iter != map.end())
+ hash = iter->second.asUuid();
+}
+
+
+Variant::Map SchemaIdImpl::asMap() const
+{
+ Variant::Map result;
+
+ result["_package_name"] = package;
+ result["_class_name"] = name;
+ if (sType == SCHEMA_TYPE_DATA)
+ result["_type"] = "_data";
+ else
+ result["_type"] = "_event";
+ if (!hash.isNull())
+ result["_hash"] = hash;
+ return result;
+}
+
+
+SchemaIdImpl& SchemaIdImplAccess::get(SchemaId& item)
+{
+ return *item.impl;
+}
+
+
+const SchemaIdImpl& SchemaIdImplAccess::get(const SchemaId& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/SchemaIdImpl.h b/qpid/cpp/src/qmf/SchemaIdImpl.h
new file mode 100644
index 0000000000..ae1a3d8d3b
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaIdImpl.h
@@ -0,0 +1,83 @@
+#ifndef _QMF_SCHEMA_ID_IMPL_H_
+#define _QMF_SCHEMA_ID_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/SchemaId.h"
+#include "qpid/types/Variant.h"
+#include "qpid/types/Uuid.h"
+#include <string>
+
+namespace qmf {
+ class SchemaIdImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ SchemaIdImpl(const qpid::types::Variant::Map&);
+ qpid::types::Variant::Map asMap() const;
+
+ //
+ // Methods from API handle
+ //
+ SchemaIdImpl(int t, const std::string& p, const std::string& n) : sType(t), package(p), name(n) {}
+ void setHash(const qpid::types::Uuid& h) { hash = h; }
+ int getType() const { return sType; }
+ const std::string& getPackageName() const { return package; }
+ const std::string& getName() const { return name; }
+ const qpid::types::Uuid& getHash() const { return hash; }
+
+ private:
+ int sType;
+ std::string package;
+ std::string name;
+ qpid::types::Uuid hash;
+ };
+
+ struct SchemaIdImplAccess
+ {
+ static SchemaIdImpl& get(SchemaId&);
+ static const SchemaIdImpl& get(const SchemaId&);
+ };
+
+ struct SchemaIdCompare {
+ bool operator() (const SchemaId& lhs, const SchemaId& rhs) const
+ {
+ if (lhs.getName() != rhs.getName())
+ return lhs.getName() < rhs.getName();
+ if (lhs.getPackageName() != rhs.getPackageName())
+ return lhs.getPackageName() < rhs.getPackageName();
+ return lhs.getHash() < rhs.getHash();
+ }
+ };
+
+ struct SchemaIdCompareNoHash {
+ bool operator() (const SchemaId& lhs, const SchemaId& rhs) const
+ {
+ if (lhs.getName() != rhs.getName())
+ return lhs.getName() < rhs.getName();
+ return lhs.getPackageName() < rhs.getPackageName();
+ }
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h
new file mode 100644
index 0000000000..1c88f87808
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaImpl.h
@@ -0,0 +1,95 @@
+#ifndef _QMF_SCHEMAIMPL_H_
+#define _QMF_SCHEMAIMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/SchemaTypes.h"
+#include "qmf/SchemaId.h"
+#include "qmf/Schema.h"
+#include "qmf/SchemaProperty.h"
+#include "qmf/SchemaMethod.h"
+#include <list>
+
+namespace qpid {
+namespace management {
+ class Buffer;
+}}
+
+namespace qmf {
+ class SchemaImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Impl-only public methods
+ //
+ SchemaImpl(const qpid::types::Variant::Map& m);
+ qpid::types::Variant::Map asMap() const;
+ SchemaImpl(qpid::management::Buffer& v1Buffer);
+ std::string asV1Content(uint32_t sequence) const;
+ bool isValidProperty(const std::string& k, const qpid::types::Variant& v) const;
+ bool isValidMethodInArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const;
+ bool isValidMethodOutArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const;
+
+ //
+ // Methods from API handle
+ //
+ SchemaImpl(int t, const std::string& p, const std::string& c) : schemaId(t, p, c), finalized(false) {}
+ const SchemaId& getSchemaId() const { checkNotFinal(); return schemaId; }
+
+ void finalize();
+ bool isFinalized() const { return finalized; }
+ void addProperty(const SchemaProperty& p) { checkFinal(); properties.push_back(p); }
+ void addMethod(const SchemaMethod& m) { checkFinal(); methods.push_back(m); }
+
+ void setDesc(const std::string& d) { description = d; }
+ const std::string& getDesc() const { return description; }
+
+ void setDefaultSeverity(int s) { checkFinal(); defaultSeverity = s; }
+ int getDefaultSeverity() const { return defaultSeverity; }
+
+ uint32_t getPropertyCount() const { return properties.size(); }
+ SchemaProperty getProperty(uint32_t i) const;
+
+ uint32_t getMethodCount() const { return methods.size(); }
+ SchemaMethod getMethod(uint32_t i) const;
+ private:
+ SchemaId schemaId;
+ int defaultSeverity;
+ std::string description;
+ bool finalized;
+ std::list<SchemaProperty> properties;
+ std::list<SchemaMethod> methods;
+
+ void checkFinal() const;
+ void checkNotFinal() const;
+ bool isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const;
+ };
+
+ struct SchemaImplAccess
+ {
+ static SchemaImpl& get(Schema&);
+ static const SchemaImpl& get(const Schema&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/SchemaMethod.cpp b/qpid/cpp/src/qmf/SchemaMethod.cpp
new file mode 100644
index 0000000000..e267878238
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaMethod.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/SchemaMethodImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/Hash.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/management/Buffer.h"
+
+using namespace std;
+using qpid::types::Variant;
+using namespace qmf;
+
+typedef PrivateImplRef<SchemaMethod> PI;
+
+SchemaMethod::SchemaMethod(SchemaMethodImpl* impl) { PI::ctor(*this, impl); }
+SchemaMethod::SchemaMethod(const SchemaMethod& s) : qmf::Handle<SchemaMethodImpl>() { PI::copy(*this, s); }
+SchemaMethod::~SchemaMethod() { PI::dtor(*this); }
+SchemaMethod& SchemaMethod::operator=(const SchemaMethod& s) { return PI::assign(*this, s); }
+
+SchemaMethod::SchemaMethod(const string& n, const string& o) { PI::ctor(*this, new SchemaMethodImpl(n, o)); }
+void SchemaMethod::setDesc(const string& d) { impl->setDesc(d); }
+void SchemaMethod::addArgument(const SchemaProperty& p) { impl->addArgument(p); }
+const string& SchemaMethod::getName() const { return impl->getName(); }
+const string& SchemaMethod::getDesc() const { return impl->getDesc(); }
+uint32_t SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); }
+SchemaProperty SchemaMethod::getArgument(uint32_t i) const { return impl->getArgument(i); }
+
+//========================================================================================
+// Impl Method Bodies
+//========================================================================================
+
+SchemaMethodImpl::SchemaMethodImpl(const string& n, const string& options) : name(n)
+{
+ if (!options.empty()) {
+ qpid::messaging::AddressParser parser = qpid::messaging::AddressParser(options);
+ Variant::Map optMap;
+ Variant::Map::iterator iter;
+
+ parser.parseMap(optMap);
+ iter = optMap.find("desc");
+ if (iter != optMap.end()) {
+ desc = iter->second.asString();
+ optMap.erase(iter);
+ }
+
+ if (!optMap.empty())
+ throw QmfException("Unrecognized option: " + optMap.begin()->first);
+ }
+}
+
+
+SchemaMethodImpl::SchemaMethodImpl(const qpid::types::Variant::Map& map)
+{
+ Variant::Map::const_iterator iter;
+ Variant::List::const_iterator lIter;
+
+ iter = map.find("_name");
+ if (iter == map.end())
+ throw QmfException("SchemaMethod without a _name element");
+ name = iter->second.asString();
+
+ iter = map.find("_desc");
+ if (iter != map.end())
+ desc = iter->second.asString();
+
+ iter = map.find("_arguments");
+ if (iter != map.end()) {
+ const Variant::List& argList(iter->second.asList());
+ for (lIter = argList.begin(); lIter != argList.end(); lIter++)
+ addArgument(SchemaProperty(new SchemaPropertyImpl(lIter->asMap())));
+ }
+}
+
+
+Variant::Map SchemaMethodImpl::asMap() const
+{
+ Variant::Map map;
+ Variant::List argList;
+
+ map["_name"] = name;
+
+ if (!desc.empty())
+ map["_desc"] = desc;
+
+ for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++)
+ argList.push_back(SchemaPropertyImplAccess::get(*iter).asMap());
+ map["_arguments"] = argList;
+
+ return map;
+}
+
+
+SchemaMethodImpl::SchemaMethodImpl(qpid::management::Buffer& buffer)
+{
+ Variant::Map::const_iterator iter;
+ Variant::Map argMap;
+
+ buffer.getMap(argMap);
+
+ iter = argMap.find("name");
+ if (iter == argMap.end())
+ throw QmfException("Received V1 Method without a name");
+ name = iter->second.asString();
+
+ iter = argMap.find("desc");
+ if (iter != argMap.end())
+ desc = iter->second.asString();
+
+ iter = argMap.find("argCount");
+ if (iter == argMap.end())
+ throw QmfException("Received V1 Method without argCount");
+
+ int64_t count = iter->second.asInt64();
+ for (int idx = 0; idx < count; idx++) {
+ SchemaProperty arg(new SchemaPropertyImpl(buffer));
+ addArgument(arg);
+ }
+}
+
+
+SchemaProperty SchemaMethodImpl::getArgument(uint32_t i) const
+{
+ uint32_t count = 0;
+ for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++)
+ if (count++ == i)
+ return *iter;
+
+ throw IndexOutOfRange();
+}
+
+
+void SchemaMethodImpl::updateHash(Hash& hash) const
+{
+ hash.update(name);
+ hash.update(desc);
+ for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++)
+ SchemaPropertyImplAccess::get(*iter).updateHash(hash);
+}
+
+
+void SchemaMethodImpl::encodeV1(qpid::management::Buffer& buffer) const
+{
+ Variant::Map map;
+
+ map["name"] = name;
+ map["argCount"] = (uint64_t) arguments.size();
+ if (!desc.empty())
+ map["desc"] = desc;
+
+ buffer.putMap(map);
+
+ for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++)
+ SchemaPropertyImplAccess::get(*iter).encodeV1(buffer, true, true);
+}
+
+
+SchemaMethodImpl& SchemaMethodImplAccess::get(SchemaMethod& item)
+{
+ return *item.impl;
+}
+
+
+const SchemaMethodImpl& SchemaMethodImplAccess::get(const SchemaMethod& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/SchemaMethodImpl.h b/qpid/cpp/src/qmf/SchemaMethodImpl.h
new file mode 100644
index 0000000000..930d48509c
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaMethodImpl.h
@@ -0,0 +1,75 @@
+#ifndef _QMF_SCHEMA_METHOD_IMPL_H_
+#define _QMF_SCHEMA_METHOD_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/SchemaTypes.h"
+#include "qmf/SchemaMethod.h"
+#include "qmf/SchemaPropertyImpl.h"
+#include "qpid/management/Buffer.h"
+#include <list>
+#include <string>
+
+namespace qpid {
+namespace management {
+ class Buffer;
+}}
+
+namespace qmf {
+ class Hash;
+ class SchemaMethodImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ SchemaMethodImpl(const qpid::types::Variant::Map& m);
+ SchemaMethodImpl(qpid::management::Buffer& v1Buffer);
+ qpid::types::Variant::Map asMap() const;
+ void updateHash(Hash&) const;
+ void encodeV1(qpid::management::Buffer&) const;
+
+ //
+ // Methods from API handle
+ //
+ SchemaMethodImpl(const std::string& n, const std::string& options);
+
+ void setDesc(const std::string& d) { desc = d; }
+ void addArgument(const SchemaProperty& p) { arguments.push_back(p); }
+ const std::string& getName() const { return name; }
+ const std::string& getDesc() const { return desc; }
+ uint32_t getArgumentCount() const { return arguments.size(); }
+ SchemaProperty getArgument(uint32_t i) const;
+
+ private:
+ std::string name;
+ std::string desc;
+ std::list<SchemaProperty> arguments;
+ };
+
+ struct SchemaMethodImplAccess
+ {
+ static SchemaMethodImpl& get(SchemaMethod&);
+ static const SchemaMethodImpl& get(const SchemaMethod&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/SchemaProperty.cpp b/qpid/cpp/src/qmf/SchemaProperty.cpp
new file mode 100644
index 0000000000..106127261b
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaProperty.cpp
@@ -0,0 +1,434 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qmf/SchemaPropertyImpl.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/SchemaTypes.h"
+#include "qmf/SchemaProperty.h"
+#include "qmf/Hash.h"
+#include "qpid/messaging/AddressParser.h"
+#include <list>
+#include <iostream>
+
+using namespace std;
+using qpid::types::Variant;
+using namespace qmf;
+
+typedef PrivateImplRef<SchemaProperty> PI;
+
+SchemaProperty::SchemaProperty(SchemaPropertyImpl* impl) { PI::ctor(*this, impl); }
+SchemaProperty::SchemaProperty(const SchemaProperty& s) : qmf::Handle<SchemaPropertyImpl>() { PI::copy(*this, s); }
+SchemaProperty::~SchemaProperty() { PI::dtor(*this); }
+SchemaProperty& SchemaProperty::operator=(const SchemaProperty& s) { return PI::assign(*this, s); }
+
+SchemaProperty::SchemaProperty(const string& n, int t, const string& o) { PI::ctor(*this, new SchemaPropertyImpl(n, t, o)); }
+
+void SchemaProperty::setAccess(int a) { impl->setAccess(a); }
+void SchemaProperty::setIndex(bool i) { impl->setIndex(i); }
+void SchemaProperty::setOptional(bool o) { impl->setOptional(o); }
+void SchemaProperty::setUnit(const string& u) { impl->setUnit(u); }
+void SchemaProperty::setDesc(const string& d) { impl->setDesc(d); }
+void SchemaProperty::setSubtype(const string& s) { impl->setSubtype(s); }
+void SchemaProperty::setDirection(int d) { impl->setDirection(d); }
+
+const string& SchemaProperty::getName() const { return impl->getName(); }
+int SchemaProperty::getType() const { return impl->getType(); }
+int SchemaProperty::getAccess() const { return impl->getAccess(); }
+bool SchemaProperty::isIndex() const { return impl->isIndex(); }
+bool SchemaProperty::isOptional() const { return impl->isOptional(); }
+const string& SchemaProperty::getUnit() const { return impl->getUnit(); }
+const string& SchemaProperty::getDesc() const { return impl->getDesc(); }
+const string& SchemaProperty::getSubtype() const { return impl->getSubtype(); }
+int SchemaProperty::getDirection() const { return impl->getDirection(); }
+
+//========================================================================================
+// Impl Method Bodies
+//========================================================================================
+
+SchemaPropertyImpl::SchemaPropertyImpl(const string& n, int t, const string options) :
+ name(n), dataType(t), access(ACCESS_READ_ONLY), index(false),
+ optional(false), direction(DIR_IN)
+{
+ if (!options.empty()) {
+ qpid::messaging::AddressParser parser = qpid::messaging::AddressParser(options);
+ Variant::Map optMap;
+ Variant::Map::iterator iter;
+
+ parser.parseMap(optMap);
+
+ iter = optMap.find("access");
+ if (iter != optMap.end()) {
+ const string& v(iter->second.asString());
+ if (v == "RC") access = ACCESS_READ_CREATE;
+ else if (v == "RO") access = ACCESS_READ_ONLY;
+ else if (v == "RW") access = ACCESS_READ_WRITE;
+ else
+ throw QmfException("Invalid value for 'access' option. Expected RC, RO, or RW");
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("index");
+ if (iter != optMap.end()) {
+ index = iter->second.asBool();
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("optional");
+ if (iter != optMap.end()) {
+ optional = iter->second.asBool();
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("unit");
+ if (iter != optMap.end()) {
+ unit = iter->second.asString();
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("desc");
+ if (iter != optMap.end()) {
+ desc = iter->second.asString();
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("subtype");
+ if (iter != optMap.end()) {
+ subtype = iter->second.asString();
+ optMap.erase(iter);
+ }
+
+ iter = optMap.find("dir");
+ if (iter != optMap.end()) {
+ const string& v(iter->second.asString());
+ if (v == "IN") direction = DIR_IN;
+ else if (v == "OUT") direction = DIR_OUT;
+ else if (v == "INOUT") direction = DIR_IN_OUT;
+ else
+ throw QmfException("Invalid value for 'dir' option. Expected IN, OUT, or INOUT");
+ optMap.erase(iter);
+ }
+
+ if (!optMap.empty())
+ throw QmfException("Unexpected option: " + optMap.begin()->first);
+ }
+}
+
+
+SchemaPropertyImpl::SchemaPropertyImpl(const Variant::Map& map) :
+ access(ACCESS_READ_ONLY), index(false), optional(false), direction(DIR_IN)
+{
+ Variant::Map::const_iterator iter;
+
+ iter = map.find("_name");
+ if (iter == map.end())
+ throw QmfException("SchemaProperty without a _name element");
+ name = iter->second.asString();
+
+ iter = map.find("_type");
+ if (iter == map.end())
+ throw QmfException("SchemaProperty without a _type element");
+ const string& ts(iter->second.asString());
+ if (ts == "TYPE_VOID") dataType = SCHEMA_DATA_VOID;
+ else if (ts == "TYPE_BOOL") dataType = SCHEMA_DATA_BOOL;
+ else if (ts == "TYPE_INT") dataType = SCHEMA_DATA_INT;
+ else if (ts == "TYPE_FLOAT") dataType = SCHEMA_DATA_FLOAT;
+ else if (ts == "TYPE_STRING") dataType = SCHEMA_DATA_STRING;
+ else if (ts == "TYPE_MAP") dataType = SCHEMA_DATA_MAP;
+ else if (ts == "TYPE_LIST") dataType = SCHEMA_DATA_LIST;
+ else if (ts == "TYPE_UUID") dataType = SCHEMA_DATA_UUID;
+ else
+ throw QmfException("SchemaProperty with an invalid type code: " + ts);
+
+ iter = map.find("_access");
+ if (iter != map.end()) {
+ const string& as(iter->second.asString());
+ if (as == "RO") access = ACCESS_READ_ONLY;
+ else if (as == "RC") access = ACCESS_READ_CREATE;
+ else if (as == "RW") access = ACCESS_READ_WRITE;
+ else
+ throw QmfException("SchemaProperty with an invalid access code: " + as);
+ }
+
+ iter = map.find("_unit");
+ if (iter != map.end())
+ unit = iter->second.asString();
+
+ iter = map.find("_dir");
+ if (iter != map.end()) {
+ const string& ds(iter->second.asString());
+ if (ds == "I") direction = DIR_IN;
+ else if (ds == "O") direction = DIR_OUT;
+ else if (ds == "IO") direction = DIR_IN_OUT;
+ else
+ throw QmfException("SchemaProperty with an invalid direction code: " + ds);
+ }
+
+ iter = map.find("_desc");
+ if (iter != map.end())
+ desc = iter->second.asString();
+
+ iter = map.find("_index");
+ if (iter != map.end())
+ index = iter->second.asBool();
+
+ iter = map.find("_subtype");
+ if (iter != map.end())
+ subtype = iter->second.asString();
+}
+
+
+Variant::Map SchemaPropertyImpl::asMap() const
+{
+ Variant::Map map;
+ string ts;
+
+ map["_name"] = name;
+
+ switch (dataType) {
+ case SCHEMA_DATA_VOID: ts = "TYPE_VOID"; break;
+ case SCHEMA_DATA_BOOL: ts = "TYPE_BOOL"; break;
+ case SCHEMA_DATA_INT: ts = "TYPE_INT"; break;
+ case SCHEMA_DATA_FLOAT: ts = "TYPE_FLOAT"; break;
+ case SCHEMA_DATA_STRING: ts = "TYPE_STRING"; break;
+ case SCHEMA_DATA_MAP: ts = "TYPE_MAP"; break;
+ case SCHEMA_DATA_LIST: ts = "TYPE_LIST"; break;
+ case SCHEMA_DATA_UUID: ts = "TYPE_UUID"; break;
+ }
+ map["_type"] = ts;
+
+ switch (access) {
+ case ACCESS_READ_ONLY: ts = "RO"; break;
+ case ACCESS_READ_CREATE: ts = "RC"; break;
+ case ACCESS_READ_WRITE: ts = "RW"; break;
+ }
+ map["_access"] = ts;
+
+ if (!unit.empty())
+ map["_unit"] = unit;
+
+ switch (direction) {
+ case DIR_IN: ts = "I"; break;
+ case DIR_OUT: ts = "O"; break;
+ case DIR_IN_OUT: ts = "IO"; break;
+ }
+ map["_dir"] = ts;
+
+ if (!desc.empty())
+ map["_desc"] = desc;
+
+ if (index)
+ map["_index"] = true;
+
+ if (!subtype.empty())
+ map["_subtype"] = subtype;
+
+ return map;
+}
+
+
+SchemaPropertyImpl::SchemaPropertyImpl(qpid::management::Buffer& buffer) :
+ access(ACCESS_READ_ONLY), index(false), optional(false), direction(DIR_IN)
+{
+ Variant::Map::const_iterator iter;
+ Variant::Map pmap;
+
+ buffer.getMap(pmap);
+ iter = pmap.find("name");
+ if (iter == pmap.end())
+ throw QmfException("Received V1 Schema property without a name");
+ name = iter->second.asString();
+
+ iter = pmap.find("type");
+ if (iter == pmap.end())
+ throw QmfException("Received V1 Schema property without a type");
+ fromV1TypeCode(iter->second.asInt8());
+
+ iter = pmap.find("unit");
+ if (iter != pmap.end())
+ unit = iter->second.asString();
+
+ iter = pmap.find("desc");
+ if (iter != pmap.end())
+ desc = iter->second.asString();
+
+ iter = pmap.find("access");
+ if (iter != pmap.end()) {
+ int8_t val = iter->second.asInt8();
+ if (val < 1 || val > 3)
+ throw QmfException("Received V1 Schema property with invalid 'access' code");
+ access = val;
+ }
+
+ iter = pmap.find("index");
+ if (iter != pmap.end())
+ index = iter->second.asInt64() != 0;
+
+ iter = pmap.find("optional");
+ if (iter != pmap.end())
+ optional = iter->second.asInt64() != 0;
+
+ iter = pmap.find("dir");
+ if (iter != pmap.end()) {
+ string dirStr(iter->second.asString());
+ if (dirStr == "I") direction = DIR_IN;
+ else if (dirStr == "O") direction = DIR_OUT;
+ else if (dirStr == "IO") direction = DIR_IN_OUT;
+ else
+ throw QmfException("Received V1 Schema property with invalid 'dir' code");
+ }
+}
+
+
+void SchemaPropertyImpl::updateHash(Hash& hash) const
+{
+ hash.update(name);
+ hash.update((uint8_t) dataType);
+ hash.update(subtype);
+ hash.update((uint8_t) access);
+ hash.update(index);
+ hash.update(optional);
+ hash.update(unit);
+ hash.update(desc);
+ hash.update((uint8_t) direction);
+}
+
+
+void SchemaPropertyImpl::encodeV1(qpid::management::Buffer& buffer, bool isArg, bool isMethodArg) const
+{
+ Variant::Map pmap;
+
+ pmap["name"] = name;
+ pmap["type"] = v1TypeCode();
+ if (!unit.empty())
+ pmap["unit"] = unit;
+ if (!desc.empty())
+ pmap["desc"] = desc;
+ if (!isArg) {
+ pmap["access"] = access;
+ pmap["index"] = index ? 1 : 0;
+ pmap["optional"] = optional ? 1 : 0;
+ } else {
+ if (isMethodArg) {
+ string dirStr;
+ switch (direction) {
+ case DIR_IN : dirStr = "I"; break;
+ case DIR_OUT : dirStr = "O"; break;
+ case DIR_IN_OUT : dirStr = "IO"; break;
+ }
+ pmap["dir"] = dirStr;
+ }
+ }
+
+ buffer.putMap(pmap);
+}
+
+
+uint8_t SchemaPropertyImpl::v1TypeCode() const
+{
+ switch (dataType) {
+ case SCHEMA_DATA_VOID: return 1;
+ case SCHEMA_DATA_BOOL: return 11;
+ case SCHEMA_DATA_INT:
+ if (subtype == "timestamp") return 8;
+ if (subtype == "duration") return 9;
+ return 19;
+ case SCHEMA_DATA_FLOAT: return 13;
+ case SCHEMA_DATA_STRING: return 7;
+ case SCHEMA_DATA_LIST: return 21;
+ case SCHEMA_DATA_UUID: return 14;
+ case SCHEMA_DATA_MAP:
+ if (subtype == "reference") return 10;
+ if (subtype == "data") return 20;
+ return 15;
+ }
+
+ return 1;
+}
+
+void SchemaPropertyImpl::fromV1TypeCode(int8_t code)
+{
+ switch (code) {
+ case 1: // U8
+ case 2: // U16
+ case 3: // U32
+ case 4: // U64
+ dataType = SCHEMA_DATA_INT;
+ break;
+ case 6: // SSTR
+ case 7: // LSTR
+ dataType = SCHEMA_DATA_STRING;
+ break;
+ case 8: // ABSTIME
+ dataType = SCHEMA_DATA_INT;
+ subtype = "timestamp";
+ break;
+ case 9: // DELTATIME
+ dataType = SCHEMA_DATA_INT;
+ subtype = "duration";
+ break;
+ case 10: // REF
+ dataType = SCHEMA_DATA_MAP;
+ subtype = "reference";
+ break;
+ case 11: // BOOL
+ dataType = SCHEMA_DATA_BOOL;
+ break;
+ case 12: // FLOAT
+ case 13: // DOUBLE
+ dataType = SCHEMA_DATA_FLOAT;
+ break;
+ case 14: // UUID
+ dataType = SCHEMA_DATA_UUID;
+ break;
+ case 15: // FTABLE
+ dataType = SCHEMA_DATA_MAP;
+ break;
+ case 16: // S8
+ case 17: // S16
+ case 18: // S32
+ case 19: // S64
+ dataType = SCHEMA_DATA_INT;
+ break;
+ case 20: // OBJECT
+ dataType = SCHEMA_DATA_MAP;
+ subtype = "data";
+ break;
+ case 21: // LIST
+ case 22: // ARRAY
+ dataType = SCHEMA_DATA_LIST;
+ break;
+ default:
+ throw QmfException("Received V1 schema with an unknown data type");
+ }
+}
+
+
+SchemaPropertyImpl& SchemaPropertyImplAccess::get(SchemaProperty& item)
+{
+ return *item.impl;
+}
+
+
+const SchemaPropertyImpl& SchemaPropertyImplAccess::get(const SchemaProperty& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/SchemaPropertyImpl.h b/qpid/cpp/src/qmf/SchemaPropertyImpl.h
new file mode 100644
index 0000000000..cdfc29066f
--- /dev/null
+++ b/qpid/cpp/src/qmf/SchemaPropertyImpl.h
@@ -0,0 +1,93 @@
+#ifndef _QMF_SCHEMA_PROPERTY_IMPL_H_
+#define _QMF_SCHEMA_PROPERTY_IMPL_H_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/SchemaTypes.h"
+#include "qmf/SchemaProperty.h"
+#include "qpid/types/Variant.h"
+#include "qpid/management/Buffer.h"
+
+namespace qpid {
+namespace management {
+ class Buffer;
+}}
+
+namespace qmf {
+ class Hash;
+ class SchemaPropertyImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ SchemaPropertyImpl(const qpid::types::Variant::Map& m);
+ SchemaPropertyImpl(qpid::management::Buffer& v1Buffer);
+ qpid::types::Variant::Map asMap() const;
+ void updateHash(Hash&) const;
+ void encodeV1(qpid::management::Buffer&, bool isArg, bool isMethodArg) const;
+
+ //
+ // Methods from API handle
+ //
+ SchemaPropertyImpl(const std::string& n, int t, const std::string o);
+ void setAccess(int a) { access = a; }
+ void setIndex(bool i) { index = i; }
+ void setOptional(bool o) { optional = o; }
+ void setUnit(const std::string& u) { unit = u; }
+ void setDesc(const std::string& d) { desc = d; }
+ void setSubtype(const std::string& s) { subtype = s; }
+ void setDirection(int d) { direction = d; }
+
+ const std::string& getName() const { return name; }
+ int getType() const { return dataType; }
+ int getAccess() const { return access; }
+ bool isIndex() const { return index; }
+ bool isOptional() const { return optional; }
+ const std::string& getUnit() const { return unit; }
+ const std::string& getDesc() const { return desc; }
+ const std::string& getSubtype() const { return subtype; }
+ int getDirection() const { return direction; }
+
+ private:
+ std::string name;
+ int dataType;
+ std::string subtype;
+ int access;
+ bool index;
+ bool optional;
+ std::string unit;
+ std::string desc;
+ int direction;
+
+ uint8_t v1TypeCode() const;
+ void fromV1TypeCode(int8_t);
+ };
+
+ struct SchemaPropertyImplAccess
+ {
+ static SchemaPropertyImpl& get(SchemaProperty&);
+ static const SchemaPropertyImpl& get(const SchemaProperty&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/Subscription.cpp b/qpid/cpp/src/qmf/Subscription.cpp
new file mode 100644
index 0000000000..73afc8c79d
--- /dev/null
+++ b/qpid/cpp/src/qmf/Subscription.cpp
@@ -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.
+ *
+ */
+
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/SubscriptionImpl.h"
+#include "qmf/DataImpl.h"
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+
+typedef PrivateImplRef<Subscription> PI;
+
+Subscription::Subscription(SubscriptionImpl* impl) { PI::ctor(*this, impl); }
+Subscription::Subscription(const Subscription& s) : qmf::Handle<SubscriptionImpl>() { PI::copy(*this, s); }
+Subscription::~Subscription() { PI::dtor(*this); }
+Subscription& Subscription::operator=(const Subscription& s) { return PI::assign(*this, s); }
+
+void Subscription::cancel() { impl->cancel(); }
+bool Subscription::isActive() const { return impl->isActive(); }
+void Subscription::lock() { impl->lock(); }
+void Subscription::unlock() { impl->unlock(); }
+uint32_t Subscription::getDataCount() const { return impl->getDataCount(); }
+Data Subscription::getData(uint32_t i) const { return impl->getData(i); }
+
+
+void SubscriptionImpl::cancel()
+{
+}
+
+
+bool SubscriptionImpl::isActive() const
+{
+ return false;
+}
+
+
+void SubscriptionImpl::lock()
+{
+}
+
+
+void SubscriptionImpl::unlock()
+{
+}
+
+
+uint32_t SubscriptionImpl::getDataCount() const
+{
+ return 0;
+}
+
+
+Data SubscriptionImpl::getData(uint32_t) const
+{
+ return Data();
+}
+
+
+SubscriptionImpl& SubscriptionImplAccess::get(Subscription& item)
+{
+ return *item.impl;
+}
+
+
+const SubscriptionImpl& SubscriptionImplAccess::get(const Subscription& item)
+{
+ return *item.impl;
+}
diff --git a/qpid/cpp/src/qmf/SubscriptionImpl.h b/qpid/cpp/src/qmf/SubscriptionImpl.h
new file mode 100644
index 0000000000..053e3cd00e
--- /dev/null
+++ b/qpid/cpp/src/qmf/SubscriptionImpl.h
@@ -0,0 +1,57 @@
+#ifndef _QMF_SUBSCRIPTION_IMPL_H_
+#define _QMF_SUBSCRIPTION_IMPL_H_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qmf/Subscription.h"
+
+namespace qmf {
+ class SubscriptionImpl : public virtual qpid::RefCounted {
+ public:
+ //
+ // Public impl-only methods
+ //
+ SubscriptionImpl(int p) : placeholder(p) {}
+ ~SubscriptionImpl();
+
+ //
+ // Methods from API handle
+ //
+ void cancel();
+ bool isActive() const;
+ void lock();
+ void unlock();
+ uint32_t getDataCount() const;
+ Data getData(uint32_t) const;
+
+ private:
+ int placeholder;
+ };
+
+ struct SubscriptionImplAccess
+ {
+ static SubscriptionImpl& get(Subscription&);
+ static const SubscriptionImpl& get(const Subscription&);
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/agentCapability.h b/qpid/cpp/src/qmf/agentCapability.h
new file mode 100644
index 0000000000..6a3f6f8534
--- /dev/null
+++ b/qpid/cpp/src/qmf/agentCapability.h
@@ -0,0 +1,39 @@
+#ifndef QMF_AGENT_CAPABILITY_H
+#define QMF_AGENT_CAPABILITY_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qmf {
+
+ /**
+ * Legacy (Qpid 0.7 C++ Agent, 0.7 Broker Agent) capabilities
+ */
+ const uint32_t AGENT_CAPABILITY_LEGACY = 0;
+
+ /**
+ * Qpid 0.8 QMFv2 capabilities
+ */
+ const uint32_t AGENT_CAPABILITY_0_8 = 1;
+ const uint32_t AGENT_CAPABILITY_V2_SCHEMA = 1;
+ const uint32_t AGENT_CAPABILITY_AGENT_PREDICATE = 1;
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/constants.cpp b/qpid/cpp/src/qmf/constants.cpp
new file mode 100644
index 0000000000..6e2fd935a9
--- /dev/null
+++ b/qpid/cpp/src/qmf/constants.cpp
@@ -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.
+ *
+ */
+
+#include "constants.h"
+
+using namespace std;
+using namespace qmf;
+
+/**
+ * Header key strings
+ */
+const string protocol::HEADER_KEY_APP_ID = "x-amqp-0-10.app-id";
+const string protocol::HEADER_KEY_METHOD = "method";
+const string protocol::HEADER_KEY_OPCODE = "qmf.opcode";
+const string protocol::HEADER_KEY_AGENT = "qmf.agent";
+const string protocol::HEADER_KEY_CONTENT = "qmf.content";
+const string protocol::HEADER_KEY_PARTIAL = "partial";
+
+/**
+ * Header values per-key
+ */
+const string protocol::HEADER_APP_ID_QMF = "qmf2";
+
+const string protocol::HEADER_METHOD_REQUEST = "request";
+const string protocol::HEADER_METHOD_RESPONSE = "response";
+const string protocol::HEADER_METHOD_INDICATION = "indication";
+
+const string protocol::HEADER_OPCODE_EXCEPTION = "_exception";
+const string protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST = "_agent_locate_request";
+const string protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE = "_agent_locate_response";
+const string protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION = "_agent_heartbeat_indication";
+const string protocol::HEADER_OPCODE_QUERY_REQUEST = "_query_request";
+const string protocol::HEADER_OPCODE_QUERY_RESPONSE = "_query_response";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_REQUEST = "_subscribe_request";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_RESPONSE = "_subscribe_response";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION = "_subscribe_cancel_indication";
+const string protocol::HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION = "_subscribe_refresh_indication";
+const string protocol::HEADER_OPCODE_DATA_INDICATION = "_data_indication";
+const string protocol::HEADER_OPCODE_METHOD_REQUEST = "_method_request";
+const string protocol::HEADER_OPCODE_METHOD_RESPONSE = "_method_response";
+
+const string protocol::HEADER_CONTENT_SCHEMA_ID = "_schema_id";
+const string protocol::HEADER_CONTENT_SCHEMA_CLASS = "_schema_class";
+const string protocol::HEADER_CONTENT_OBJECT_ID = "_object_id";
+const string protocol::HEADER_CONTENT_DATA = "_data";
+const string protocol::HEADER_CONTENT_EVENT = "_event";
+const string protocol::HEADER_CONTENT_QUERY = "_query";
+
+/**
+ * Keywords for Agent attributes
+ */
+const string protocol::AGENT_ATTR_VENDOR = "_vendor";
+const string protocol::AGENT_ATTR_PRODUCT = "_product";
+const string protocol::AGENT_ATTR_INSTANCE = "_instance";
+const string protocol::AGENT_ATTR_NAME = "_name";
+const string protocol::AGENT_ATTR_TIMESTAMP = "_timestamp";
+const string protocol::AGENT_ATTR_HEARTBEAT_INTERVAL = "_heartbeat_interval";
+const string protocol::AGENT_ATTR_EPOCH = "_epoch";
+const string protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP = "_schema_updated";
diff --git a/qpid/cpp/src/qmf/constants.h b/qpid/cpp/src/qmf/constants.h
new file mode 100644
index 0000000000..79beaaf1ca
--- /dev/null
+++ b/qpid/cpp/src/qmf/constants.h
@@ -0,0 +1,83 @@
+#ifndef QMF_CONSTANTS_H
+#define QMF_CONSTANTS_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+
+namespace qmf {
+
+ struct protocol {
+ /**
+ * Header key strings
+ */
+ static const std::string HEADER_KEY_APP_ID;
+ static const std::string HEADER_KEY_METHOD;
+ static const std::string HEADER_KEY_OPCODE;
+ static const std::string HEADER_KEY_AGENT;
+ static const std::string HEADER_KEY_CONTENT;
+ static const std::string HEADER_KEY_PARTIAL;
+
+ /**
+ * Header values per-key
+ */
+ static const std::string HEADER_APP_ID_QMF;
+
+ static const std::string HEADER_METHOD_REQUEST;
+ static const std::string HEADER_METHOD_RESPONSE;
+ static const std::string HEADER_METHOD_INDICATION;
+
+ static const std::string HEADER_OPCODE_EXCEPTION;
+ static const std::string HEADER_OPCODE_AGENT_LOCATE_REQUEST;
+ static const std::string HEADER_OPCODE_AGENT_LOCATE_RESPONSE;
+ static const std::string HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION;
+ static const std::string HEADER_OPCODE_QUERY_REQUEST;
+ static const std::string HEADER_OPCODE_QUERY_RESPONSE;
+ static const std::string HEADER_OPCODE_SUBSCRIBE_REQUEST;
+ static const std::string HEADER_OPCODE_SUBSCRIBE_RESPONSE;
+ static const std::string HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION;
+ static const std::string HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION;
+ static const std::string HEADER_OPCODE_DATA_INDICATION;
+ static const std::string HEADER_OPCODE_METHOD_REQUEST;
+ static const std::string HEADER_OPCODE_METHOD_RESPONSE;
+
+ static const std::string HEADER_CONTENT_SCHEMA_ID;
+ static const std::string HEADER_CONTENT_SCHEMA_CLASS;
+ static const std::string HEADER_CONTENT_OBJECT_ID;
+ static const std::string HEADER_CONTENT_DATA;
+ static const std::string HEADER_CONTENT_EVENT;
+ static const std::string HEADER_CONTENT_QUERY;
+
+ /**
+ * Keywords for Agent attributes
+ */
+ static const std::string AGENT_ATTR_VENDOR;
+ static const std::string AGENT_ATTR_PRODUCT;
+ static const std::string AGENT_ATTR_INSTANCE;
+ static const std::string AGENT_ATTR_NAME;
+ static const std::string AGENT_ATTR_TIMESTAMP;
+ static const std::string AGENT_ATTR_HEARTBEAT_INTERVAL;
+ static const std::string AGENT_ATTR_EPOCH;
+ static const std::string AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP;
+ };
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/engine/Agent.cpp b/qpid/cpp/src/qmf/engine/Agent.cpp
new file mode 100644
index 0000000000..1f08dded94
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/Agent.cpp
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Agent.h"
+#include "qmf/engine/MessageImpl.h"
+#include "qmf/engine/SchemaImpl.h"
+#include "qmf/engine/Typecode.h"
+#include "qmf/engine/EventImpl.h"
+#include "qmf/engine/ObjectImpl.h"
+#include "qmf/engine/ObjectIdImpl.h"
+#include "qmf/engine/QueryImpl.h"
+#include "qmf/engine/ValueImpl.h"
+#include "qmf/engine/Protocol.h"
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/Uuid.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/FieldValue.h>
+#include <qpid/sys/Mutex.h>
+#include <qpid/log/Statement.h>
+#include <qpid/sys/Time.h>
+#include <string.h>
+#include <string>
+#include <deque>
+#include <map>
+#include <iostream>
+#include <fstream>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace qmf {
+namespace engine {
+
+ struct AgentEventImpl {
+ typedef boost::shared_ptr<AgentEventImpl> Ptr;
+ AgentEvent::EventKind kind;
+ uint32_t sequence;
+ string authUserId;
+ string authToken;
+ string name;
+ Object* object;
+ boost::shared_ptr<ObjectId> objectId;
+ boost::shared_ptr<Query> query;
+ boost::shared_ptr<Value> arguments;
+ string exchange;
+ string bindingKey;
+ const SchemaObjectClass* objectClass;
+
+ AgentEventImpl(AgentEvent::EventKind k) :
+ kind(k), sequence(0), object(0), objectClass(0) {}
+ ~AgentEventImpl() {}
+ AgentEvent copy();
+ };
+
+ struct AgentQueryContext {
+ typedef boost::shared_ptr<AgentQueryContext> Ptr;
+ uint32_t sequence;
+ string exchange;
+ string key;
+ const SchemaMethod* schemaMethod;
+ AgentQueryContext() : schemaMethod(0) {}
+ };
+
+ class AgentImpl : public boost::noncopyable {
+ public:
+ AgentImpl(char* label, bool internalStore);
+ ~AgentImpl();
+
+ void setStoreDir(const char* path);
+ void setTransferDir(const char* path);
+ void handleRcvMessage(Message& message);
+ bool getXmtMessage(Message& item) const;
+ void popXmt();
+ bool getEvent(AgentEvent& event) const;
+ void popEvent();
+ void newSession();
+ void startProtocol();
+ void heartbeat();
+ void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments);
+ void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat);
+ void queryComplete(uint32_t sequence);
+ void registerClass(SchemaObjectClass* cls);
+ void registerClass(SchemaEventClass* cls);
+ const ObjectId* addObject(Object& obj, uint64_t persistId);
+ const ObjectId* allocObjectId(uint64_t persistId);
+ const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi);
+ void raiseEvent(Event& event);
+
+ private:
+ mutable Mutex lock;
+ Mutex addLock;
+ string label;
+ string queueName;
+ string storeDir;
+ string transferDir;
+ bool internalStore;
+ uint64_t nextTransientId;
+ Uuid systemId;
+ uint32_t requestedBrokerBank;
+ uint32_t requestedAgentBank;
+ uint32_t assignedBrokerBank;
+ uint32_t assignedAgentBank;
+ AgentAttachment attachment;
+ uint16_t bootSequence;
+ uint64_t nextObjectId;
+ uint32_t nextContextNum;
+ deque<AgentEventImpl::Ptr> eventQueue;
+ deque<MessageImpl::Ptr> xmtQueue;
+ map<uint32_t, AgentQueryContext::Ptr> contextMap;
+ bool attachComplete;
+
+ static const char* QMF_EXCHANGE;
+ static const char* DIR_EXCHANGE;
+ static const char* BROKER_KEY;
+ static const uint32_t MERR_UNKNOWN_METHOD = 2;
+ static const uint32_t MERR_UNKNOWN_PACKAGE = 8;
+ static const uint32_t MERR_UNKNOWN_CLASS = 9;
+ static const uint32_t MERR_INTERNAL_ERROR = 10;
+# define MA_BUFFER_SIZE 65536
+ char outputBuffer[MA_BUFFER_SIZE];
+
+ struct AgentClassKey {
+ string name;
+ uint8_t hash[16];
+ AgentClassKey(const string& n, const uint8_t* h) : name(n) {
+ memcpy(hash, h, 16);
+ }
+ AgentClassKey(Buffer& buffer) {
+ buffer.getShortString(name);
+ buffer.getBin128(hash);
+ }
+ string repr() {
+ return name;
+ }
+ };
+
+ struct AgentClassKeyComp {
+ bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const
+ {
+ if (lhs.name != rhs.name)
+ return lhs.name < rhs.name;
+ else
+ for (int i = 0; i < 16; i++)
+ if (lhs.hash[i] != rhs.hash[i])
+ return lhs.hash[i] < rhs.hash[i];
+ return false;
+ }
+ };
+
+ typedef map<AgentClassKey, SchemaObjectClass*, AgentClassKeyComp> ObjectClassMap;
+ typedef map<AgentClassKey, SchemaEventClass*, AgentClassKeyComp> EventClassMap;
+
+ struct ClassMaps {
+ ObjectClassMap objectClasses;
+ EventClassMap eventClasses;
+ };
+
+ map<string, ClassMaps> packages;
+
+ AgentEventImpl::Ptr eventDeclareQueue(const string& queueName);
+ AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key);
+ AgentEventImpl::Ptr eventSetupComplete();
+ AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls,
+ boost::shared_ptr<ObjectId> oid);
+ AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method,
+ boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap,
+ const SchemaObjectClass* objectClass);
+ void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey);
+
+ void sendPackageIndicationLH(const string& packageName);
+ void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key);
+ void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq,
+ uint32_t code = 0, const string& text = "OK");
+ void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text="");
+ void handleAttachResponse(Buffer& inBuffer);
+ void handlePackageRequest(Buffer& inBuffer);
+ void handleClassQuery(Buffer& inBuffer);
+ void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence,
+ const string& replyToExchange, const string& replyToKey);
+ void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId);
+ void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId);
+ void handleConsoleAddedIndication();
+ };
+}
+}
+
+const char* AgentImpl::QMF_EXCHANGE = "qpid.management";
+const char* AgentImpl::DIR_EXCHANGE = "amq.direct";
+const char* AgentImpl::BROKER_KEY = "broker";
+
+#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());}
+
+AgentEvent AgentEventImpl::copy()
+{
+ AgentEvent item;
+
+ ::memset(&item, 0, sizeof(AgentEvent));
+ item.kind = kind;
+ item.sequence = sequence;
+ item.object = object;
+ item.objectId = objectId.get();
+ item.query = query.get();
+ item.arguments = arguments.get();
+ item.objectClass = objectClass;
+
+ STRING_REF(authUserId);
+ STRING_REF(authToken);
+ STRING_REF(name);
+ STRING_REF(exchange);
+ STRING_REF(bindingKey);
+
+ return item;
+}
+
+AgentImpl::AgentImpl(char* _label, bool i) :
+ label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1),
+ requestedBrokerBank(0), requestedAgentBank(0),
+ assignedBrokerBank(0), assignedAgentBank(0),
+ bootSequence(1), nextObjectId(1), nextContextNum(1), attachComplete(false)
+{
+ queueName += Uuid(true).str();
+}
+
+AgentImpl::~AgentImpl()
+{
+}
+
+void AgentImpl::setStoreDir(const char* path)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (path)
+ storeDir = path;
+ else
+ storeDir.clear();
+}
+
+void AgentImpl::setTransferDir(const char* path)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (path)
+ transferDir = path;
+ else
+ transferDir.clear();
+}
+
+void AgentImpl::handleRcvMessage(Message& message)
+{
+ Buffer inBuffer(message.body, message.length);
+ uint8_t opcode;
+ uint32_t sequence;
+ string replyToExchange(message.replyExchange ? message.replyExchange : "");
+ string replyToKey(message.replyKey ? message.replyKey : "");
+ string userId(message.userId ? message.userId : "");
+
+ while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) {
+ if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer);
+ else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey);
+ else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication();
+ else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId);
+ else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId);
+ else {
+ QPID_LOG(error, "AgentImpl::handleRcvMessage invalid opcode=" << opcode);
+ break;
+ }
+ }
+}
+
+bool AgentImpl::getXmtMessage(Message& item) const
+{
+ Mutex::ScopedLock _lock(lock);
+ if (xmtQueue.empty())
+ return false;
+ item = xmtQueue.front()->copy();
+ return true;
+}
+
+void AgentImpl::popXmt()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!xmtQueue.empty())
+ xmtQueue.pop_front();
+}
+
+bool AgentImpl::getEvent(AgentEvent& event) const
+{
+ Mutex::ScopedLock _lock(lock);
+ if (eventQueue.empty())
+ return false;
+ event = eventQueue.front()->copy();
+ return true;
+}
+
+void AgentImpl::popEvent()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!eventQueue.empty())
+ eventQueue.pop_front();
+}
+
+void AgentImpl::newSession()
+{
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.clear();
+ xmtQueue.clear();
+ eventQueue.push_back(eventDeclareQueue(queueName));
+ eventQueue.push_back(eventBind("amq.direct", queueName, queueName));
+ eventQueue.push_back(eventSetupComplete());
+}
+
+void AgentImpl::startProtocol()
+{
+ Mutex::ScopedLock _lock(lock);
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST);
+ buffer.putShortString(label);
+ systemId.encode(buffer);
+ buffer.putLong(requestedBrokerBank);
+ buffer.putLong(requestedAgentBank);
+ sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank <<
+ " reqAgent=" << requestedAgentBank);
+}
+
+void AgentImpl::heartbeat()
+{
+ Mutex::ScopedLock _lock(lock);
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+
+ Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION);
+ buffer.putLongLong(uint64_t(Duration(EPOCH, now())));
+ stringstream key;
+ key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank;
+ sendBufferLH(buffer, QMF_EXCHANGE, key.str());
+ QPID_LOG(trace, "SENT HeartbeatIndication");
+}
+
+void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& argMap)
+{
+ Mutex::ScopedLock _lock(lock);
+ map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence);
+ if (iter == contextMap.end())
+ return;
+ AgentQueryContext::Ptr context = iter->second;
+ contextMap.erase(iter);
+
+ char* buf(outputBuffer);
+ uint32_t bufLen(114 + strlen(text)); // header(8) + status(4) + mstring(2 + size) + margin(100)
+ bool allocated(false);
+
+ if (status == 0) {
+ for (vector<const SchemaArgument*>::const_iterator aIter = context->schemaMethod->impl->arguments.begin();
+ aIter != context->schemaMethod->impl->arguments.end(); aIter++) {
+ const SchemaArgument* schemaArg = *aIter;
+ if (schemaArg->getDirection() == DIR_OUT || schemaArg->getDirection() == DIR_IN_OUT) {
+ if (argMap.keyInMap(schemaArg->getName())) {
+ const Value* val = argMap.byKey(schemaArg->getName());
+ bufLen += val->impl->encodedSize();
+ } else {
+ Value val(schemaArg->getType());
+ bufLen += val.impl->encodedSize();
+ }
+ }
+ }
+ }
+
+ if (bufLen > MA_BUFFER_SIZE) {
+ buf = (char*) malloc(bufLen);
+ allocated = true;
+ }
+
+ Buffer buffer(buf, bufLen);
+ Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence);
+ buffer.putLong(status);
+ buffer.putMediumString(text);
+ if (status == 0) {
+ for (vector<const SchemaArgument*>::const_iterator aIter = context->schemaMethod->impl->arguments.begin();
+ aIter != context->schemaMethod->impl->arguments.end(); aIter++) {
+ const SchemaArgument* schemaArg = *aIter;
+ if (schemaArg->getDirection() == DIR_OUT || schemaArg->getDirection() == DIR_IN_OUT) {
+ if (argMap.keyInMap(schemaArg->getName())) {
+ const Value* val = argMap.byKey(schemaArg->getName());
+ val->impl->encode(buffer);
+ } else {
+ Value val(schemaArg->getType());
+ val.impl->encode(buffer);
+ }
+ }
+ }
+ }
+ sendBufferLH(buffer, context->exchange, context->key);
+ if (allocated)
+ free(buf);
+ QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text);
+}
+
+void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat)
+{
+ Mutex::ScopedLock _lock(lock);
+ map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence);
+ if (iter == contextMap.end())
+ return;
+ AgentQueryContext::Ptr context = iter->second;
+
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence);
+
+ object.impl->encodeSchemaKey(buffer);
+ object.impl->encodeManagedObjectData(buffer);
+ if (prop)
+ object.impl->encodeProperties(buffer);
+ if (stat)
+ object.impl->encodeStatistics(buffer);
+
+ sendBufferLH(buffer, context->exchange, context->key);
+ QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence);
+}
+
+void AgentImpl::queryComplete(uint32_t sequence)
+{
+ Mutex::ScopedLock _lock(lock);
+ map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence);
+ if (iter == contextMap.end())
+ return;
+
+ AgentQueryContext::Ptr context = iter->second;
+ contextMap.erase(iter);
+ sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK");
+}
+
+void AgentImpl::registerClass(SchemaObjectClass* cls)
+{
+ Mutex::ScopedLock _lock(lock);
+ bool newPackage = false;
+
+ map<string, ClassMaps>::iterator iter = packages.find(cls->getClassKey()->getPackageName());
+ if (iter == packages.end()) {
+ packages[cls->getClassKey()->getPackageName()] = ClassMaps();
+ iter = packages.find(cls->getClassKey()->getPackageName());
+ newPackage = true;
+ }
+
+ AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash());
+ iter->second.objectClasses[key] = cls;
+
+ // Indicate this new schema if connected.
+
+ if (attachComplete) {
+
+ if (newPackage) {
+ sendPackageIndicationLH(iter->first);
+ }
+ sendClassIndicationLH(CLASS_OBJECT, iter->first, key);
+ }
+}
+
+void AgentImpl::registerClass(SchemaEventClass* cls)
+{
+ Mutex::ScopedLock _lock(lock);
+ bool newPackage = false;
+
+ map<string, ClassMaps>::iterator iter = packages.find(cls->getClassKey()->getPackageName());
+ if (iter == packages.end()) {
+ packages[cls->getClassKey()->getPackageName()] = ClassMaps();
+ iter = packages.find(cls->getClassKey()->getPackageName());
+ newPackage = true;
+ }
+
+ AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash());
+ iter->second.eventClasses[key] = cls;
+
+ // Indicate this new schema if connected.
+
+ if (attachComplete) {
+
+ if (newPackage) {
+ sendPackageIndicationLH(iter->first);
+ }
+ sendClassIndicationLH(CLASS_EVENT, iter->first, key);
+ }
+}
+
+const ObjectId* AgentImpl::addObject(Object&, uint64_t)
+{
+ Mutex::ScopedLock _lock(lock);
+ return 0;
+}
+
+const ObjectId* AgentImpl::allocObjectId(uint64_t persistId)
+{
+ Mutex::ScopedLock _lock(lock);
+ uint16_t sequence = persistId ? 0 : bootSequence;
+ uint64_t objectNum = persistId ? persistId : nextObjectId++;
+
+ ObjectId* oid = ObjectIdImpl::factory(&attachment, 0, sequence, objectNum);
+ return oid;
+}
+
+const ObjectId* AgentImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi)
+{
+ return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo);
+}
+
+void AgentImpl::raiseEvent(Event& event)
+{
+ Mutex::ScopedLock _lock(lock);
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_EVENT_INDICATION);
+
+ event.impl->encodeSchemaKey(buffer);
+ buffer.putLongLong(uint64_t(Duration(EPOCH, now())));
+ event.impl->encode(buffer);
+ string key(event.impl->getRoutingKey(assignedBrokerBank, assignedAgentBank));
+
+ sendBufferLH(buffer, QMF_EXCHANGE, key);
+ QPID_LOG(trace, "SENT EventIndication");
+}
+
+AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name)
+{
+ AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE));
+ event->name = name;
+
+ return event;
+}
+
+AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& queue,
+ const string& key)
+{
+ AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND));
+ event->name = queue;
+ event->exchange = exchange;
+ event->bindingKey = key;
+
+ return event;
+}
+
+AgentEventImpl::Ptr AgentImpl::eventSetupComplete()
+{
+ AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE));
+ return event;
+}
+
+AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, const string& package,
+ const string& cls, boost::shared_ptr<ObjectId> oid)
+{
+ AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY));
+ event->sequence = num;
+ event->authUserId = userId;
+ if (oid.get())
+ event->query.reset(new Query(oid.get()));
+ else
+ event->query.reset(new Query(cls.c_str(), package.c_str()));
+ return event;
+}
+
+AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, const string& method,
+ boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap,
+ const SchemaObjectClass* objectClass)
+{
+ AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL));
+ event->sequence = num;
+ event->authUserId = userId;
+ event->name = method;
+ event->objectId = oid;
+ event->arguments = argMap;
+ event->objectClass = objectClass;
+ return event;
+}
+
+void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey)
+{
+ uint32_t length = buf.getPosition();
+ MessageImpl::Ptr message(new MessageImpl);
+
+ buf.reset();
+ buf.getRawData(message->body, length);
+ message->destination = destination;
+ message->routingKey = routingKey;
+ message->replyExchange = "amq.direct";
+ message->replyKey = queueName;
+
+ xmtQueue.push_back(message);
+}
+
+void AgentImpl::sendPackageIndicationLH(const string& packageName)
+{
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION);
+ buffer.putShortString(packageName);
+ sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName);
+}
+
+void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key)
+{
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION);
+ buffer.putOctet((int) kind);
+ buffer.putShortString(packageName);
+ buffer.putShortString(key.name);
+ buffer.putBin128(const_cast<uint8_t*>(key.hash)); // const_cast needed for older Qpid libraries
+ sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name);
+}
+
+void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey,
+ uint32_t sequence, uint32_t code, const string& text)
+{
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence);
+ buffer.putLong(code);
+ buffer.putShortString(text);
+ sendBufferLH(buffer, exchange, replyToKey);
+ QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text);
+}
+
+void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text)
+{
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence);
+ buffer.putLong(code);
+
+ string fulltext;
+ switch (code) {
+ case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break;
+ case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break;
+ case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break;
+ case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break;
+ default: fulltext = "Unspecified Error"; break;
+ }
+
+ if (!text.empty()) {
+ fulltext += " (";
+ fulltext += text;
+ fulltext += ")";
+ }
+
+ buffer.putMediumString(fulltext);
+ sendBufferLH(buffer, DIR_EXCHANGE, key);
+ QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext);
+}
+
+void AgentImpl::handleAttachResponse(Buffer& inBuffer)
+{
+ Mutex::ScopedLock _lock(lock);
+
+ assignedBrokerBank = inBuffer.getLong();
+ assignedAgentBank = inBuffer.getLong();
+
+ QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank);
+
+ if ((assignedBrokerBank != requestedBrokerBank) ||
+ (assignedAgentBank != requestedAgentBank)) {
+ if (requestedAgentBank == 0) {
+ QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." <<
+ assignedAgentBank);
+ } else {
+ QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank <<
+ "." << assignedAgentBank);
+ }
+ //storeData(); // TODO
+ requestedBrokerBank = assignedBrokerBank;
+ requestedAgentBank = assignedAgentBank;
+ }
+
+ attachment.setBanks(assignedBrokerBank, assignedAgentBank);
+
+ // Bind to qpid.management to receive commands
+ stringstream key;
+ key << "agent." << assignedBrokerBank << "." << assignedAgentBank;
+ eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str()));
+
+ // Send package indications for all local packages
+ for (map<string, ClassMaps>::iterator pIter = packages.begin();
+ pIter != packages.end();
+ pIter++) {
+ sendPackageIndicationLH(pIter->first);
+
+ // Send class indications for all local classes
+ ClassMaps cMap = pIter->second;
+ for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin();
+ cIter != cMap.objectClasses.end(); cIter++)
+ sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first);
+ for (EventClassMap::iterator cIter = cMap.eventClasses.begin();
+ cIter != cMap.eventClasses.end(); cIter++)
+ sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first);
+ }
+
+ attachComplete = true;
+}
+
+void AgentImpl::handlePackageRequest(Buffer&)
+{
+ Mutex::ScopedLock _lock(lock);
+}
+
+void AgentImpl::handleClassQuery(Buffer&)
+{
+ Mutex::ScopedLock _lock(lock);
+}
+
+void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence,
+ const string& replyExchange, const string& replyKey)
+{
+ Mutex::ScopedLock _lock(lock);
+ string rExchange(replyExchange);
+ string rKey(replyKey);
+ string packageName;
+ inBuffer.getShortString(packageName);
+ AgentClassKey key(inBuffer);
+
+ if (rExchange.empty())
+ rExchange = QMF_EXCHANGE;
+ if (rKey.empty())
+ rKey = BROKER_KEY;
+
+ QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name);
+
+ map<string, ClassMaps>::iterator pIter = packages.find(packageName);
+ if (pIter == packages.end()) {
+ sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found");
+ return;
+ }
+
+ ClassMaps cMap = pIter->second;
+ ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key);
+ if (ocIter != cMap.objectClasses.end()) {
+ SchemaObjectClass* oImpl = ocIter->second;
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence);
+ oImpl->impl->encode(buffer);
+ sendBufferLH(buffer, rExchange, rKey);
+ QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name);
+ return;
+ }
+
+ EventClassMap::iterator ecIter = cMap.eventClasses.find(key);
+ if (ecIter != cMap.eventClasses.end()) {
+ SchemaEventClass* eImpl = ecIter->second;
+ Buffer buffer(outputBuffer, MA_BUFFER_SIZE);
+ Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence);
+ eImpl->impl->encode(buffer);
+ sendBufferLH(buffer, rExchange, rKey);
+ QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name);
+ return;
+ }
+
+ sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found");
+}
+
+void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId)
+{
+ Mutex::ScopedLock _lock(lock);
+ FieldTable ft;
+ FieldTable::ValuePtr value;
+ map<string, ClassMaps>::const_iterator pIter = packages.end();
+ string pname;
+ string cname;
+ string oidRepr;
+ boost::shared_ptr<ObjectId> oid;
+
+ ft.decode(inBuffer);
+
+ QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft);
+
+ value = ft.get("_package");
+ if (value.get() && value->convertsTo<string>()) {
+ pname = value->get<string>();
+ pIter = packages.find(pname);
+ if (pIter == packages.end()) {
+ sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence);
+ return;
+ }
+ }
+
+ value = ft.get("_class");
+ if (value.get() && value->convertsTo<string>()) {
+ cname = value->get<string>();
+ // TODO - check for validity of class (in package or any package)
+ if (pIter == packages.end()) {
+ } else {
+
+ }
+ }
+
+ value = ft.get("_objectid");
+ if (value.get() && value->convertsTo<string>()) {
+ oidRepr = value->get<string>();
+ oid.reset(new ObjectId());
+ oid->impl->fromString(oidRepr);
+ }
+
+ AgentQueryContext::Ptr context(new AgentQueryContext);
+ uint32_t contextNum = nextContextNum++;
+ context->sequence = sequence;
+ context->exchange = DIR_EXCHANGE;
+ context->key = replyTo;
+ contextMap[contextNum] = context;
+
+ eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid));
+}
+
+void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId)
+{
+ Mutex::ScopedLock _lock(lock);
+ string pname;
+ string method;
+ boost::shared_ptr<ObjectId> oid(ObjectIdImpl::factory(buffer));
+ buffer.getShortString(pname);
+ AgentClassKey classKey(buffer);
+ buffer.getShortString(method);
+
+ QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method);
+
+ map<string, ClassMaps>::const_iterator pIter = packages.find(pname);
+ if (pIter == packages.end()) {
+ sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname);
+ return;
+ }
+
+ ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey);
+ if (cIter == pIter->second.objectClasses.end()) {
+ sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr());
+ return;
+ }
+
+ const SchemaObjectClass* schema = cIter->second;
+ vector<const SchemaMethod*>::const_iterator mIter = schema->impl->methods.begin();
+ for (; mIter != schema->impl->methods.end(); mIter++) {
+ if ((*mIter)->getName() == method)
+ break;
+ }
+
+ if (mIter == schema->impl->methods.end()) {
+ sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method);
+ return;
+ }
+
+ const SchemaMethod* schemaMethod = *mIter;
+ boost::shared_ptr<Value> argMap(new Value(TYPE_MAP));
+ Value* value;
+ for (vector<const SchemaArgument*>::const_iterator aIter = schemaMethod->impl->arguments.begin();
+ aIter != schemaMethod->impl->arguments.end(); aIter++) {
+ const SchemaArgument* schemaArg = *aIter;
+ if (schemaArg->getDirection() == DIR_IN || schemaArg->getDirection() == DIR_IN_OUT)
+ value = ValueImpl::factory(schemaArg->getType(), buffer);
+ else
+ value = ValueImpl::factory(schemaArg->getType());
+ argMap->insert(schemaArg->getName(), value);
+ }
+
+ AgentQueryContext::Ptr context(new AgentQueryContext);
+ uint32_t contextNum = nextContextNum++;
+ context->sequence = sequence;
+ context->exchange = DIR_EXCHANGE;
+ context->key = replyTo;
+ context->schemaMethod = schemaMethod;
+ contextMap[contextNum] = context;
+
+ eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema));
+}
+
+void AgentImpl::handleConsoleAddedIndication()
+{
+ Mutex::ScopedLock _lock(lock);
+}
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+Agent::Agent(char* label, bool internalStore) { impl = new AgentImpl(label, internalStore); }
+Agent::~Agent() { delete impl; }
+void Agent::setStoreDir(const char* path) { impl->setStoreDir(path); }
+void Agent::setTransferDir(const char* path) { impl->setTransferDir(path); }
+void Agent::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); }
+bool Agent::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); }
+void Agent::popXmt() { impl->popXmt(); }
+bool Agent::getEvent(AgentEvent& event) const { return impl->getEvent(event); }
+void Agent::popEvent() { impl->popEvent(); }
+void Agent::newSession() { impl->newSession(); }
+void Agent::startProtocol() { impl->startProtocol(); }
+void Agent::heartbeat() { impl->heartbeat(); }
+void Agent::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); }
+void Agent::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); }
+void Agent::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); }
+void Agent::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); }
+void Agent::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); }
+const ObjectId* Agent::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); }
+const ObjectId* Agent::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); }
+const ObjectId* Agent::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); }
+void Agent::raiseEvent(Event& event) { impl->raiseEvent(event); }
+
diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp
new file mode 100644
index 0000000000..5fc71979fd
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp
@@ -0,0 +1,827 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/BrokerProxyImpl.h"
+#include "qmf/engine/ConsoleImpl.h"
+#include "qmf/engine/Protocol.h"
+#include "qpid/Address.h"
+#include "qpid/sys/SystemInfo.h"
+#include <qpid/log/Statement.h>
+#include <qpid/StringUtils.h>
+#include <string.h>
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace {
+ const char* QMF_EXCHANGE = "qpid.management";
+ const char* DIR_EXCHANGE = "amq.direct";
+ const char* BROKER_KEY = "broker";
+ const char* BROKER_PACKAGE = "org.apache.qpid.broker";
+ const char* AGENT_CLASS = "agent";
+ const char* BROKER_AGENT_KEY = "agent.1.0";
+}
+
+const Object* QueryResponseImpl::getObject(uint32_t idx) const
+{
+ vector<ObjectPtr>::const_iterator iter = results.begin();
+
+ while (idx > 0) {
+ if (iter == results.end())
+ return 0;
+ iter++;
+ idx--;
+ }
+
+ return iter->get();
+}
+
+#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());}
+
+BrokerEvent BrokerEventImpl::copy()
+{
+ BrokerEvent item;
+
+ ::memset(&item, 0, sizeof(BrokerEvent));
+ item.kind = kind;
+
+ STRING_REF(name);
+ STRING_REF(exchange);
+ STRING_REF(bindingKey);
+ item.context = context;
+ item.queryResponse = queryResponse.get();
+ item.methodResponse = methodResponse.get();
+
+ return item;
+}
+
+BrokerProxyImpl::BrokerProxyImpl(BrokerProxy& pub, Console& _console) : publicObject(pub), console(_console)
+{
+ stringstream qn;
+ qpid::Address addr;
+
+ SystemInfo::getLocalHostname(addr);
+ qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId();
+ queueName = qn.str();
+
+ seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this)));
+}
+
+void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/)
+{
+ Mutex::ScopedLock _lock(lock);
+ agentList.clear();
+ eventQueue.clear();
+ xmtQueue.clear();
+ eventQueue.push_back(eventDeclareQueue(queueName));
+ eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName));
+ eventQueue.push_back(eventSetupComplete());
+
+ // TODO: Store session handle
+}
+
+void BrokerProxyImpl::sessionClosed()
+{
+ Mutex::ScopedLock _lock(lock);
+ agentList.clear();
+ eventQueue.clear();
+ xmtQueue.clear();
+}
+
+void BrokerProxyImpl::startProtocol()
+{
+ AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, 0, "Agent embedded in broker"));
+ {
+ Mutex::ScopedLock _lock(lock);
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ agentList[0] = agent;
+
+ requestsOutstanding = 1;
+ topicBound = false;
+ uint32_t sequence(seqMgr.reserve());
+ Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence);
+ sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence);
+ }
+
+ console.impl->eventAgentAdded(agent);
+}
+
+void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey)
+{
+ uint32_t length = buf.getPosition();
+ MessageImpl::Ptr message(new MessageImpl);
+
+ buf.reset();
+ buf.getRawData(message->body, length);
+ message->destination = destination;
+ message->routingKey = routingKey;
+ message->replyExchange = DIR_EXCHANGE;
+ message->replyKey = queueName;
+
+ xmtQueue.push_back(message);
+}
+
+void BrokerProxyImpl::handleRcvMessage(Message& message)
+{
+ Buffer inBuffer(message.body, message.length);
+ uint8_t opcode;
+ uint32_t sequence;
+
+ while (Protocol::checkHeader(inBuffer, &opcode, &sequence))
+ seqMgr.dispatch(opcode, sequence, message.routingKey ? string(message.routingKey) : string(), inBuffer);
+}
+
+bool BrokerProxyImpl::getXmtMessage(Message& item) const
+{
+ Mutex::ScopedLock _lock(lock);
+ if (xmtQueue.empty())
+ return false;
+ item = xmtQueue.front()->copy();
+ return true;
+}
+
+void BrokerProxyImpl::popXmt()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!xmtQueue.empty())
+ xmtQueue.pop_front();
+}
+
+bool BrokerProxyImpl::getEvent(BrokerEvent& event) const
+{
+ Mutex::ScopedLock _lock(lock);
+ if (eventQueue.empty())
+ return false;
+ event = eventQueue.front()->copy();
+ return true;
+}
+
+void BrokerProxyImpl::popEvent()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!eventQueue.empty())
+ eventQueue.pop_front();
+}
+
+uint32_t BrokerProxyImpl::agentCount() const
+{
+ Mutex::ScopedLock _lock(lock);
+ return agentList.size();
+}
+
+const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const
+{
+ Mutex::ScopedLock _lock(lock);
+ for (map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.begin();
+ iter != agentList.end(); iter++)
+ if (idx-- == 0)
+ return iter->second.get();
+ return 0;
+}
+
+void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent)
+{
+ SequenceContext::Ptr queryContext(new QueryContext(*this, context));
+ Mutex::ScopedLock _lock(lock);
+ bool sent = false;
+ if (agent != 0) {
+ if (sendGetRequestLH(queryContext, query, agent))
+ sent = true;
+ } else {
+ // TODO (optimization) only send queries to agents that have the requested class+package
+ for (map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.begin();
+ iter != agentList.end(); iter++) {
+ if (sendGetRequestLH(queryContext, query, iter->second.get()))
+ sent = true;
+ }
+ }
+
+ if (!sent) {
+ queryContext->reserve();
+ queryContext->release();
+ }
+}
+
+bool BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent)
+{
+ if (query.impl->singleAgent()) {
+ if (query.impl->agentBank() != agent->getAgentBank())
+ return false;
+ }
+ stringstream key;
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence(seqMgr.reserve(queryContext));
+ agent->impl->addSequence(sequence);
+
+ Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence);
+ query.impl->encode(outBuffer);
+ key << "agent.1." << agent->impl->agentBank;
+ sendBufferLH(outBuffer, QMF_EXCHANGE, key.str());
+ QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str());
+ return true;
+}
+
+string BrokerProxyImpl::encodeMethodArguments(const SchemaMethod* schema, const Value* argmap, Buffer& buffer)
+{
+ int argCount = schema->getArgumentCount();
+
+ if (argmap == 0 || !argmap->isMap())
+ return string("Arguments must be in a map value");
+
+ for (int aIdx = 0; aIdx < argCount; aIdx++) {
+ const SchemaArgument* arg(schema->getArgument(aIdx));
+ if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) {
+ if (argmap->keyInMap(arg->getName())) {
+ const Value* argVal(argmap->byKey(arg->getName()));
+ if (argVal->getType() != arg->getType())
+ return string("Argument is the wrong type: ") + arg->getName();
+ argVal->impl->encode(buffer);
+ } else {
+ Value defaultValue(arg->getType());
+ defaultValue.impl->encode(buffer);
+ }
+ }
+ }
+
+ return string();
+}
+
+string BrokerProxyImpl::encodedSizeMethodArguments(const SchemaMethod* schema, const Value* argmap, uint32_t& size)
+{
+ int argCount = schema->getArgumentCount();
+
+ if (argmap == 0 || !argmap->isMap())
+ return string("Arguments must be in a map value");
+
+ for (int aIdx = 0; aIdx < argCount; aIdx++) {
+ const SchemaArgument* arg(schema->getArgument(aIdx));
+ if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) {
+ if (argmap->keyInMap(arg->getName())) {
+ const Value* argVal(argmap->byKey(arg->getName()));
+ if (argVal->getType() != arg->getType())
+ return string("Argument is the wrong type: ") + arg->getName();
+ size += argVal->impl->encodedSize();
+ } else {
+ Value defaultValue(arg->getType());
+ size += defaultValue.impl->encodedSize();
+ }
+ }
+ }
+
+ return string();
+}
+
+void BrokerProxyImpl::sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls,
+ const string& methodName, const Value* args, void* userContext)
+{
+ int methodCount = cls->getMethodCount();
+ int idx;
+ for (idx = 0; idx < methodCount; idx++) {
+ const SchemaMethod* method = cls->getMethod(idx);
+ if (string(method->getName()) == methodName) {
+ Mutex::ScopedLock _lock(lock);
+ SequenceContext::Ptr methodContext(new MethodContext(*this, userContext, method));
+ stringstream key;
+ char* buf(outputBuffer);
+ uint32_t bufLen(1024);
+ bool allocated(false);
+
+ string argErrorString = encodedSizeMethodArguments(method, args, bufLen);
+ if (!argErrorString.empty()) {
+ MethodResponsePtr argError(MethodResponseImpl::factory(1, argErrorString));
+ eventQueue.push_back(eventMethodResponse(userContext, argError));
+ return;
+ }
+
+ if (bufLen > MA_BUFFER_SIZE) {
+ buf = (char*) malloc(bufLen);
+ allocated = true;
+ }
+
+ Buffer outBuffer(buf, bufLen);
+ uint32_t sequence(seqMgr.reserve(methodContext));
+
+ Protocol::encodeHeader(outBuffer, Protocol::OP_METHOD_REQUEST, sequence);
+ oid->impl->encode(outBuffer);
+ cls->getClassKey()->impl->encode(outBuffer);
+ outBuffer.putShortString(methodName);
+
+ encodeMethodArguments(method, args, outBuffer);
+ key << "agent.1." << oid->impl->getAgentBank();
+ sendBufferLH(outBuffer, QMF_EXCHANGE, key.str());
+ QPID_LOG(trace, "SENT MethodRequest seq=" << sequence << " method=" << methodName << " key=" << key.str());
+
+ if (allocated)
+ free(buf);
+
+ return;
+ }
+ }
+
+ MethodResponsePtr error(MethodResponseImpl::factory(1, string("Unknown method: ") + methodName));
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(eventMethodResponse(userContext, error));
+}
+
+void BrokerProxyImpl::addBinding(const string& exchange, const string& key)
+{
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(eventBind(exchange, queueName, key));
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName)
+{
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE));
+ event->name = queueName;
+ return event;
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key)
+{
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND));
+ event->name = queue;
+ event->exchange = exchange;
+ event->bindingKey = key;
+
+ return event;
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete()
+{
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE));
+ return event;
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventStable()
+{
+ QPID_LOG(trace, "Console Link to Broker Stable");
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE));
+ return event;
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponsePtr response)
+{
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE));
+ event->context = context;
+ event->queryResponse = response;
+ return event;
+}
+
+BrokerEventImpl::Ptr BrokerProxyImpl::eventMethodResponse(void* context, MethodResponsePtr response)
+{
+ BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::METHOD_RESPONSE));
+ event->context = context;
+ event->methodResponse = response;
+ return event;
+}
+
+void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq)
+{
+ brokerId.decode(inBuffer);
+ QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId);
+ Mutex::ScopedLock _lock(lock);
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence(seqMgr.reserve());
+ incOutstandingLH();
+ Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence);
+ sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT PackageRequest seq=" << sequence);
+}
+
+void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq)
+{
+ string package;
+
+ inBuffer.getShortString(package);
+ QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package);
+ console.impl->learnPackage(package);
+
+ Mutex::ScopedLock _lock(lock);
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence(seqMgr.reserve());
+ incOutstandingLH();
+ Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence);
+ outBuffer.putShortString(package);
+ sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package);
+}
+
+void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq)
+{
+ string text;
+ uint32_t code = inBuffer.getLong();
+ inBuffer.getShortString(text);
+ QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text);
+}
+
+void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq)
+{
+ uint8_t kind = inBuffer.getOctet();
+ auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer));
+
+ QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey->impl->str());
+
+ if (!console.impl->haveClass(classKey.get())) {
+ Mutex::ScopedLock _lock(lock);
+ incOutstandingLH();
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence(seqMgr.reserve());
+ Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence);
+ classKey->impl->encode(outBuffer);
+ sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY);
+ QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey->impl->str());
+ }
+}
+
+MethodResponsePtr BrokerProxyImpl::handleMethodResponse(Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema)
+{
+ MethodResponsePtr response(MethodResponseImpl::factory(inBuffer, schema));
+
+ QPID_LOG(trace, "RCVD MethodResponse seq=" << seq << " status=" << response->getStatus() << " text=" <<
+ response->getException()->asString());
+
+ return response;
+}
+
+void BrokerProxyImpl::handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq, const string& routingKey)
+{
+ vector<string> tokens = qpid::split(routingKey, ".");
+ uint32_t agentBank;
+ uint64_t timestamp;
+
+ if (routingKey.empty() || tokens.size() != 4)
+ agentBank = 0;
+ else
+ agentBank = ::atoi(tokens[3].c_str());
+
+ timestamp = inBuffer.getLongLong();
+ map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.find(agentBank);
+ if (iter != agentList.end()) {
+ console.impl->eventAgentHeartbeat(iter->second, timestamp);
+ }
+ QPID_LOG(trace, "RCVD HeartbeatIndication seq=" << seq << " agentBank=" << agentBank);
+}
+
+void BrokerProxyImpl::handleEventIndication(Buffer& inBuffer, uint32_t seq)
+{
+ auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer));
+ const SchemaEventClass *schema = console.impl->getEventClass(classKey.get());
+ if (schema == 0) {
+ QPID_LOG(trace, "No Schema Found for EventIndication. seq=" << seq << " key=" << classKey->impl->str());
+ return;
+ }
+
+ EventPtr eptr(EventImpl::factory(schema, inBuffer));
+
+ console.impl->eventEventReceived(eptr);
+ QPID_LOG(trace, "RCVD EventIndication seq=" << seq << " key=" << classKey->impl->str());
+}
+
+void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq)
+{
+ SchemaObjectClass* oClassPtr;
+ SchemaEventClass* eClassPtr;
+ uint8_t kind = inBuffer.getOctet();
+ const SchemaClassKey* key;
+ if (kind == CLASS_OBJECT) {
+ oClassPtr = SchemaObjectClassImpl::factory(inBuffer);
+ console.impl->learnClass(oClassPtr);
+ key = oClassPtr->getClassKey();
+ QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->impl->str());
+
+ //
+ // If we have just learned about the org.apache.qpid.broker:agent class, send a get
+ // request for the current list of agents so we can have it on-hand before we declare
+ // this session "stable".
+ //
+ if (key->impl->getClassName() == AGENT_CLASS && key->impl->getPackageName() == BROKER_PACKAGE) {
+ Mutex::ScopedLock _lock(lock);
+ incOutstandingLH();
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence(seqMgr.reserve());
+ Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence);
+ FieldTable ft;
+ ft.setString("_class", AGENT_CLASS);
+ ft.setString("_package", BROKER_PACKAGE);
+ ft.encode(outBuffer);
+ sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY);
+ QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY);
+ }
+ } else if (kind == CLASS_EVENT) {
+ eClassPtr = SchemaEventClassImpl::factory(inBuffer);
+ console.impl->learnClass(eClassPtr);
+ key = eClassPtr->getClassKey();
+ QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->impl->str());
+ }
+ else {
+ QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind);
+ }
+}
+
+ObjectPtr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat)
+{
+ auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer));
+ QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey->impl->str());
+
+ SchemaObjectClass* schema = console.impl->getSchema(classKey.get());
+ if (schema == 0) {
+ QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey->impl->str());
+ return ObjectPtr();
+ }
+
+ ObjectPtr optr(ObjectImpl::factory(schema, this, inBuffer, prop, stat, true));
+ if (prop && classKey->impl->getPackageName() == BROKER_PACKAGE && classKey->impl->getClassName() == AGENT_CLASS) {
+ //
+ // We've intercepted information about a remote agent... update the agent list accordingly
+ //
+ updateAgentList(optr);
+ }
+ return optr;
+}
+
+void BrokerProxyImpl::updateAgentList(ObjectPtr obj)
+{
+ Value* value = obj->getValue("agentBank");
+ Mutex::ScopedLock _lock(lock);
+ if (value != 0 && value->isUint()) {
+ uint32_t agentBank = value->asUint();
+ if (obj->isDeleted()) {
+ map<uint32_t, AgentProxyPtr>::iterator iter = agentList.find(agentBank);
+ if (iter != agentList.end()) {
+ AgentProxyPtr agent(iter->second);
+ console.impl->eventAgentDeleted(agent);
+ agentList.erase(agentBank);
+ QPID_LOG(trace, "Agent at bank " << agentBank << " removed from agent list");
+
+ //
+ // Release all sequence numbers for requests in-flight to this agent.
+ // Since the agent is no longer connected, these requests would not
+ // otherwise complete.
+ //
+ agent->impl->releaseInFlight(seqMgr);
+ }
+ } else {
+ Value* str = obj->getValue("label");
+ string label;
+ if (str != 0 && str->isString())
+ label = str->asString();
+ map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.find(agentBank);
+ if (iter == agentList.end()) {
+ AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, agentBank, label));
+ agentList[agentBank] = agent;
+ console.impl->eventAgentAdded(agent);
+ QPID_LOG(trace, "Agent '" << label << "' found at bank " << agentBank);
+ }
+ }
+ }
+}
+
+void BrokerProxyImpl::incOutstandingLH()
+{
+ requestsOutstanding++;
+}
+
+void BrokerProxyImpl::decOutstanding()
+{
+ Mutex::ScopedLock _lock(lock);
+ requestsOutstanding--;
+ if (requestsOutstanding == 0 && !topicBound) {
+ topicBound = true;
+ for (vector<pair<string, string> >::const_iterator iter = console.impl->bindingList.begin();
+ iter != console.impl->bindingList.end(); iter++) {
+ string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first);
+ string key(iter->second);
+ eventQueue.push_back(eventBind(exchange, queueName, key));
+ }
+ eventQueue.push_back(eventStable());
+ }
+}
+
+MethodResponseImpl::MethodResponseImpl(const MethodResponseImpl& from) :
+ status(from.status), schema(from.schema)
+{
+ if (from.exception.get())
+ exception.reset(new Value(*(from.exception)));
+ if (from.arguments.get())
+ arguments.reset(new Value(*(from.arguments)));
+}
+
+MethodResponseImpl::MethodResponseImpl(Buffer& buf, const SchemaMethod* s) : schema(s)
+{
+ string text;
+
+ status = buf.getLong();
+ buf.getMediumString(text);
+ exception.reset(new Value(TYPE_LSTR));
+ exception->setString(text.c_str());
+
+ if (status != 0)
+ return;
+
+ arguments.reset(new Value(TYPE_MAP));
+ int argCount(schema->getArgumentCount());
+ for (int idx = 0; idx < argCount; idx++) {
+ const SchemaArgument* arg = schema->getArgument(idx);
+ if (arg->getDirection() == DIR_OUT || arg->getDirection() == DIR_IN_OUT) {
+ Value* value(ValueImpl::factory(arg->getType(), buf));
+ arguments->insert(arg->getName(), value);
+ }
+ }
+}
+
+MethodResponseImpl::MethodResponseImpl(uint32_t s, const string& text) : schema(0)
+{
+ status = s;
+ exception.reset(new Value(TYPE_LSTR));
+ exception->setString(text.c_str());
+}
+
+MethodResponse* MethodResponseImpl::factory(Buffer& buf, const SchemaMethod* schema)
+{
+ MethodResponseImpl* impl(new MethodResponseImpl(buf, schema));
+ return new MethodResponse(impl);
+}
+
+MethodResponse* MethodResponseImpl::factory(uint32_t status, const std::string& text)
+{
+ MethodResponseImpl* impl(new MethodResponseImpl(status, text));
+ return new MethodResponse(impl);
+}
+
+bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& routingKey, Buffer& buffer)
+{
+ ObjectPtr object;
+ bool completeContext = false;
+
+ if (opcode == Protocol::OP_BROKER_RESPONSE) {
+ broker.handleBrokerResponse(buffer, sequence);
+ completeContext = true;
+ }
+ else if (opcode == Protocol::OP_COMMAND_COMPLETE) {
+ broker.handleCommandComplete(buffer, sequence);
+ completeContext = true;
+ }
+ else if (opcode == Protocol::OP_SCHEMA_RESPONSE) {
+ broker.handleSchemaResponse(buffer, sequence);
+ completeContext = true;
+ }
+ else if (opcode == Protocol::OP_PACKAGE_INDICATION)
+ broker.handlePackageIndication(buffer, sequence);
+ else if (opcode == Protocol::OP_CLASS_INDICATION)
+ broker.handleClassIndication(buffer, sequence);
+ else if (opcode == Protocol::OP_HEARTBEAT_INDICATION)
+ broker.handleHeartbeatIndication(buffer, sequence, routingKey);
+ else if (opcode == Protocol::OP_EVENT_INDICATION)
+ broker.handleEventIndication(buffer, sequence);
+ else if (opcode == Protocol::OP_PROPERTY_INDICATION) {
+ object = broker.handleObjectIndication(buffer, sequence, true, false);
+ broker.console.impl->eventObjectUpdate(object, true, false);
+ }
+ else if (opcode == Protocol::OP_STATISTIC_INDICATION) {
+ object = broker.handleObjectIndication(buffer, sequence, false, true);
+ broker.console.impl->eventObjectUpdate(object, false, true);
+ }
+ else if (opcode == Protocol::OP_OBJECT_INDICATION) {
+ object = broker.handleObjectIndication(buffer, sequence, true, true);
+ broker.console.impl->eventObjectUpdate(object, true, true);
+ }
+ else {
+ QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode);
+ completeContext = true;
+ }
+
+ return completeContext;
+}
+
+void QueryContext::reserve()
+{
+ Mutex::ScopedLock _lock(lock);
+ requestsOutstanding++;
+}
+
+void QueryContext::release()
+{
+ {
+ Mutex::ScopedLock _lock(lock);
+ if (--requestsOutstanding > 0)
+ return;
+ }
+
+ Mutex::ScopedLock _block(broker.lock);
+ broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse));
+}
+
+bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer)
+{
+ bool completeContext = false;
+ ObjectPtr object;
+
+ if (opcode == Protocol::OP_COMMAND_COMPLETE) {
+ broker.handleCommandComplete(buffer, sequence);
+ completeContext = true;
+
+ //
+ // Visit each agent and remove the sequence from that agent's in-flight list.
+ // This could be made more efficient because only one agent will have this sequence
+ // in its list.
+ //
+ map<uint32_t, AgentProxyPtr> copy;
+ {
+ Mutex::ScopedLock _block(broker.lock);
+ copy = broker.agentList;
+ }
+ for (map<uint32_t, AgentProxyPtr>::iterator iter = copy.begin(); iter != copy.end(); iter++)
+ iter->second->impl->delSequence(sequence);
+ }
+ else if (opcode == Protocol::OP_OBJECT_INDICATION) {
+ object = broker.handleObjectIndication(buffer, sequence, true, true);
+ if (object.get() != 0)
+ queryResponse->impl->results.push_back(object);
+ }
+ else {
+ QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode);
+ completeContext = true;
+ }
+
+ return completeContext;
+}
+
+void MethodContext::release()
+{
+ Mutex::ScopedLock _block(broker.lock);
+ broker.eventQueue.push_back(broker.eventMethodResponse(userContext, methodResponse));
+}
+
+bool MethodContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer)
+{
+ if (opcode == Protocol::OP_METHOD_RESPONSE)
+ methodResponse = broker.handleMethodResponse(buffer, sequence, schema);
+ else
+ QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode);
+
+ return true;
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {}
+AgentProxy::AgentProxy(const AgentProxy& from) : impl(new AgentProxyImpl(*(from.impl))) {}
+AgentProxy::~AgentProxy() { delete impl; }
+const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); }
+uint32_t AgentProxy::getBrokerBank() const { return impl->getBrokerBank(); }
+uint32_t AgentProxy::getAgentBank() const { return impl->getAgentBank(); }
+
+BrokerProxy::BrokerProxy(Console& console) : impl(new BrokerProxyImpl(*this, console)) {}
+BrokerProxy::~BrokerProxy() { delete impl; }
+void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); }
+void BrokerProxy::sessionClosed() { impl->sessionClosed(); }
+void BrokerProxy::startProtocol() { impl->startProtocol(); }
+void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); }
+bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); }
+void BrokerProxy::popXmt() { impl->popXmt(); }
+bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); }
+void BrokerProxy::popEvent() { impl->popEvent(); }
+uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); }
+const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); }
+void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); }
+
+MethodResponse::MethodResponse(const MethodResponse& from) : impl(new MethodResponseImpl(*(from.impl))) {}
+MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {}
+MethodResponse::~MethodResponse() {}
+uint32_t MethodResponse::getStatus() const { return impl->getStatus(); }
+const Value* MethodResponse::getException() const { return impl->getException(); }
+const Value* MethodResponse::getArgs() const { return impl->getArgs(); }
+
+QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {}
+QueryResponse::~QueryResponse() {}
+uint32_t QueryResponse::getStatus() const { return impl->getStatus(); }
+const Value* QueryResponse::getException() const { return impl->getException(); }
+uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); }
+const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); }
+
diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h
new file mode 100644
index 0000000000..0542b67dbb
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h
@@ -0,0 +1,241 @@
+#ifndef _QmfEngineBrokerProxyImpl_
+#define _QmfEngineBrokerProxyImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Console.h"
+#include "qmf/engine/ObjectImpl.h"
+#include "qmf/engine/EventImpl.h"
+#include "qmf/engine/SchemaImpl.h"
+#include "qmf/engine/ValueImpl.h"
+#include "qmf/engine/QueryImpl.h"
+#include "qmf/engine/SequenceManager.h"
+#include "qmf/engine/MessageImpl.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Mutex.h"
+#include "boost/shared_ptr.hpp"
+#include "boost/noncopyable.hpp"
+#include <memory>
+#include <string>
+#include <deque>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace qmf {
+namespace engine {
+
+ typedef boost::shared_ptr<MethodResponse> MethodResponsePtr;
+ struct MethodResponseImpl {
+ uint32_t status;
+ const SchemaMethod* schema;
+ std::auto_ptr<Value> exception;
+ std::auto_ptr<Value> arguments;
+
+ MethodResponseImpl(const MethodResponseImpl& from);
+ MethodResponseImpl(qpid::framing::Buffer& buf, const SchemaMethod* schema);
+ MethodResponseImpl(uint32_t status, const std::string& text);
+ static MethodResponse* factory(qpid::framing::Buffer& buf, const SchemaMethod* schema);
+ static MethodResponse* factory(uint32_t status, const std::string& text);
+ ~MethodResponseImpl() {}
+ uint32_t getStatus() const { return status; }
+ const Value* getException() const { return exception.get(); }
+ const Value* getArgs() const { return arguments.get(); }
+ };
+
+ typedef boost::shared_ptr<QueryResponse> QueryResponsePtr;
+ struct QueryResponseImpl {
+ uint32_t status;
+ std::auto_ptr<Value> exception;
+ std::vector<ObjectPtr> results;
+
+ QueryResponseImpl() : status(0) {}
+ static QueryResponse* factory() {
+ QueryResponseImpl* impl(new QueryResponseImpl());
+ return new QueryResponse(impl);
+ }
+ ~QueryResponseImpl() {}
+ uint32_t getStatus() const { return status; }
+ const Value* getException() const { return exception.get(); }
+ uint32_t getObjectCount() const { return results.size(); }
+ const Object* getObject(uint32_t idx) const;
+ };
+
+ struct BrokerEventImpl {
+ typedef boost::shared_ptr<BrokerEventImpl> Ptr;
+ BrokerEvent::EventKind kind;
+ std::string name;
+ std::string exchange;
+ std::string bindingKey;
+ void* context;
+ QueryResponsePtr queryResponse;
+ MethodResponsePtr methodResponse;
+
+ BrokerEventImpl(BrokerEvent::EventKind k) : kind(k), context(0) {}
+ ~BrokerEventImpl() {}
+ BrokerEvent copy();
+ };
+
+ typedef boost::shared_ptr<AgentProxy> AgentProxyPtr;
+ struct AgentProxyImpl {
+ Console& console;
+ BrokerProxy& broker;
+ uint32_t agentBank;
+ std::string label;
+ std::set<uint32_t> inFlightSequences;
+
+ AgentProxyImpl(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) : console(c), broker(b), agentBank(ab), label(l) {}
+ static AgentProxy* factory(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) {
+ AgentProxyImpl* impl(new AgentProxyImpl(c, b, ab, l));
+ return new AgentProxy(impl);
+ }
+ ~AgentProxyImpl() {}
+ const std::string& getLabel() const { return label; }
+ uint32_t getBrokerBank() const { return 1; }
+ uint32_t getAgentBank() const { return agentBank; }
+ void addSequence(uint32_t seq) { inFlightSequences.insert(seq); }
+ void delSequence(uint32_t seq) { inFlightSequences.erase(seq); }
+ void releaseInFlight(SequenceManager& seqMgr) {
+ for (std::set<uint32_t>::iterator iter = inFlightSequences.begin(); iter != inFlightSequences.end(); iter++)
+ seqMgr.release(*iter);
+ inFlightSequences.clear();
+ }
+ };
+
+ class BrokerProxyImpl : public boost::noncopyable {
+ public:
+ BrokerProxyImpl(BrokerProxy& pub, Console& _console);
+ ~BrokerProxyImpl() {}
+
+ void sessionOpened(SessionHandle& sh);
+ void sessionClosed();
+ void startProtocol();
+
+ void sendBufferLH(qpid::framing::Buffer& buf, const std::string& destination, const std::string& routingKey);
+ void handleRcvMessage(Message& message);
+ bool getXmtMessage(Message& item) const;
+ void popXmt();
+
+ bool getEvent(BrokerEvent& event) const;
+ void popEvent();
+
+ uint32_t agentCount() const;
+ const AgentProxy* getAgent(uint32_t idx) const;
+ void sendQuery(const Query& query, void* context, const AgentProxy* agent);
+ bool sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent);
+ std::string encodeMethodArguments(const SchemaMethod* schema, const Value* args, qpid::framing::Buffer& buffer);
+ std::string encodedSizeMethodArguments(const SchemaMethod* schema, const Value* args, uint32_t& size);
+ void sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls, const std::string& method, const Value* args, void* context);
+
+ void addBinding(const std::string& exchange, const std::string& key);
+ void staticRelease() { decOutstanding(); }
+
+ private:
+ friend struct StaticContext;
+ friend struct QueryContext;
+ friend struct MethodContext;
+ BrokerProxy& publicObject;
+ mutable qpid::sys::Mutex lock;
+ Console& console;
+ std::string queueName;
+ qpid::framing::Uuid brokerId;
+ SequenceManager seqMgr;
+ uint32_t requestsOutstanding;
+ bool topicBound;
+ std::map<uint32_t, AgentProxyPtr> agentList;
+ std::deque<MessageImpl::Ptr> xmtQueue;
+ std::deque<BrokerEventImpl::Ptr> eventQueue;
+
+# define MA_BUFFER_SIZE 65536
+ char outputBuffer[MA_BUFFER_SIZE];
+
+ BrokerEventImpl::Ptr eventDeclareQueue(const std::string& queueName);
+ BrokerEventImpl::Ptr eventBind(const std::string& exchange, const std::string& queue, const std::string& key);
+ BrokerEventImpl::Ptr eventSetupComplete();
+ BrokerEventImpl::Ptr eventStable();
+ BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponsePtr response);
+ BrokerEventImpl::Ptr eventMethodResponse(void* context, MethodResponsePtr response);
+
+ void handleBrokerResponse(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ void handlePackageIndication(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ void handleCommandComplete(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ void handleClassIndication(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ MethodResponsePtr handleMethodResponse(qpid::framing::Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema);
+ void handleHeartbeatIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, const std::string& routingKey);
+ void handleEventIndication(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ void handleSchemaResponse(qpid::framing::Buffer& inBuffer, uint32_t seq);
+ ObjectPtr handleObjectIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, bool prop, bool stat);
+ void updateAgentList(ObjectPtr obj);
+ void incOutstandingLH();
+ void decOutstanding();
+ };
+
+ //
+ // StaticContext is used to handle:
+ //
+ // 1) Responses to console-level requests (for schema info, etc.)
+ // 2) Unsolicited messages from agents (events, published updates, etc.)
+ //
+ struct StaticContext : public SequenceContext {
+ StaticContext(BrokerProxyImpl& b) : broker(b) {}
+ virtual ~StaticContext() {}
+ void reserve() {}
+ void release() { broker.staticRelease(); }
+ bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer);
+ BrokerProxyImpl& broker;
+ };
+
+ //
+ // QueryContext is used to track and handle responses associated with a single Get Query
+ //
+ struct QueryContext : public SequenceContext {
+ QueryContext(BrokerProxyImpl& b, void* u) :
+ broker(b), userContext(u), requestsOutstanding(0), queryResponse(QueryResponseImpl::factory()) {}
+ virtual ~QueryContext() {}
+ void reserve();
+ void release();
+ bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer);
+
+ mutable qpid::sys::Mutex lock;
+ BrokerProxyImpl& broker;
+ void* userContext;
+ uint32_t requestsOutstanding;
+ QueryResponsePtr queryResponse;
+ };
+
+ struct MethodContext : public SequenceContext {
+ MethodContext(BrokerProxyImpl& b, void* u, const SchemaMethod* s) : broker(b), userContext(u), schema(s) {}
+ virtual ~MethodContext() {}
+ void reserve() {}
+ void release();
+ bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer);
+
+ BrokerProxyImpl& broker;
+ void* userContext;
+ const SchemaMethod* schema;
+ MethodResponsePtr methodResponse;
+ };
+
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp
new file mode 100644
index 0000000000..22a65f28ca
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/ConnectionSettingsImpl.h"
+#include "qmf/engine/Typecode.h"
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid;
+
+const string attrProtocol("protocol");
+const string attrHost("host");
+const string attrPort("port");
+const string attrVirtualhost("virtualhost");
+const string attrUsername("username");
+const string attrPassword("password");
+const string attrMechanism("mechanism");
+const string attrLocale("locale");
+const string attrHeartbeat("heartbeat");
+const string attrMaxChannels("maxChannels");
+const string attrMaxFrameSize("maxFrameSize");
+const string attrBounds("bounds");
+const string attrTcpNoDelay("tcpNoDelay");
+const string attrService("service");
+const string attrMinSsf("minSsf");
+const string attrMaxSsf("maxSsf");
+const string attrRetryDelayMin("retryDelayMin");
+const string attrRetryDelayMax("retryDelayMax");
+const string attrRetryDelayFactor("retryDelayFactor");
+const string attrSendUserId("sendUserId");
+
+ConnectionSettingsImpl::ConnectionSettingsImpl() :
+ retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true)
+{
+}
+
+ConnectionSettingsImpl::ConnectionSettingsImpl(const string& /*url*/) :
+ retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true)
+{
+ // TODO: Parse the URL
+}
+
+bool ConnectionSettingsImpl::setAttr(const string& key, const Value& value)
+{
+ if (key == attrProtocol) clientSettings.protocol = value.asString();
+ else if (key == attrHost) clientSettings.host = value.asString();
+ else if (key == attrPort) clientSettings.port = value.asUint();
+ else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString();
+ else if (key == attrUsername) clientSettings.username = value.asString();
+ else if (key == attrPassword) clientSettings.password = value.asString();
+ else if (key == attrMechanism) clientSettings.mechanism = value.asString();
+ else if (key == attrLocale) clientSettings.locale = value.asString();
+ else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint();
+ else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint();
+ else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint();
+ else if (key == attrBounds) clientSettings.bounds = value.asUint();
+ else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool();
+ else if (key == attrService) clientSettings.service = value.asString();
+ else if (key == attrMinSsf) clientSettings.minSsf = value.asUint();
+ else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint();
+
+ else if (key == attrRetryDelayMin) retryDelayMin = value.asUint();
+ else if (key == attrRetryDelayMax) retryDelayMax = value.asUint();
+ else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint();
+ else if (key == attrSendUserId) sendUserId = value.asBool();
+ else
+ return false;
+ return true;
+}
+
+Value ConnectionSettingsImpl::getAttr(const string& key) const
+{
+ Value strval(TYPE_LSTR);
+ Value intval(TYPE_UINT32);
+ Value boolval(TYPE_BOOL);
+
+ if (key == attrProtocol) {
+ strval.setString(clientSettings.protocol.c_str());
+ return strval;
+ }
+
+ if (key == attrHost) {
+ strval.setString(clientSettings.host.c_str());
+ return strval;
+ }
+
+ if (key == attrPort) {
+ intval.setUint(clientSettings.port);
+ return intval;
+ }
+
+ if (key == attrVirtualhost) {
+ strval.setString(clientSettings.virtualhost.c_str());
+ return strval;
+ }
+
+ if (key == attrUsername) {
+ strval.setString(clientSettings.username.c_str());
+ return strval;
+ }
+
+ if (key == attrPassword) {
+ strval.setString(clientSettings.password.c_str());
+ return strval;
+ }
+
+ if (key == attrMechanism) {
+ strval.setString(clientSettings.mechanism.c_str());
+ return strval;
+ }
+
+ if (key == attrLocale) {
+ strval.setString(clientSettings.locale.c_str());
+ return strval;
+ }
+
+ if (key == attrHeartbeat) {
+ intval.setUint(clientSettings.heartbeat);
+ return intval;
+ }
+
+ if (key == attrMaxChannels) {
+ intval.setUint(clientSettings.maxChannels);
+ return intval;
+ }
+
+ if (key == attrMaxFrameSize) {
+ intval.setUint(clientSettings.maxFrameSize);
+ return intval;
+ }
+
+ if (key == attrBounds) {
+ intval.setUint(clientSettings.bounds);
+ return intval;
+ }
+
+ if (key == attrTcpNoDelay) {
+ boolval.setBool(clientSettings.tcpNoDelay);
+ return boolval;
+ }
+
+ if (key == attrService) {
+ strval.setString(clientSettings.service.c_str());
+ return strval;
+ }
+
+ if (key == attrMinSsf) {
+ intval.setUint(clientSettings.minSsf);
+ return intval;
+ }
+
+ if (key == attrMaxSsf) {
+ intval.setUint(clientSettings.maxSsf);
+ return intval;
+ }
+
+ if (key == attrRetryDelayMin) {
+ intval.setUint(retryDelayMin);
+ return intval;
+ }
+
+ if (key == attrRetryDelayMax) {
+ intval.setUint(retryDelayMax);
+ return intval;
+ }
+
+ if (key == attrRetryDelayFactor) {
+ intval.setUint(retryDelayFactor);
+ return intval;
+ }
+
+ if (key == attrSendUserId) {
+ boolval.setBool(sendUserId);
+ return boolval;
+ }
+
+ return strval;
+}
+
+const string& ConnectionSettingsImpl::getAttrString() const
+{
+ // TODO: build and return attribute string
+ return attrString;
+}
+
+void ConnectionSettingsImpl::transportTcp(uint16_t port)
+{
+ clientSettings.protocol = "tcp";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::transportSsl(uint16_t port)
+{
+ clientSettings.protocol = "ssl";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::transportRdma(uint16_t port)
+{
+ clientSettings.protocol = "rdma";
+ clientSettings.port = port;
+}
+
+void ConnectionSettingsImpl::authAnonymous(const string& username)
+{
+ clientSettings.mechanism = "ANONYMOUS";
+ clientSettings.username = username;
+}
+
+void ConnectionSettingsImpl::authPlain(const string& username, const string& password)
+{
+ clientSettings.mechanism = "PLAIN";
+ clientSettings.username = username;
+ clientSettings.password = password;
+}
+
+void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf)
+{
+ clientSettings.mechanism = "GSSAPI";
+ clientSettings.service = serviceName;
+ clientSettings.minSsf = minSsf;
+ clientSettings.maxSsf = maxSsf;
+}
+
+void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor)
+{
+ retryDelayMin = delayMin;
+ retryDelayMax = delayMax;
+ retryDelayFactor = delayFactor;
+}
+
+const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const
+{
+ return clientSettings;
+}
+
+void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const
+{
+ *min = retryDelayMin;
+ *max = retryDelayMax;
+ *factor = retryDelayFactor;
+}
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+ConnectionSettings::ConnectionSettings(const ConnectionSettings& from) { impl = new ConnectionSettingsImpl(*from.impl); }
+ConnectionSettings::ConnectionSettings() { impl = new ConnectionSettingsImpl(); }
+ConnectionSettings::ConnectionSettings(const char* url) { impl = new ConnectionSettingsImpl(url); }
+ConnectionSettings::~ConnectionSettings() { delete impl; }
+bool ConnectionSettings::setAttr(const char* key, const Value& value) { return impl->setAttr(key, value); }
+Value ConnectionSettings::getAttr(const char* key) const { return impl->getAttr(key); }
+const char* ConnectionSettings::getAttrString() const { return impl->getAttrString().c_str(); }
+void ConnectionSettings::transportTcp(uint16_t port) { impl->transportTcp(port); }
+void ConnectionSettings::transportSsl(uint16_t port) { impl->transportSsl(port); }
+void ConnectionSettings::transportRdma(uint16_t port) { impl->transportRdma(port); }
+void ConnectionSettings::authAnonymous(const char* username) { impl->authAnonymous(username); }
+void ConnectionSettings::authPlain(const char* username, const char* password) { impl->authPlain(username, password); }
+void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf) { impl->authGssapi(serviceName, minSsf, maxSsf); }
+void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor) { impl->setRetry(delayMin, delayMax, delayFactor); }
+
diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h
new file mode 100644
index 0000000000..98bf87868b
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h
@@ -0,0 +1,63 @@
+#ifndef _QmfEngineConnectionSettingsImpl_
+#define _QmfEngineConnectionSettingsImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/ConnectionSettings.h"
+#include "qmf/engine/Value.h"
+#include "qpid/client/ConnectionSettings.h"
+#include <string>
+#include <map>
+
+namespace qmf {
+namespace engine {
+
+ class ConnectionSettingsImpl {
+ qpid::client::ConnectionSettings clientSettings;
+ mutable std::string attrString;
+ int retryDelayMin;
+ int retryDelayMax;
+ int retryDelayFactor;
+ bool sendUserId;
+
+ public:
+ ConnectionSettingsImpl();
+ ConnectionSettingsImpl(const std::string& url);
+ ~ConnectionSettingsImpl() {}
+ bool setAttr(const std::string& key, const Value& value);
+ Value getAttr(const std::string& key) const;
+ const std::string& getAttrString() const;
+ void transportTcp(uint16_t port);
+ void transportSsl(uint16_t port);
+ void transportRdma(uint16_t port);
+ void authAnonymous(const std::string& username);
+ void authPlain(const std::string& username, const std::string& password);
+ void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf);
+ void setRetry(int delayMin, int delayMax, int delayFactor);
+
+ const qpid::client::ConnectionSettings& getClientSettings() const;
+ void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const;
+ bool getSendUserId() const { return sendUserId; }
+ };
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp
new file mode 100644
index 0000000000..4a5da31bdc
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp
@@ -0,0 +1,458 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/ConsoleImpl.h"
+#include "qmf/engine/MessageImpl.h"
+#include "qmf/engine/SchemaImpl.h"
+#include "qmf/engine/Typecode.h"
+#include "qmf/engine/ObjectImpl.h"
+#include "qmf/engine/ObjectIdImpl.h"
+#include "qmf/engine/QueryImpl.h"
+#include "qmf/engine/ValueImpl.h"
+#include "qmf/engine/Protocol.h"
+#include "qmf/engine/SequenceManager.h"
+#include "qmf/engine/BrokerProxyImpl.h"
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/Uuid.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/FieldValue.h>
+#include <qpid/log/Statement.h>
+#include <qpid/sys/Time.h>
+#include <qpid/sys/SystemInfo.h>
+#include <string.h>
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace {
+ const char* QMF_EXCHANGE = "qpid.management";
+}
+
+#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());}
+
+ConsoleEvent ConsoleEventImpl::copy()
+{
+ ConsoleEvent item;
+
+ ::memset(&item, 0, sizeof(ConsoleEvent));
+ item.kind = kind;
+ item.agent = agent.get();
+ item.classKey = classKey;
+ item.object = object.get();
+ item.context = context;
+ item.event = event.get();
+ item.timestamp = timestamp;
+ item.hasProps = hasProps;
+ item.hasStats = hasStats;
+
+ STRING_REF(name);
+
+ return item;
+}
+
+ConsoleImpl::ConsoleImpl(const ConsoleSettings& s) : settings(s)
+{
+ bindingList.push_back(pair<string, string>(string(), "schema.#"));
+ if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) {
+ bindingList.push_back(pair<string, string>(string(), "console.#"));
+ } else {
+ if (settings.rcvObjects && !settings.userBindings)
+ bindingList.push_back(pair<string, string>(string(), "console.obj.#"));
+ else
+ bindingList.push_back(pair<string, string>(string(), "console.obj.*.*.org.apache.qpid.broker.agent"));
+ if (settings.rcvEvents)
+ bindingList.push_back(pair<string, string>(string(), "console.event.#"));
+ if (settings.rcvHeartbeats)
+ bindingList.push_back(pair<string, string>(string(), "console.heartbeat.#"));
+ }
+}
+
+ConsoleImpl::~ConsoleImpl()
+{
+ // This function intentionally left blank.
+}
+
+bool ConsoleImpl::getEvent(ConsoleEvent& event) const
+{
+ Mutex::ScopedLock _lock(lock);
+ if (eventQueue.empty())
+ return false;
+ event = eventQueue.front()->copy();
+ return true;
+}
+
+void ConsoleImpl::popEvent()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!eventQueue.empty())
+ eventQueue.pop_front();
+}
+
+void ConsoleImpl::addConnection(BrokerProxy& broker, void* /*context*/)
+{
+ Mutex::ScopedLock _lock(lock);
+ brokerList.push_back(broker.impl);
+}
+
+void ConsoleImpl::delConnection(BrokerProxy& broker)
+{
+ Mutex::ScopedLock _lock(lock);
+ for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin();
+ iter != brokerList.end(); iter++)
+ if (*iter == broker.impl) {
+ brokerList.erase(iter);
+ break;
+ }
+}
+
+uint32_t ConsoleImpl::packageCount() const
+{
+ Mutex::ScopedLock _lock(lock);
+ return packages.size();
+}
+
+const string& ConsoleImpl::getPackageName(uint32_t idx) const
+{
+ const static string empty;
+
+ Mutex::ScopedLock _lock(lock);
+ if (idx >= packages.size())
+ return empty;
+
+ PackageList::const_iterator iter = packages.begin();
+ for (uint32_t i = 0; i < idx; i++) iter++;
+ return iter->first;
+}
+
+uint32_t ConsoleImpl::classCount(const char* packageName) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(packageName);
+ if (pIter == packages.end())
+ return 0;
+
+ const ObjectClassList& oList = pIter->second.first;
+ const EventClassList& eList = pIter->second.second;
+
+ return oList.size() + eList.size();
+}
+
+const SchemaClassKey* ConsoleImpl::getClass(const char* packageName, uint32_t idx) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(packageName);
+ if (pIter == packages.end())
+ return 0;
+
+ const ObjectClassList& oList = pIter->second.first;
+ const EventClassList& eList = pIter->second.second;
+ uint32_t count = 0;
+
+ for (ObjectClassList::const_iterator oIter = oList.begin();
+ oIter != oList.end(); oIter++) {
+ if (count == idx)
+ return oIter->second->getClassKey();
+ count++;
+ }
+
+ for (EventClassList::const_iterator eIter = eList.begin();
+ eIter != eList.end(); eIter++) {
+ if (count == idx)
+ return eIter->second->getClassKey();
+ count++;
+ }
+
+ return 0;
+}
+
+ClassKind ConsoleImpl::getClassKind(const SchemaClassKey* key) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return CLASS_OBJECT;
+
+ const EventClassList& eList = pIter->second.second;
+ if (eList.find(key) != eList.end())
+ return CLASS_EVENT;
+ return CLASS_OBJECT;
+}
+
+const SchemaObjectClass* ConsoleImpl::getObjectClass(const SchemaClassKey* key) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return 0;
+
+ const ObjectClassList& oList = pIter->second.first;
+ ObjectClassList::const_iterator iter = oList.find(key);
+ if (iter == oList.end())
+ return 0;
+ return iter->second;
+}
+
+const SchemaEventClass* ConsoleImpl::getEventClass(const SchemaClassKey* key) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return 0;
+
+ const EventClassList& eList = pIter->second.second;
+ EventClassList::const_iterator iter = eList.find(key);
+ if (iter == eList.end())
+ return 0;
+ return iter->second;
+}
+
+void ConsoleImpl::bindPackage(const char* packageName)
+{
+ stringstream key;
+ key << "console.obj.*.*." << packageName << ".#";
+ Mutex::ScopedLock _lock(lock);
+ bindingList.push_back(pair<string, string>(string(), key.str()));
+ for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin();
+ iter != brokerList.end(); iter++)
+ (*iter)->addBinding(QMF_EXCHANGE, key.str());
+}
+
+void ConsoleImpl::bindClass(const SchemaClassKey* classKey)
+{
+ stringstream key;
+ key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#";
+ Mutex::ScopedLock _lock(lock);
+ bindingList.push_back(pair<string, string>(string(), key.str()));
+ for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin();
+ iter != brokerList.end(); iter++)
+ (*iter)->addBinding(QMF_EXCHANGE, key.str());
+}
+
+void ConsoleImpl::bindClass(const char* packageName, const char* className)
+{
+ stringstream key;
+ key << "console.obj.*.*." << packageName << "." << className << ".#";
+ Mutex::ScopedLock _lock(lock);
+ bindingList.push_back(pair<string, string>(string(), key.str()));
+ for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin();
+ iter != brokerList.end(); iter++)
+ (*iter)->addBinding(QMF_EXCHANGE, key.str());
+}
+
+
+void ConsoleImpl::bindEvent(const SchemaClassKey* classKey)
+{
+ bindEvent(classKey->getPackageName(), classKey->getClassName());
+}
+
+void ConsoleImpl::bindEvent(const char* packageName, const char* eventName)
+{
+ if (!settings.userBindings) throw qpid::Exception("Console not configured for userBindings.");
+ if (settings.rcvEvents) throw qpid::Exception("Console already configured to receive all events.");
+
+ stringstream key;
+ key << "console.event.*.*." << packageName;
+ if (eventName && *eventName) {
+ key << "." << eventName << ".#";
+ } else {
+ key << ".#";
+ }
+
+ Mutex::ScopedLock _lock(lock);
+ bindingList.push_back(pair<string, string>(string(), key.str()));
+ for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin();
+ iter != brokerList.end(); iter++)
+ (*iter)->addBinding(QMF_EXCHANGE, key.str());
+}
+
+/*
+void ConsoleImpl::startSync(const Query& query, void* context, SyncQuery& sync)
+{
+}
+
+void ConsoleImpl::touchSync(SyncQuery& sync)
+{
+}
+
+void ConsoleImpl::endSync(SyncQuery& sync)
+{
+}
+*/
+
+void ConsoleImpl::learnPackage(const string& packageName)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (packages.find(packageName) == packages.end()) {
+ packages.insert(pair<string, pair<ObjectClassList, EventClassList> >
+ (packageName, pair<ObjectClassList, EventClassList>(ObjectClassList(), EventClassList())));
+ eventNewPackage(packageName);
+ }
+}
+
+void ConsoleImpl::learnClass(SchemaObjectClass* cls)
+{
+ Mutex::ScopedLock _lock(lock);
+ const SchemaClassKey* key = cls->getClassKey();
+ PackageList::iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return;
+
+ ObjectClassList& list = pIter->second.first;
+ if (list.find(key) == list.end()) {
+ list[key] = cls;
+ eventNewClass(key);
+ }
+}
+
+void ConsoleImpl::learnClass(SchemaEventClass* cls)
+{
+ Mutex::ScopedLock _lock(lock);
+ const SchemaClassKey* key = cls->getClassKey();
+ PackageList::iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return;
+
+ EventClassList& list = pIter->second.second;
+ if (list.find(key) == list.end()) {
+ list[key] = cls;
+ eventNewClass(key);
+ }
+}
+
+bool ConsoleImpl::haveClass(const SchemaClassKey* key) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return false;
+
+ const ObjectClassList& oList = pIter->second.first;
+ const EventClassList& eList = pIter->second.second;
+
+ return oList.find(key) != oList.end() || eList.find(key) != eList.end();
+}
+
+SchemaObjectClass* ConsoleImpl::getSchema(const SchemaClassKey* key) const
+{
+ Mutex::ScopedLock _lock(lock);
+ PackageList::const_iterator pIter = packages.find(key->getPackageName());
+ if (pIter == packages.end())
+ return 0;
+
+ const ObjectClassList& oList = pIter->second.first;
+ ObjectClassList::const_iterator iter = oList.find(key);
+ if (iter == oList.end())
+ return 0;
+
+ return iter->second;
+}
+
+void ConsoleImpl::eventAgentAdded(boost::shared_ptr<AgentProxy> agent)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_ADDED));
+ event->agent = agent;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+void ConsoleImpl::eventAgentDeleted(boost::shared_ptr<AgentProxy> agent)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_DELETED));
+ event->agent = agent;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+void ConsoleImpl::eventNewPackage(const string& packageName)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_PACKAGE));
+ event->name = packageName;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+void ConsoleImpl::eventNewClass(const SchemaClassKey* key)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_CLASS));
+ event->classKey = key;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+void ConsoleImpl::eventObjectUpdate(ObjectPtr object, bool prop, bool stat)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::OBJECT_UPDATE));
+ event->object = object;
+ event->hasProps = prop;
+ event->hasStats = stat;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+void ConsoleImpl::eventAgentHeartbeat(boost::shared_ptr<AgentProxy> agent, uint64_t timestamp)
+{
+ ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_HEARTBEAT));
+ event->agent = agent;
+ event->timestamp = timestamp;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(event);
+}
+
+
+void ConsoleImpl::eventEventReceived(EventPtr event)
+{
+ ConsoleEventImpl::Ptr console_event(new ConsoleEventImpl(ConsoleEvent::EVENT_RECEIVED));
+ console_event->event = event;
+ Mutex::ScopedLock _lock(lock);
+ eventQueue.push_back(console_event);
+}
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+Console::Console(const ConsoleSettings& settings) : impl(new ConsoleImpl(settings)) {}
+Console::~Console() { delete impl; }
+bool Console::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); }
+void Console::popEvent() { impl->popEvent(); }
+void Console::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); }
+void Console::delConnection(BrokerProxy& broker) { impl->delConnection(broker); }
+uint32_t Console::packageCount() const { return impl->packageCount(); }
+const char* Console::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); }
+uint32_t Console::classCount(const char* packageName) const { return impl->classCount(packageName); }
+const SchemaClassKey* Console::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); }
+ClassKind Console::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); }
+const SchemaObjectClass* Console::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); }
+const SchemaEventClass* Console::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); }
+void Console::bindPackage(const char* packageName) { impl->bindPackage(packageName); }
+void Console::bindClass(const SchemaClassKey* key) { impl->bindClass(key); }
+void Console::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); }
+
+void Console::bindEvent(const SchemaClassKey *key) { impl->bindEvent(key); }
+void Console::bindEvent(const char* packageName, const char* eventName) { impl->bindEvent(packageName, eventName); }
+
+//void Console::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); }
+//void Console::touchSync(SyncQuery& sync) { impl->touchSync(sync); }
+//void Console::endSync(SyncQuery& sync) { impl->endSync(sync); }
+
+
diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.h b/qpid/cpp/src/qmf/engine/ConsoleImpl.h
new file mode 100644
index 0000000000..0c27fdabcd
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.h
@@ -0,0 +1,148 @@
+#ifndef _QmfEngineConsoleEngineImpl_
+#define _QmfEngineConsoleEngineImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Console.h"
+#include "qmf/engine/MessageImpl.h"
+#include "qmf/engine/SchemaImpl.h"
+#include "qmf/engine/Typecode.h"
+#include "qmf/engine/ObjectImpl.h"
+#include "qmf/engine/ObjectIdImpl.h"
+#include "qmf/engine/QueryImpl.h"
+#include "qmf/engine/ValueImpl.h"
+#include "qmf/engine/Protocol.h"
+#include "qmf/engine/SequenceManager.h"
+#include "qmf/engine/BrokerProxyImpl.h"
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/Uuid.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/FieldValue.h>
+#include <qpid/sys/Mutex.h>
+#include <qpid/sys/Time.h>
+#include <qpid/sys/SystemInfo.h>
+#include <string.h>
+#include <string>
+#include <deque>
+#include <map>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace qmf {
+namespace engine {
+
+ struct ConsoleEventImpl {
+ typedef boost::shared_ptr<ConsoleEventImpl> Ptr;
+ ConsoleEvent::EventKind kind;
+ boost::shared_ptr<AgentProxy> agent;
+ std::string name;
+ const SchemaClassKey* classKey;
+ boost::shared_ptr<Object> object;
+ void* context;
+ boost::shared_ptr<Event> event;
+ uint64_t timestamp;
+ bool hasProps;
+ bool hasStats;
+
+ ConsoleEventImpl(ConsoleEvent::EventKind k) :
+ kind(k), classKey(0), context(0), timestamp(0) {}
+ ~ConsoleEventImpl() {}
+ ConsoleEvent copy();
+ };
+
+ class ConsoleImpl : public boost::noncopyable {
+ public:
+ ConsoleImpl(const ConsoleSettings& settings = ConsoleSettings());
+ ~ConsoleImpl();
+
+ bool getEvent(ConsoleEvent& event) const;
+ void popEvent();
+
+ void addConnection(BrokerProxy& broker, void* context);
+ void delConnection(BrokerProxy& broker);
+
+ uint32_t packageCount() const;
+ const std::string& getPackageName(uint32_t idx) const;
+
+ uint32_t classCount(const char* packageName) const;
+ const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const;
+
+ ClassKind getClassKind(const SchemaClassKey* key) const;
+ const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const;
+ const SchemaEventClass* getEventClass(const SchemaClassKey* key) const;
+
+ void bindPackage(const char* packageName);
+ void bindClass(const SchemaClassKey* key);
+ void bindClass(const char* packageName, const char* className);
+ void bindEvent(const SchemaClassKey* key);
+ void bindEvent(const char* packageName, const char* eventName);
+
+ /*
+ void startSync(const Query& query, void* context, SyncQuery& sync);
+ void touchSync(SyncQuery& sync);
+ void endSync(SyncQuery& sync);
+ */
+
+ private:
+ friend class BrokerProxyImpl;
+ friend struct StaticContext;
+ const ConsoleSettings& settings;
+ mutable qpid::sys::Mutex lock;
+ std::deque<ConsoleEventImpl::Ptr> eventQueue;
+ std::vector<BrokerProxyImpl*> brokerList;
+ std::vector<std::pair<std::string, std::string> > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE)
+
+ // Declare a compare class for the class maps that compares the dereferenced
+ // class key pointers. The default behavior would be to compare the pointer
+ // addresses themselves.
+ struct KeyCompare {
+ bool operator()(const SchemaClassKey* left, const SchemaClassKey* right) const {
+ return *left < *right;
+ }
+ };
+
+ typedef std::map<const SchemaClassKey*, SchemaObjectClass*, KeyCompare> ObjectClassList;
+ typedef std::map<const SchemaClassKey*, SchemaEventClass*, KeyCompare> EventClassList;
+ typedef std::map<std::string, std::pair<ObjectClassList, EventClassList> > PackageList;
+
+ PackageList packages;
+
+ void learnPackage(const std::string& packageName);
+ void learnClass(SchemaObjectClass* cls);
+ void learnClass(SchemaEventClass* cls);
+ bool haveClass(const SchemaClassKey* key) const;
+ SchemaObjectClass* getSchema(const SchemaClassKey* key) const;
+
+ void eventAgentAdded(boost::shared_ptr<AgentProxy> agent);
+ void eventAgentDeleted(boost::shared_ptr<AgentProxy> agent);
+ void eventNewPackage(const std::string& packageName);
+ void eventNewClass(const SchemaClassKey* key);
+ void eventObjectUpdate(ObjectPtr object, bool prop, bool stat);
+ void eventAgentHeartbeat(boost::shared_ptr<AgentProxy> agent, uint64_t timestamp);
+ void eventEventReceived(boost::shared_ptr<Event> event);
+ };
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/EventImpl.cpp b/qpid/cpp/src/qmf/engine/EventImpl.cpp
new file mode 100644
index 0000000000..4b034e8e83
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/EventImpl.cpp
@@ -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.
+ */
+
+#include <qmf/engine/EventImpl.h>
+#include <qmf/engine/ValueImpl.h>
+
+#include <sstream>
+
+using namespace std;
+using namespace qmf::engine;
+using qpid::framing::Buffer;
+
+EventImpl::EventImpl(const SchemaEventClass* type) : eventClass(type), timestamp(0), severity(0)
+{
+ int argCount = eventClass->getArgumentCount();
+ int idx;
+
+ for (idx = 0; idx < argCount; idx++) {
+ const SchemaArgument* arg = eventClass->getArgument(idx);
+ arguments[arg->getName()] = ValuePtr(new Value(arg->getType()));
+ }
+}
+
+
+EventImpl::EventImpl(const SchemaEventClass* type, Buffer& buffer) :
+ eventClass(type), timestamp(0), severity(0)
+{
+ int argCount = eventClass->getArgumentCount();
+ int idx;
+
+ timestamp = buffer.getLongLong();
+ severity = buffer.getOctet();
+
+ for (idx = 0; idx < argCount; idx++)
+ {
+ const SchemaArgument *arg = eventClass->getArgument(idx);
+ Value* pval = ValueImpl::factory(arg->getType(), buffer);
+ arguments[arg->getName()] = ValuePtr(pval);
+ }
+}
+
+
+Event* EventImpl::factory(const SchemaEventClass* type, Buffer& buffer)
+{
+ EventImpl* impl(new EventImpl(type, buffer));
+ return new Event(impl);
+}
+
+
+Value* EventImpl::getValue(const char* key) const
+{
+ map<string, ValuePtr>::const_iterator iter;
+
+ iter = arguments.find(key);
+ if (iter != arguments.end())
+ return iter->second.get();
+
+ return 0;
+}
+
+
+void EventImpl::encodeSchemaKey(Buffer& buffer) const
+{
+ buffer.putShortString(eventClass->getClassKey()->getPackageName());
+ buffer.putShortString(eventClass->getClassKey()->getClassName());
+ buffer.putBin128(const_cast<uint8_t*>(eventClass->getClassKey()->getHash()));
+}
+
+
+void EventImpl::encode(Buffer& buffer) const
+{
+ buffer.putOctet((uint8_t) eventClass->getSeverity());
+
+ int argCount = eventClass->getArgumentCount();
+ for (int idx = 0; idx < argCount; idx++) {
+ const SchemaArgument* arg = eventClass->getArgument(idx);
+ ValuePtr value = arguments[arg->getName()];
+ value->impl->encode(buffer);
+ }
+}
+
+
+string EventImpl::getRoutingKey(uint32_t brokerBank, uint32_t agentBank) const
+{
+ stringstream key;
+
+ key << "console.event." << brokerBank << "." << agentBank << "." <<
+ eventClass->getClassKey()->getPackageName() << "." <<
+ eventClass->getClassKey()->getClassName();
+ return key.str();
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+Event::Event(const SchemaEventClass* type) : impl(new EventImpl(type)) {}
+Event::Event(EventImpl* i) : impl(i) {}
+Event::Event(const Event& from) : impl(new EventImpl(*(from.impl))) {}
+Event::~Event() { delete impl; }
+const SchemaEventClass* Event::getClass() const { return impl->getClass(); }
+Value* Event::getValue(const char* key) const { return impl->getValue(key); }
+
diff --git a/qpid/cpp/src/qmf/engine/EventImpl.h b/qpid/cpp/src/qmf/engine/EventImpl.h
new file mode 100644
index 0000000000..4046e71ef9
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/EventImpl.h
@@ -0,0 +1,57 @@
+#ifndef _QmfEngineEventImpl_
+#define _QmfEngineEventImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qmf/engine/Event.h>
+#include <qmf/engine/Schema.h>
+#include <qpid/framing/Buffer.h>
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+namespace qmf {
+namespace engine {
+
+ typedef boost::shared_ptr<Event> EventPtr;
+
+ struct EventImpl {
+ typedef boost::shared_ptr<Value> ValuePtr;
+ const SchemaEventClass* eventClass;
+ uint64_t timestamp;
+ uint8_t severity;
+ mutable std::map<std::string, ValuePtr> arguments;
+
+ EventImpl(const SchemaEventClass* type);
+ EventImpl(const SchemaEventClass* type, qpid::framing::Buffer& buffer);
+ static Event* factory(const SchemaEventClass* type, qpid::framing::Buffer& buffer);
+
+ const SchemaEventClass* getClass() const { return eventClass; }
+ Value* getValue(const char* key) const;
+
+ void encodeSchemaKey(qpid::framing::Buffer& buffer) const;
+ void encode(qpid::framing::Buffer& buffer) const;
+ std::string getRoutingKey(uint32_t brokerBank, uint32_t agentBank) const;
+ };
+
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.cpp b/qpid/cpp/src/qmf/engine/MessageImpl.cpp
new file mode 100644
index 0000000000..0047d3eb9d
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/MessageImpl.cpp
@@ -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.
+ */
+
+#include "qmf/engine/MessageImpl.h"
+#include <string.h>
+
+using namespace std;
+using namespace qmf::engine;
+
+#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());}
+
+Message MessageImpl::copy()
+{
+ Message item;
+
+ ::memset(&item, 0, sizeof(Message));
+ item.body = const_cast<char*>(body.c_str());
+ item.length = body.length();
+ STRING_REF(destination);
+ STRING_REF(routingKey);
+ STRING_REF(replyExchange);
+ STRING_REF(replyKey);
+ STRING_REF(userId);
+
+ return item;
+}
+
diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.h b/qpid/cpp/src/qmf/engine/MessageImpl.h
new file mode 100644
index 0000000000..b91291d2e4
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/MessageImpl.h
@@ -0,0 +1,44 @@
+#ifndef _QmfEngineMessageImpl_
+#define _QmfEngineMessageImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Message.h"
+#include <string>
+#include <boost/shared_ptr.hpp>
+
+namespace qmf {
+namespace engine {
+
+ struct MessageImpl {
+ typedef boost::shared_ptr<MessageImpl> Ptr;
+ std::string body;
+ std::string destination;
+ std::string routingKey;
+ std::string replyExchange;
+ std::string replyKey;
+ std::string userId;
+
+ Message copy();
+ };
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp
new file mode 100644
index 0000000000..9216f7bac0
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp
@@ -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.
+ */
+
+#include "qmf/engine/ObjectIdImpl.h"
+#include <stdlib.h>
+#include <sstream>
+
+using namespace std;
+using namespace qmf::engine;
+using qpid::framing::Buffer;
+
+void AgentAttachment::setBanks(uint32_t broker, uint32_t agent)
+{
+ first =
+ ((uint64_t) (broker & 0x000fffff)) << 28 |
+ ((uint64_t) (agent & 0x0fffffff));
+}
+
+ObjectIdImpl::ObjectIdImpl(Buffer& buffer) : agent(0)
+{
+ decode(buffer);
+}
+
+ObjectIdImpl::ObjectIdImpl(AgentAttachment* a, uint8_t flags, uint16_t seq, uint64_t object) : agent(a)
+{
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48;
+ second = object;
+}
+
+ObjectId* ObjectIdImpl::factory(Buffer& buffer)
+{
+ ObjectIdImpl* impl(new ObjectIdImpl(buffer));
+ return new ObjectId(impl);
+}
+
+ObjectId* ObjectIdImpl::factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object)
+{
+ ObjectIdImpl* impl(new ObjectIdImpl(agent, flags, seq, object));
+ return new ObjectId(impl);
+}
+
+void ObjectIdImpl::decode(Buffer& buffer)
+{
+ first = buffer.getLongLong();
+ second = buffer.getLongLong();
+}
+
+void ObjectIdImpl::encode(Buffer& buffer) const
+{
+ if (agent == 0)
+ buffer.putLongLong(first);
+ else
+ buffer.putLongLong(first | agent->first);
+ buffer.putLongLong(second);
+}
+
+void ObjectIdImpl::fromString(const std::string& repr)
+{
+#define FIELDS 5
+#if defined (_WIN32) && !defined (atoll)
+# define atoll(X) _atoi64(X)
+#endif
+
+ std::string copy(repr.c_str());
+ char* cText;
+ char* field[FIELDS];
+ bool atFieldStart = true;
+ int idx = 0;
+
+ cText = const_cast<char*>(copy.c_str());
+ for (char* cursor = cText; *cursor; cursor++) {
+ if (atFieldStart) {
+ if (idx >= FIELDS)
+ return; // TODO error
+ field[idx++] = cursor;
+ atFieldStart = false;
+ } else {
+ if (*cursor == '-') {
+ *cursor = '\0';
+ atFieldStart = true;
+ }
+ }
+ }
+
+ if (idx != FIELDS)
+ return; // TODO error
+
+ first = (atoll(field[0]) << 60) +
+ (atoll(field[1]) << 48) +
+ (atoll(field[2]) << 28) +
+ atoll(field[3]);
+ second = atoll(field[4]);
+ agent = 0;
+}
+
+const string& ObjectIdImpl::asString() const
+{
+ stringstream val;
+
+ val << (int) getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" <<
+ getAgentBank() << "-" << getObjectNum();
+ repr = val.str();
+ return repr;
+}
+
+#define ACTUAL_FIRST (agent == 0 ? first : first | agent->first)
+#define ACTUAL_OTHER (other.agent == 0 ? other.first : other.first | other.agent->first)
+
+uint8_t ObjectIdImpl::getFlags() const
+{
+ return (ACTUAL_FIRST & 0xF000000000000000LL) >> 60;
+}
+
+uint16_t ObjectIdImpl::getSequence() const
+{
+ return (ACTUAL_FIRST & 0x0FFF000000000000LL) >> 48;
+}
+
+uint32_t ObjectIdImpl::getBrokerBank() const
+{
+ return (ACTUAL_FIRST & 0x0000FFFFF0000000LL) >> 28;
+}
+
+uint32_t ObjectIdImpl::getAgentBank() const
+{
+ return ACTUAL_FIRST & 0x000000000FFFFFFFLL;
+}
+
+uint64_t ObjectIdImpl::getObjectNum() const
+{
+ return second;
+}
+
+uint32_t ObjectIdImpl::getObjectNumHi() const
+{
+ return (uint32_t) (second >> 32);
+}
+
+uint32_t ObjectIdImpl::getObjectNumLo() const
+{
+ return (uint32_t) (second & 0x00000000FFFFFFFFLL);
+}
+
+bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const
+{
+ return ACTUAL_FIRST == ACTUAL_OTHER && second == other.second;
+}
+
+bool ObjectIdImpl::operator<(const ObjectIdImpl& other) const
+{
+ return (ACTUAL_FIRST < ACTUAL_OTHER) || ((ACTUAL_FIRST == ACTUAL_OTHER) && (second < other.second));
+}
+
+bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const
+{
+ return (ACTUAL_FIRST > ACTUAL_OTHER) || ((ACTUAL_FIRST == ACTUAL_OTHER) && (second > other.second));
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+ObjectId::ObjectId() : impl(new ObjectIdImpl()) {}
+ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {}
+ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {}
+ObjectId::~ObjectId() { delete impl; }
+uint64_t ObjectId::getObjectNum() const { return impl->getObjectNum(); }
+uint32_t ObjectId::getObjectNumHi() const { return impl->getObjectNumHi(); }
+uint32_t ObjectId::getObjectNumLo() const { return impl->getObjectNumLo(); }
+bool ObjectId::isDurable() const { return impl->isDurable(); }
+const char* ObjectId::str() const { return impl->asString().c_str(); }
+uint8_t ObjectId::getFlags() const { return impl->getFlags(); }
+uint16_t ObjectId::getSequence() const { return impl->getSequence(); }
+uint32_t ObjectId::getBrokerBank() const { return impl->getBrokerBank(); }
+uint32_t ObjectId::getAgentBank() const { return impl->getAgentBank(); }
+bool ObjectId::operator==(const ObjectId& other) const { return *impl == *other.impl; }
+bool ObjectId::operator<(const ObjectId& other) const { return *impl < *other.impl; }
+bool ObjectId::operator>(const ObjectId& other) const { return *impl > *other.impl; }
+bool ObjectId::operator<=(const ObjectId& other) const { return !(*impl > *other.impl); }
+bool ObjectId::operator>=(const ObjectId& other) const { return !(*impl < *other.impl); }
+ObjectId& ObjectId::operator=(const ObjectId& other) {
+ ObjectIdImpl *old;
+ if (this != &other) {
+ old = impl;
+ impl = new ObjectIdImpl(*(other.impl));
+ if (old)
+ delete old;
+ }
+ return *this;
+}
+
diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.h b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h
new file mode 100644
index 0000000000..d70c8efff4
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h
@@ -0,0 +1,72 @@
+#ifndef _QmfEngineObjectIdImpl_
+#define _QmfEngineObjectIdImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qmf/engine/ObjectId.h>
+#include <qpid/framing/Buffer.h>
+
+namespace qmf {
+namespace engine {
+
+ struct AgentAttachment {
+ uint64_t first;
+
+ AgentAttachment() : first(0) {}
+ void setBanks(uint32_t broker, uint32_t bank);
+ uint64_t getFirst() const { return first; }
+ };
+
+ struct ObjectIdImpl {
+ AgentAttachment* agent;
+ uint64_t first;
+ uint64_t second;
+ mutable std::string repr;
+
+ ObjectIdImpl() : agent(0), first(0), second(0) {}
+ ObjectIdImpl(qpid::framing::Buffer& buffer);
+ ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object);
+
+ static ObjectId* factory(qpid::framing::Buffer& buffer);
+ static ObjectId* factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object);
+
+ void decode(qpid::framing::Buffer& buffer);
+ void encode(qpid::framing::Buffer& buffer) const;
+ void fromString(const std::string& repr);
+ const std::string& asString() const;
+ uint8_t getFlags() const;
+ uint16_t getSequence() const;
+ uint32_t getBrokerBank() const;
+ uint32_t getAgentBank() const;
+ uint64_t getObjectNum() const;
+ uint32_t getObjectNumHi() const;
+ uint32_t getObjectNumLo() const;
+ bool isDurable() const { return getSequence() == 0; }
+ void setValue(uint64_t f, uint64_t s) { first = f; second = s; agent = 0; }
+
+ bool operator==(const ObjectIdImpl& other) const;
+ bool operator<(const ObjectIdImpl& other) const;
+ bool operator>(const ObjectIdImpl& other) const;
+ };
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp
new file mode 100644
index 0000000000..45925cb804
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp
@@ -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.
+ */
+
+#include "qmf/engine/ObjectImpl.h"
+#include "qmf/engine/ValueImpl.h"
+#include "qmf/engine/BrokerProxyImpl.h"
+#include <qpid/sys/Time.h>
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::sys;
+using qpid::framing::Buffer;
+
+ObjectImpl::ObjectImpl(const SchemaObjectClass* type) : objectClass(type), broker(0), createTime(uint64_t(Duration(EPOCH, now()))), destroyTime(0), lastUpdatedTime(createTime)
+{
+ int propCount = objectClass->getPropertyCount();
+ int statCount = objectClass->getStatisticCount();
+ int idx;
+
+ for (idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* prop = objectClass->getProperty(idx);
+ properties[prop->getName()] = ValuePtr(new Value(prop->getType()));
+ }
+
+ for (idx = 0; idx < statCount; idx++) {
+ const SchemaStatistic* stat = objectClass->getStatistic(idx);
+ statistics[stat->getName()] = ValuePtr(new Value(stat->getType()));
+ }
+}
+
+ObjectImpl::ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) :
+ objectClass(type), broker(b), createTime(0), destroyTime(0), lastUpdatedTime(0)
+{
+ int idx;
+
+ if (managed) {
+ lastUpdatedTime = buffer.getLongLong();
+ createTime = buffer.getLongLong();
+ destroyTime = buffer.getLongLong();
+ objectId.reset(ObjectIdImpl::factory(buffer));
+ }
+
+ if (prop) {
+ int propCount = objectClass->getPropertyCount();
+ set<string> excludes;
+ parsePresenceMasks(buffer, excludes);
+ for (idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* prop = objectClass->getProperty(idx);
+ if (excludes.count(prop->getName()) != 0) {
+ properties[prop->getName()] = ValuePtr(new Value(prop->getType()));
+ } else {
+ Value* pval = ValueImpl::factory(prop->getType(), buffer);
+ properties[prop->getName()] = ValuePtr(pval);
+ }
+ }
+ }
+
+ if (stat) {
+ int statCount = objectClass->getStatisticCount();
+ for (idx = 0; idx < statCount; idx++) {
+ const SchemaStatistic* stat = objectClass->getStatistic(idx);
+ Value* sval = ValueImpl::factory(stat->getType(), buffer);
+ statistics[stat->getName()] = ValuePtr(sval);
+ }
+ }
+}
+
+Object* ObjectImpl::factory(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed)
+{
+ ObjectImpl* impl(new ObjectImpl(type, b, buffer, prop, stat, managed));
+ return new Object(impl);
+}
+
+ObjectImpl::~ObjectImpl()
+{
+}
+
+void ObjectImpl::destroy()
+{
+ destroyTime = uint64_t(Duration(EPOCH, now()));
+ // TODO - flag deletion
+}
+
+Value* ObjectImpl::getValue(const string& key) const
+{
+ map<string, ValuePtr>::const_iterator iter;
+
+ iter = properties.find(key);
+ if (iter != properties.end())
+ return iter->second.get();
+
+ iter = statistics.find(key);
+ if (iter != statistics.end())
+ return iter->second.get();
+
+ return 0;
+}
+
+void ObjectImpl::invokeMethod(const string& methodName, const Value* inArgs, void* context) const
+{
+ if (broker != 0 && objectId.get() != 0)
+ broker->sendMethodRequest(objectId.get(), objectClass, methodName, inArgs, context);
+}
+
+void ObjectImpl::merge(const Object& from)
+{
+ for (map<string, ValuePtr>::const_iterator piter = from.impl->properties.begin();
+ piter != from.impl->properties.end(); piter++)
+ properties[piter->first] = piter->second;
+ for (map<string, ValuePtr>::const_iterator siter = from.impl->statistics.begin();
+ siter != from.impl->statistics.end(); siter++)
+ statistics[siter->first] = siter->second;
+}
+
+void ObjectImpl::parsePresenceMasks(Buffer& buffer, set<string>& excludeList)
+{
+ int propCount = objectClass->getPropertyCount();
+ excludeList.clear();
+ uint8_t bit = 0;
+ uint8_t mask = 0;
+
+ for (int idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* prop = objectClass->getProperty(idx);
+ if (prop->isOptional()) {
+ if (bit == 0) {
+ mask = buffer.getOctet();
+ bit = 1;
+ }
+ if ((mask & bit) == 0)
+ excludeList.insert(string(prop->getName()));
+ if (bit == 0x80)
+ bit = 0;
+ else
+ bit = bit << 1;
+ }
+ }
+}
+
+void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const
+{
+ buffer.putShortString(objectClass->getClassKey()->getPackageName());
+ buffer.putShortString(objectClass->getClassKey()->getClassName());
+ buffer.putBin128(const_cast<uint8_t*>(objectClass->getClassKey()->getHash()));
+}
+
+void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const
+{
+ buffer.putLongLong(lastUpdatedTime);
+ buffer.putLongLong(createTime);
+ buffer.putLongLong(destroyTime);
+ objectId->impl->encode(buffer);
+}
+
+void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const
+{
+ int propCount = objectClass->getPropertyCount();
+ uint8_t bit = 0;
+ uint8_t mask = 0;
+ ValuePtr value;
+
+ for (int idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* prop = objectClass->getProperty(idx);
+ if (prop->isOptional()) {
+ value = properties[prop->getName()];
+ if (bit == 0)
+ bit = 1;
+ if (!value->isNull())
+ mask |= bit;
+ if (bit == 0x80) {
+ buffer.putOctet(mask);
+ bit = 0;
+ mask = 0;
+ } else
+ bit = bit << 1;
+ }
+ }
+ if (bit != 0) {
+ buffer.putOctet(mask);
+ }
+
+ for (int idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* prop = objectClass->getProperty(idx);
+ value = properties[prop->getName()];
+ if (!prop->isOptional() || !value->isNull()) {
+ value->impl->encode(buffer);
+ }
+ }
+}
+
+void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const
+{
+ int statCount = objectClass->getStatisticCount();
+ for (int idx = 0; idx < statCount; idx++) {
+ const SchemaStatistic* stat = objectClass->getStatistic(idx);
+ ValuePtr value = statistics[stat->getName()];
+ value->impl->encode(buffer);
+ }
+}
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(type)) {}
+Object::Object(ObjectImpl* i) : impl(i) {}
+Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {}
+Object::~Object() { delete impl; }
+void Object::destroy() { impl->destroy(); }
+const ObjectId* Object::getObjectId() const { return impl->getObjectId(); }
+void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); }
+const SchemaObjectClass* Object::getClass() const { return impl->getClass(); }
+Value* Object::getValue(const char* key) const { return impl->getValue(key); }
+void Object::invokeMethod(const char* m, const Value* a, void* c) const { impl->invokeMethod(m, a, c); }
+bool Object::isDeleted() const { return impl->isDeleted(); }
+void Object::merge(const Object& from) { impl->merge(from); }
+
diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.h b/qpid/cpp/src/qmf/engine/ObjectImpl.h
new file mode 100644
index 0000000000..6f25867004
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ObjectImpl.h
@@ -0,0 +1,76 @@
+#ifndef _QmfEngineObjectImpl_
+#define _QmfEngineObjectImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qmf/engine/Object.h>
+#include <qmf/engine/ObjectIdImpl.h>
+#include <map>
+#include <set>
+#include <string>
+#include <qpid/framing/Buffer.h>
+#include <boost/shared_ptr.hpp>
+#include <qpid/sys/Mutex.h>
+
+namespace qmf {
+namespace engine {
+
+ class BrokerProxyImpl;
+
+ typedef boost::shared_ptr<Object> ObjectPtr;
+
+ struct ObjectImpl {
+ typedef boost::shared_ptr<Value> ValuePtr;
+ const SchemaObjectClass* objectClass;
+ BrokerProxyImpl* broker;
+ boost::shared_ptr<ObjectId> objectId;
+ uint64_t createTime;
+ uint64_t destroyTime;
+ uint64_t lastUpdatedTime;
+ mutable std::map<std::string, ValuePtr> properties;
+ mutable std::map<std::string, ValuePtr> statistics;
+
+ ObjectImpl(const SchemaObjectClass* type);
+ ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer,
+ bool prop, bool stat, bool managed);
+ static Object* factory(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer,
+ bool prop, bool stat, bool managed);
+ ~ObjectImpl();
+
+ void destroy();
+ const ObjectId* getObjectId() const { return objectId.get(); }
+ void setObjectId(ObjectId* oid) { objectId.reset(new ObjectId(*oid)); }
+ const SchemaObjectClass* getClass() const { return objectClass; }
+ Value* getValue(const std::string& key) const;
+ void invokeMethod(const std::string& methodName, const Value* inArgs, void* context) const;
+ bool isDeleted() const { return destroyTime != 0; }
+ void merge(const Object& from);
+
+ void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set<std::string>& excludeList);
+ void encodeSchemaKey(qpid::framing::Buffer& buffer) const;
+ void encodeManagedObjectData(qpid::framing::Buffer& buffer) const;
+ void encodeProperties(qpid::framing::Buffer& buffer) const;
+ void encodeStatistics(qpid::framing::Buffer& buffer) const;
+ };
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/Protocol.cpp b/qpid/cpp/src/qmf/engine/Protocol.cpp
new file mode 100644
index 0000000000..9e5f490604
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/Protocol.cpp
@@ -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.
+ */
+
+#include "qmf/engine/Protocol.h"
+#include "qpid/framing/Buffer.h"
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::framing;
+
+
+bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq)
+{
+ if (buf.available() < 8)
+ return false;
+
+ uint8_t h1 = buf.getOctet();
+ uint8_t h2 = buf.getOctet();
+ uint8_t h3 = buf.getOctet();
+
+ *opcode = buf.getOctet();
+ *seq = buf.getLong();
+
+ return h1 == 'A' && h2 == 'M' && h3 == '2';
+}
+
+void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq)
+{
+ buf.putOctet('A');
+ buf.putOctet('M');
+ buf.putOctet('2');
+ buf.putOctet(opcode);
+ buf.putLong (seq);
+}
+
+
diff --git a/qpid/cpp/src/qmf/engine/Protocol.h b/qpid/cpp/src/qmf/engine/Protocol.h
new file mode 100644
index 0000000000..1cdfa60c84
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/Protocol.h
@@ -0,0 +1,69 @@
+#ifndef _QmfEngineProtocol_
+#define _QmfEngineProtocol_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qpid/sys/IntegerTypes.h>
+
+namespace qpid {
+ namespace framing {
+ class Buffer;
+ }
+}
+
+namespace qmf {
+namespace engine {
+
+ class Protocol {
+ public:
+ static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
+ static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
+
+ const static uint8_t OP_ATTACH_REQUEST = 'A';
+ const static uint8_t OP_ATTACH_RESPONSE = 'a';
+
+ const static uint8_t OP_BROKER_REQUEST = 'B';
+ const static uint8_t OP_BROKER_RESPONSE = 'b';
+
+ const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x';
+ const static uint8_t OP_COMMAND_COMPLETE = 'z';
+ const static uint8_t OP_HEARTBEAT_INDICATION = 'h';
+
+ const static uint8_t OP_PACKAGE_REQUEST = 'P';
+ const static uint8_t OP_PACKAGE_INDICATION = 'p';
+ const static uint8_t OP_CLASS_QUERY = 'Q';
+ const static uint8_t OP_CLASS_INDICATION = 'q';
+ const static uint8_t OP_SCHEMA_REQUEST = 'S';
+ const static uint8_t OP_SCHEMA_RESPONSE = 's';
+
+ const static uint8_t OP_METHOD_REQUEST = 'M';
+ const static uint8_t OP_METHOD_RESPONSE = 'm';
+ const static uint8_t OP_GET_QUERY = 'G';
+ const static uint8_t OP_OBJECT_INDICATION = 'g';
+ const static uint8_t OP_PROPERTY_INDICATION = 'c';
+ const static uint8_t OP_STATISTIC_INDICATION = 'i';
+ const static uint8_t OP_EVENT_INDICATION = 'e';
+ };
+
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.cpp b/qpid/cpp/src/qmf/engine/QueryImpl.cpp
new file mode 100644
index 0000000000..6f2beeee87
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/QueryImpl.cpp
@@ -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.
+ */
+
+#include "qmf/engine/QueryImpl.h"
+#include "qmf/engine/ObjectIdImpl.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::framing;
+
+bool QueryElementImpl::evaluate(const Object* /*object*/) const
+{
+ // TODO: Implement this
+ return false;
+}
+
+bool QueryExpressionImpl::evaluate(const Object* /*object*/) const
+{
+ // TODO: Implement this
+ return false;
+}
+
+QueryImpl::QueryImpl(Buffer& buffer)
+{
+ FieldTable ft;
+ ft.decode(buffer);
+ // TODO
+}
+
+Query* QueryImpl::factory(Buffer& buffer)
+{
+ QueryImpl* impl(new QueryImpl(buffer));
+ return new Query(impl);
+}
+
+void QueryImpl::encode(Buffer& buffer) const
+{
+ FieldTable ft;
+
+ if (oid.get() != 0) {
+ ft.setString("_objectid", oid->impl->asString());
+ } else {
+ if (!packageName.empty())
+ ft.setString("_package", packageName);
+ ft.setString("_class", className);
+ }
+
+ ft.encode(buffer);
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {}
+QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {}
+QueryElement::~QueryElement() { delete impl; }
+bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); }
+
+QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {}
+QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {}
+QueryExpression::~QueryExpression() { delete impl; }
+bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); }
+
+Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {}
+Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {}
+Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {}
+Query::Query(QueryImpl* i) : impl(i) {}
+Query::Query(const Query& from) : impl(new QueryImpl(*(from.impl))) {}
+Query::~Query() { delete impl; }
+void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); }
+void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); }
+void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); }
+const char* Query::getPackage() const { return impl->getPackage().c_str(); }
+const char* Query::getClass() const { return impl->getClass().c_str(); }
+const ObjectId* Query::getObjectId() const { return impl->getObjectId(); }
+bool Query::haveSelect() const { return impl->haveSelect(); }
+bool Query::haveLimit() const { return impl->haveLimit(); }
+bool Query::haveOrderBy() const { return impl->haveOrderBy(); }
+const QueryOperand* Query::getSelect() const { return impl->getSelect(); }
+uint32_t Query::getLimit() const { return impl->getLimit(); }
+const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); }
+bool Query::getDecreasing() const { return impl->getDecreasing(); }
+
diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.h b/qpid/cpp/src/qmf/engine/QueryImpl.h
new file mode 100644
index 0000000000..8ebe0d932f
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/QueryImpl.h
@@ -0,0 +1,102 @@
+#ifndef _QmfEngineQueryImpl_
+#define _QmfEngineQueryImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Query.h"
+#include "qmf/engine/Schema.h"
+#include <string>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+ namespace framing {
+ class Buffer;
+ }
+}
+
+namespace qmf {
+namespace engine {
+
+ struct QueryElementImpl {
+ QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : attrName(a), value(v), oper(o) {}
+ ~QueryElementImpl() {}
+ bool evaluate(const Object* object) const;
+
+ std::string attrName;
+ const Value* value;
+ ValueOper oper;
+ };
+
+ struct QueryExpressionImpl {
+ QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : oper(o), left(operand1), right(operand2) {}
+ ~QueryExpressionImpl() {}
+ bool evaluate(const Object* object) const;
+
+ ExprOper oper;
+ const QueryOperand* left;
+ const QueryOperand* right;
+ };
+
+ struct QueryImpl {
+ // Constructors mapped to public
+ QueryImpl(const std::string& c, const std::string& p) : packageName(p), className(c), select(0), resultLimit(0) {}
+ QueryImpl(const SchemaClassKey* key) : packageName(key->getPackageName()), className(key->getClassName()), select(0), resultLimit(0) {}
+ QueryImpl(const ObjectId* oid) : oid(new ObjectId(*oid)), select(0), resultLimit(0) {}
+
+ // Factory constructors
+ QueryImpl(qpid::framing::Buffer& buffer);
+
+ ~QueryImpl() {};
+ static Query* factory(qpid::framing::Buffer& buffer);
+
+ void setSelect(const QueryOperand* criterion) { select = criterion; }
+ void setLimit(uint32_t maxResults) { resultLimit = maxResults; }
+ void setOrderBy(const std::string& attrName, bool decreasing) {
+ orderBy = attrName; orderDecreasing = decreasing;
+ }
+
+ const std::string& getPackage() const { return packageName; }
+ const std::string& getClass() const { return className; }
+ const ObjectId* getObjectId() const { return oid.get(); }
+
+ bool haveSelect() const { return select != 0; }
+ bool haveLimit() const { return resultLimit > 0; }
+ bool haveOrderBy() const { return !orderBy.empty(); }
+ const QueryOperand* getSelect() const { return select; }
+ uint32_t getLimit() const { return resultLimit; }
+ const std::string& getOrderBy() const { return orderBy; }
+ bool getDecreasing() const { return orderDecreasing; }
+
+ void encode(qpid::framing::Buffer& buffer) const;
+ bool singleAgent() const { return oid.get() != 0; }
+ uint32_t agentBank() const { return singleAgent() ? oid->getAgentBank() : 0; }
+
+ std::string packageName;
+ std::string className;
+ boost::shared_ptr<ObjectId> oid;
+ const QueryOperand* select;
+ uint32_t resultLimit;
+ std::string orderBy;
+ bool orderDecreasing;
+ };
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qmf/engine/ResilientConnection.cpp b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp
new file mode 100644
index 0000000000..ab65b8d768
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp
@@ -0,0 +1,514 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/ResilientConnection.h"
+#include "qmf/engine/MessageImpl.h"
+#include "qmf/engine/ConnectionSettingsImpl.h"
+#include <qpid/client/Connection.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/Message.h>
+#include <qpid/sys/Thread.h>
+#include <qpid/sys/Runnable.h>
+#include <qpid/sys/Mutex.h>
+#include <qpid/sys/Condition.h>
+#include <qpid/sys/Time.h>
+#include <qpid/log/Statement.h>
+#include <qpid/RefCounted.h>
+#include <boost/bind.hpp>
+#include <string>
+#include <deque>
+#include <vector>
+#include <set>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <unistd.h>
+#include <fcntl.h>
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid;
+using qpid::sys::Mutex;
+
+namespace qmf {
+namespace engine {
+ struct ResilientConnectionEventImpl {
+ ResilientConnectionEvent::EventKind kind;
+ void* sessionContext;
+ string errorText;
+ MessageImpl message;
+
+ ResilientConnectionEventImpl(ResilientConnectionEvent::EventKind k,
+ const MessageImpl& m = MessageImpl()) :
+ kind(k), sessionContext(0), message(m) {}
+ ResilientConnectionEvent copy();
+ };
+
+ struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted {
+ typedef boost::intrusive_ptr<RCSession> Ptr;
+ ResilientConnectionImpl& connImpl;
+ string name;
+ client::Connection& connection;
+ client::Session session;
+ client::SubscriptionManager* subscriptions;
+ string userId;
+ void* userContext;
+ vector<string> dests;
+ qpid::sys::Thread thread;
+
+ RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc);
+ ~RCSession();
+ void received(client::Message& msg);
+ void run();
+ void stop();
+ };
+
+ class ResilientConnectionImpl : public qpid::sys::Runnable, public boost::noncopyable {
+ public:
+ ResilientConnectionImpl(const ConnectionSettings& settings);
+ ~ResilientConnectionImpl();
+
+ bool isConnected() const;
+ bool getEvent(ResilientConnectionEvent& event);
+ void popEvent();
+ bool createSession(const char* name, void* sessionContext, SessionHandle& handle);
+ void destroySession(SessionHandle handle);
+ void sendMessage(SessionHandle handle, qmf::engine::Message& message);
+ void declareQueue(SessionHandle handle, char* queue);
+ void deleteQueue(SessionHandle handle, char* queue);
+ void bind(SessionHandle handle, char* exchange, char* queue, char* key);
+ void unbind(SessionHandle handle, char* exchange, char* queue, char* key);
+ void setNotifyFd(int fd);
+ void notify();
+
+ void run();
+ void failure();
+ void sessionClosed(RCSession* sess);
+
+ void EnqueueEvent(ResilientConnectionEvent::EventKind kind,
+ void* sessionContext = 0,
+ const MessageImpl& message = MessageImpl(),
+ const string& errorText = "");
+
+ private:
+ int notifyFd;
+ bool connected;
+ bool shutdown;
+ string lastError;
+ const ConnectionSettings settings;
+ client::Connection connection;
+ mutable qpid::sys::Mutex lock;
+ int delayMin;
+ int delayMax;
+ int delayFactor;
+ qpid::sys::Condition cond;
+ deque<ResilientConnectionEventImpl> eventQueue;
+ set<RCSession::Ptr> sessions;
+ qpid::sys::Thread connThread;
+ };
+}
+}
+
+ResilientConnectionEvent ResilientConnectionEventImpl::copy()
+{
+ ResilientConnectionEvent item;
+
+ ::memset(&item, 0, sizeof(ResilientConnectionEvent));
+ item.kind = kind;
+ item.sessionContext = sessionContext;
+ item.message = message.copy();
+ item.errorText = const_cast<char*>(errorText.c_str());
+
+ return item;
+}
+
+RCSession::RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) :
+ connImpl(ci), name(n), connection(c), session(connection.newSession(name)),
+ subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this)
+{
+ const qpid::client::ConnectionSettings& operSettings = connection.getNegotiatedSettings();
+ userId = operSettings.username;
+}
+
+RCSession::~RCSession()
+{
+ subscriptions->stop();
+ thread.join();
+ session.close();
+ delete subscriptions;
+}
+
+void RCSession::run()
+{
+ try {
+ subscriptions->run();
+ } catch (exception& /*e*/) {
+ connImpl.sessionClosed(this);
+ }
+}
+
+void RCSession::stop()
+{
+ subscriptions->stop();
+}
+
+void RCSession::received(client::Message& msg)
+{
+ MessageImpl qmsg;
+ qmsg.body = msg.getData();
+
+ qpid::framing::DeliveryProperties dp = msg.getDeliveryProperties();
+ if (dp.hasRoutingKey()) {
+ qmsg.routingKey = dp.getRoutingKey();
+ }
+
+ qpid::framing::MessageProperties mp = msg.getMessageProperties();
+ if (mp.hasReplyTo()) {
+ const qpid::framing::ReplyTo& rt = mp.getReplyTo();
+ qmsg.replyExchange = rt.getExchange();
+ qmsg.replyKey = rt.getRoutingKey();
+ }
+
+ if (mp.hasUserId()) {
+ qmsg.userId = mp.getUserId();
+ }
+
+ connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg);
+}
+
+ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) :
+ notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this)
+{
+ connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this));
+ settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor);
+}
+
+ResilientConnectionImpl::~ResilientConnectionImpl()
+{
+ shutdown = true;
+ connected = false;
+ cond.notify();
+ connThread.join();
+ connection.close();
+}
+
+bool ResilientConnectionImpl::isConnected() const
+{
+ Mutex::ScopedLock _lock(lock);
+ return connected;
+}
+
+bool ResilientConnectionImpl::getEvent(ResilientConnectionEvent& event)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (eventQueue.empty())
+ return false;
+ event = eventQueue.front().copy();
+ return true;
+}
+
+void ResilientConnectionImpl::popEvent()
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!eventQueue.empty())
+ eventQueue.pop_front();
+}
+
+bool ResilientConnectionImpl::createSession(const char* name, void* sessionContext,
+ SessionHandle& handle)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (!connected)
+ return false;
+
+ RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext));
+
+ handle.impl = (void*) sess.get();
+ sessions.insert(sess);
+
+ return true;
+}
+
+void ResilientConnectionImpl::destroySession(SessionHandle handle)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl);
+ set<RCSession::Ptr>::iterator iter = sessions.find(sess);
+ if (iter != sessions.end()) {
+ for (vector<string>::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++)
+ sess->subscriptions->cancel(dIter->c_str());
+ sess->subscriptions->stop();
+ sess->subscriptions->wait();
+
+ sessions.erase(iter);
+ return;
+ }
+}
+
+void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::engine::Message& message)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl);
+ set<RCSession::Ptr>::iterator iter = sessions.find(sess);
+ qpid::client::Message msg;
+ string data(message.body, message.length);
+ msg.getDeliveryProperties().setRoutingKey(message.routingKey);
+ msg.getMessageProperties().setReplyTo(qpid::framing::ReplyTo(message.replyExchange, message.replyKey));
+ if (settings.impl->getSendUserId())
+ msg.getMessageProperties().setUserId(sess->userId);
+ msg.setData(data);
+
+ try {
+ sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination);
+ } catch(exception& e) {
+ QPID_LOG(error, "Session Exception during message-transfer: " << e.what());
+ sessions.erase(iter);
+ EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, (*iter)->userContext);
+ }
+}
+
+void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession* sess = (RCSession*) handle.impl;
+
+ sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true);
+ sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE);
+ sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED);
+ sess->subscriptions->subscribe(*sess, queue, queue);
+ sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited());
+ sess->dests.push_back(string(queue));
+}
+
+void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession* sess = (RCSession*) handle.impl;
+
+ sess->session.queueDelete(client::arg::queue=queue);
+ for (vector<string>::iterator iter = sess->dests.begin();
+ iter != sess->dests.end(); iter++)
+ if (*iter == queue) {
+ sess->subscriptions->cancel(queue);
+ sess->dests.erase(iter);
+ break;
+ }
+}
+
+void ResilientConnectionImpl::bind(SessionHandle handle,
+ char* exchange, char* queue, char* key)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession* sess = (RCSession*) handle.impl;
+
+ sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key);
+}
+
+void ResilientConnectionImpl::unbind(SessionHandle handle,
+ char* exchange, char* queue, char* key)
+{
+ Mutex::ScopedLock _lock(lock);
+ RCSession* sess = (RCSession*) handle.impl;
+
+ sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key);
+}
+
+void ResilientConnectionImpl::notify()
+{
+ if (notifyFd != -1)
+ {
+ int unused_ret; //Suppress warnings about ignoring return value.
+ unused_ret = ::write(notifyFd, ".", 1);
+ }
+}
+
+
+void ResilientConnectionImpl::setNotifyFd(int fd)
+{
+ notifyFd = fd;
+ if (notifyFd > 0) {
+ int original = fcntl(notifyFd, F_GETFL);
+ fcntl(notifyFd, F_SETFL, O_NONBLOCK | original);
+ }
+}
+
+void ResilientConnectionImpl::run()
+{
+ int delay(delayMin);
+
+ while (true) {
+ try {
+ QPID_LOG(trace, "Trying to open connection...");
+ connection.open(settings.impl->getClientSettings());
+ {
+ Mutex::ScopedLock _lock(lock);
+ connected = true;
+ EnqueueEvent(ResilientConnectionEvent::CONNECTED);
+
+ while (connected)
+ cond.wait(lock);
+ delay = delayMin;
+
+ while (!sessions.empty()) {
+ set<RCSession::Ptr>::iterator iter = sessions.begin();
+ RCSession::Ptr sess = *iter;
+ sessions.erase(iter);
+ EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext);
+ Mutex::ScopedUnlock _u(lock);
+ sess->stop();
+
+ // Nullify the intrusive pointer within the scoped unlock, otherwise,
+ // the reference is held until overwritted above (under lock) which causes
+ // the session destructor to be called with the lock held.
+ sess = 0;
+ }
+
+ EnqueueEvent(ResilientConnectionEvent::DISCONNECTED);
+
+ if (shutdown)
+ return;
+ }
+ connection.close();
+ } catch (exception &e) {
+ QPID_LOG(debug, "connection.open exception: " << e.what());
+ Mutex::ScopedLock _lock(lock);
+ lastError = e.what();
+ if (delay < delayMax)
+ delay *= delayFactor;
+ }
+
+ ::qpid::sys::sleep(delay);
+ }
+}
+
+void ResilientConnectionImpl::failure()
+{
+ Mutex::ScopedLock _lock(lock);
+
+ connected = false;
+ lastError = "Closed by Peer";
+ cond.notify();
+}
+
+void ResilientConnectionImpl::sessionClosed(RCSession*)
+{
+ Mutex::ScopedLock _lock(lock);
+ connected = false;
+ lastError = "Closed due to Session failure";
+ cond.notify();
+}
+
+void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind kind,
+ void* sessionContext,
+ const MessageImpl& message,
+ const string& errorText)
+{
+ {
+ Mutex::ScopedLock _lock(lock);
+ ResilientConnectionEventImpl event(kind, message);
+
+ event.sessionContext = sessionContext;
+ event.errorText = errorText;
+
+ eventQueue.push_back(event);
+ }
+
+ if (notifyFd != -1)
+ {
+ int unused_ret; //Suppress warnings about ignoring return value.
+ unused_ret = ::write(notifyFd, ".", 1);
+ }
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+ResilientConnection::ResilientConnection(const ConnectionSettings& settings)
+{
+ impl = new ResilientConnectionImpl(settings);
+}
+
+ResilientConnection::~ResilientConnection()
+{
+ delete impl;
+}
+
+bool ResilientConnection::isConnected() const
+{
+ return impl->isConnected();
+}
+
+bool ResilientConnection::getEvent(ResilientConnectionEvent& event)
+{
+ return impl->getEvent(event);
+}
+
+void ResilientConnection::popEvent()
+{
+ impl->popEvent();
+}
+
+bool ResilientConnection::createSession(const char* name, void* sessionContext, SessionHandle& handle)
+{
+ return impl->createSession(name, sessionContext, handle);
+}
+
+void ResilientConnection::destroySession(SessionHandle handle)
+{
+ impl->destroySession(handle);
+}
+
+void ResilientConnection::sendMessage(SessionHandle handle, qmf::engine::Message& message)
+{
+ impl->sendMessage(handle, message);
+}
+
+void ResilientConnection::declareQueue(SessionHandle handle, char* queue)
+{
+ impl->declareQueue(handle, queue);
+}
+
+void ResilientConnection::deleteQueue(SessionHandle handle, char* queue)
+{
+ impl->deleteQueue(handle, queue);
+}
+
+void ResilientConnection::bind(SessionHandle handle, char* exchange, char* queue, char* key)
+{
+ impl->bind(handle, exchange, queue, key);
+}
+
+void ResilientConnection::unbind(SessionHandle handle, char* exchange, char* queue, char* key)
+{
+ impl->unbind(handle, exchange, queue, key);
+}
+
+void ResilientConnection::setNotifyFd(int fd)
+{
+ impl->setNotifyFd(fd);
+}
+
+void ResilientConnection::notify()
+{
+ impl->notify();
+}
+
diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.cpp b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp
new file mode 100644
index 0000000000..f75663e131
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp
@@ -0,0 +1,614 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/SchemaImpl.h"
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/Uuid.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <sstream>
+
+using namespace std;
+using namespace qmf::engine;
+using qpid::framing::Buffer;
+using qpid::framing::FieldTable;
+using qpid::framing::Uuid;
+
+SchemaHash::SchemaHash()
+{
+ for (int idx = 0; idx < 16; idx++)
+ hash[idx] = 0x5A;
+}
+
+void SchemaHash::encode(Buffer& buffer) const
+{
+ buffer.putBin128(hash);
+}
+
+void SchemaHash::decode(Buffer& buffer)
+{
+ buffer.getBin128(hash);
+}
+
+void SchemaHash::update(uint8_t data)
+{
+ update((char*) &data, 1);
+}
+
+void SchemaHash::update(const char* data, uint32_t len)
+{
+ union h {
+ uint8_t b[16];
+ uint64_t q[2];
+ }* h = reinterpret_cast<union h*>(&hash[0]);
+ uint64_t* first = &h->q[0];
+ uint64_t* second = &h->q[1];
+ for (uint32_t idx = 0; idx < len; idx++) {
+ *first = *first ^ (uint64_t) data[idx];
+ *second = *second << 1;
+ *second |= ((*first & 0x8000000000000000LL) >> 63);
+ *first = *first << 1;
+ *first = *first ^ *second;
+ }
+}
+
+bool SchemaHash::operator==(const SchemaHash& other) const
+{
+ return ::memcmp(&hash, &other.hash, 16) == 0;
+}
+
+bool SchemaHash::operator<(const SchemaHash& other) const
+{
+ return ::memcmp(&hash, &other.hash, 16) < 0;
+}
+
+bool SchemaHash::operator>(const SchemaHash& other) const
+{
+ return ::memcmp(&hash, &other.hash, 16) > 0;
+}
+
+SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer)
+{
+ FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typecode = (Typecode) map.getAsInt("type");
+ unit = map.getAsString("unit");
+ description = map.getAsString("desc");
+
+ dir = DIR_IN;
+ string dstr(map.getAsString("dir"));
+ if (dstr == "O")
+ dir = DIR_OUT;
+ else if (dstr == "IO")
+ dir = DIR_IN_OUT;
+}
+
+SchemaArgument* SchemaArgumentImpl::factory(Buffer& buffer)
+{
+ SchemaArgumentImpl* impl(new SchemaArgumentImpl(buffer));
+ return new SchemaArgument(impl);
+}
+
+void SchemaArgumentImpl::encode(Buffer& buffer) const
+{
+ FieldTable map;
+
+ map.setString("name", name);
+ map.setInt("type", (int) typecode);
+ if (dir == DIR_IN)
+ map.setString("dir", "I");
+ else if (dir == DIR_OUT)
+ map.setString("dir", "O");
+ else
+ map.setString("dir", "IO");
+ if (!unit.empty())
+ map.setString("unit", unit);
+ if (!description.empty())
+ map.setString("desc", description);
+
+ map.encode(buffer);
+}
+
+void SchemaArgumentImpl::updateHash(SchemaHash& hash) const
+{
+ hash.update(name);
+ hash.update(typecode);
+ hash.update(dir);
+ hash.update(unit);
+ hash.update(description);
+}
+
+SchemaMethodImpl::SchemaMethodImpl(Buffer& buffer)
+{
+ FieldTable map;
+ int argCount;
+
+ map.decode(buffer);
+ name = map.getAsString("name");
+ argCount = map.getAsInt("argCount");
+ description = map.getAsString("desc");
+
+ for (int idx = 0; idx < argCount; idx++) {
+ SchemaArgument* arg = SchemaArgumentImpl::factory(buffer);
+ addArgument(arg);
+ }
+}
+
+SchemaMethod* SchemaMethodImpl::factory(Buffer& buffer)
+{
+ SchemaMethodImpl* impl(new SchemaMethodImpl(buffer));
+ return new SchemaMethod(impl);
+}
+
+void SchemaMethodImpl::encode(Buffer& buffer) const
+{
+ FieldTable map;
+
+ map.setString("name", name);
+ map.setInt("argCount", arguments.size());
+ if (!description.empty())
+ map.setString("desc", description);
+ map.encode(buffer);
+
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ (*iter)->impl->encode(buffer);
+}
+
+void SchemaMethodImpl::addArgument(const SchemaArgument* argument)
+{
+ arguments.push_back(argument);
+}
+
+const SchemaArgument* SchemaMethodImpl::getArgument(int idx) const
+{
+ int count = 0;
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++, count++)
+ if (idx == count)
+ return (*iter);
+ return 0;
+}
+
+void SchemaMethodImpl::updateHash(SchemaHash& hash) const
+{
+ hash.update(name);
+ hash.update(description);
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ (*iter)->impl->updateHash(hash);
+}
+
+SchemaPropertyImpl::SchemaPropertyImpl(Buffer& buffer)
+{
+ FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typecode = (Typecode) map.getAsInt("type");
+ access = (Access) map.getAsInt("access");
+ index = map.getAsInt("index") != 0;
+ optional = map.getAsInt("optional") != 0;
+ unit = map.getAsString("unit");
+ description = map.getAsString("desc");
+}
+
+SchemaProperty* SchemaPropertyImpl::factory(Buffer& buffer)
+{
+ SchemaPropertyImpl* impl(new SchemaPropertyImpl(buffer));
+ return new SchemaProperty(impl);
+}
+
+void SchemaPropertyImpl::encode(Buffer& buffer) const
+{
+ FieldTable map;
+
+ map.setString("name", name);
+ map.setInt("type", (int) typecode);
+ map.setInt("access", (int) access);
+ map.setInt("index", index ? 1 : 0);
+ map.setInt("optional", optional ? 1 : 0);
+ if (!unit.empty())
+ map.setString("unit", unit);
+ if (!description.empty())
+ map.setString("desc", description);
+
+ map.encode(buffer);
+}
+
+void SchemaPropertyImpl::updateHash(SchemaHash& hash) const
+{
+ hash.update(name);
+ hash.update(typecode);
+ hash.update(access);
+ hash.update(index);
+ hash.update(optional);
+ hash.update(unit);
+ hash.update(description);
+}
+
+SchemaStatisticImpl::SchemaStatisticImpl(Buffer& buffer)
+{
+ FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typecode = (Typecode) map.getAsInt("type");
+ unit = map.getAsString("unit");
+ description = map.getAsString("desc");
+}
+
+SchemaStatistic* SchemaStatisticImpl::factory(Buffer& buffer)
+{
+ SchemaStatisticImpl* impl(new SchemaStatisticImpl(buffer));
+ return new SchemaStatistic(impl);
+}
+
+void SchemaStatisticImpl::encode(Buffer& buffer) const
+{
+ FieldTable map;
+
+ map.setString("name", name);
+ map.setInt("type", (int) typecode);
+ if (!unit.empty())
+ map.setString("unit", unit);
+ if (!description.empty())
+ map.setString("desc", description);
+
+ map.encode(buffer);
+}
+
+void SchemaStatisticImpl::updateHash(SchemaHash& hash) const
+{
+ hash.update(name);
+ hash.update(typecode);
+ hash.update(unit);
+ hash.update(description);
+}
+
+SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : package(p), name(n), hash(h) {}
+
+SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : package(packageContainer), name(nameContainer), hash(hashContainer)
+{
+ buffer.getShortString(packageContainer);
+ buffer.getShortString(nameContainer);
+ hashContainer.decode(buffer);
+}
+
+SchemaClassKey* SchemaClassKeyImpl::factory(const string& package, const string& name, const SchemaHash& hash)
+{
+ SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(package, name, hash));
+ return new SchemaClassKey(impl);
+}
+
+SchemaClassKey* SchemaClassKeyImpl::factory(Buffer& buffer)
+{
+ SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(buffer));
+ return new SchemaClassKey(impl);
+}
+
+void SchemaClassKeyImpl::encode(Buffer& buffer) const
+{
+ buffer.putShortString(package);
+ buffer.putShortString(name);
+ hash.encode(buffer);
+}
+
+bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const
+{
+ return package == other.package &&
+ name == other.name &&
+ hash == other.hash;
+}
+
+bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const
+{
+ if (package < other.package) return true;
+ if (package > other.package) return false;
+ if (name < other.name) return true;
+ if (name > other.name) return false;
+ return hash < other.hash;
+}
+
+const string& SchemaClassKeyImpl::str() const
+{
+ Uuid printableHash(hash.get());
+ stringstream str;
+ str << package << ":" << name << "(" << printableHash << ")";
+ repr = str.str();
+ return repr;
+}
+
+SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash))
+{
+ buffer.getShortString(package);
+ buffer.getShortString(name);
+ hash.decode(buffer);
+
+ uint16_t propCount = buffer.getShort();
+ uint16_t statCount = buffer.getShort();
+ uint16_t methodCount = buffer.getShort();
+
+ for (uint16_t idx = 0; idx < propCount; idx++) {
+ const SchemaProperty* property = SchemaPropertyImpl::factory(buffer);
+ addProperty(property);
+ }
+
+ for (uint16_t idx = 0; idx < statCount; idx++) {
+ const SchemaStatistic* statistic = SchemaStatisticImpl::factory(buffer);
+ addStatistic(statistic);
+ }
+
+ for (uint16_t idx = 0; idx < methodCount; idx++) {
+ SchemaMethod* method = SchemaMethodImpl::factory(buffer);
+ addMethod(method);
+ }
+}
+
+SchemaObjectClass* SchemaObjectClassImpl::factory(Buffer& buffer)
+{
+ SchemaObjectClassImpl* impl(new SchemaObjectClassImpl(buffer));
+ return new SchemaObjectClass(impl);
+}
+
+void SchemaObjectClassImpl::encode(Buffer& buffer) const
+{
+ buffer.putOctet((uint8_t) CLASS_OBJECT);
+ buffer.putShortString(package);
+ buffer.putShortString(name);
+ hash.encode(buffer);
+ //buffer.putOctet(0); // No parent class
+ buffer.putShort((uint16_t) properties.size());
+ buffer.putShort((uint16_t) statistics.size());
+ buffer.putShort((uint16_t) methods.size());
+
+ for (vector<const SchemaProperty*>::const_iterator iter = properties.begin();
+ iter != properties.end(); iter++)
+ (*iter)->impl->encode(buffer);
+ for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin();
+ iter != statistics.end(); iter++)
+ (*iter)->impl->encode(buffer);
+ for (vector<const SchemaMethod*>::const_iterator iter = methods.begin();
+ iter != methods.end(); iter++)
+ (*iter)->impl->encode(buffer);
+}
+
+const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const
+{
+ if (!hasHash) {
+ hasHash = true;
+ hash.update(package);
+ hash.update(name);
+ for (vector<const SchemaProperty*>::const_iterator iter = properties.begin();
+ iter != properties.end(); iter++)
+ (*iter)->impl->updateHash(hash);
+ for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin();
+ iter != statistics.end(); iter++)
+ (*iter)->impl->updateHash(hash);
+ for (vector<const SchemaMethod*>::const_iterator iter = methods.begin();
+ iter != methods.end(); iter++)
+ (*iter)->impl->updateHash(hash);
+ }
+
+ return classKey.get();
+}
+
+void SchemaObjectClassImpl::addProperty(const SchemaProperty* property)
+{
+ properties.push_back(property);
+}
+
+void SchemaObjectClassImpl::addStatistic(const SchemaStatistic* statistic)
+{
+ statistics.push_back(statistic);
+}
+
+void SchemaObjectClassImpl::addMethod(const SchemaMethod* method)
+{
+ methods.push_back(method);
+}
+
+const SchemaProperty* SchemaObjectClassImpl::getProperty(int idx) const
+{
+ int count = 0;
+ for (vector<const SchemaProperty*>::const_iterator iter = properties.begin();
+ iter != properties.end(); iter++, count++)
+ if (idx == count)
+ return *iter;
+ return 0;
+}
+
+const SchemaStatistic* SchemaObjectClassImpl::getStatistic(int idx) const
+{
+ int count = 0;
+ for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin();
+ iter != statistics.end(); iter++, count++)
+ if (idx == count)
+ return *iter;
+ return 0;
+}
+
+const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const
+{
+ int count = 0;
+ for (vector<const SchemaMethod*>::const_iterator iter = methods.begin();
+ iter != methods.end(); iter++, count++)
+ if (idx == count)
+ return *iter;
+ return 0;
+}
+
+SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash))
+{
+ buffer.getShortString(package);
+ buffer.getShortString(name);
+ hash.decode(buffer);
+
+ uint16_t argCount = buffer.getShort();
+
+ for (uint16_t idx = 0; idx < argCount; idx++) {
+ SchemaArgument* argument = SchemaArgumentImpl::factory(buffer);
+ addArgument(argument);
+ }
+}
+
+SchemaEventClass* SchemaEventClassImpl::factory(Buffer& buffer)
+{
+ SchemaEventClassImpl* impl(new SchemaEventClassImpl(buffer));
+ return new SchemaEventClass(impl);
+}
+
+void SchemaEventClassImpl::encode(Buffer& buffer) const
+{
+ buffer.putOctet((uint8_t) CLASS_EVENT);
+ buffer.putShortString(package);
+ buffer.putShortString(name);
+ hash.encode(buffer);
+ buffer.putShort((uint16_t) arguments.size());
+
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ (*iter)->impl->encode(buffer);
+}
+
+const SchemaClassKey* SchemaEventClassImpl::getClassKey() const
+{
+ if (!hasHash) {
+ hasHash = true;
+ hash.update(package);
+ hash.update(name);
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ (*iter)->impl->updateHash(hash);
+ }
+ return classKey.get();
+}
+
+void SchemaEventClassImpl::addArgument(const SchemaArgument* argument)
+{
+ arguments.push_back(argument);
+}
+
+const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const
+{
+ int count = 0;
+ for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin();
+ iter != arguments.end(); iter++, count++)
+ if (idx == count)
+ return (*iter);
+ return 0;
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(name, typecode); }
+SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {}
+SchemaArgument::SchemaArgument(const SchemaArgument& from) : impl(new SchemaArgumentImpl(*(from.impl))) {}
+SchemaArgument::~SchemaArgument() { delete impl; }
+void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); }
+void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); }
+void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); }
+const char* SchemaArgument::getName() const { return impl->getName().c_str(); }
+Typecode SchemaArgument::getType() const { return impl->getType(); }
+Direction SchemaArgument::getDirection() const { return impl->getDirection(); }
+const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); }
+const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); }
+
+SchemaMethod::SchemaMethod(const char* name) : impl(new SchemaMethodImpl(name)) {}
+SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {}
+SchemaMethod::SchemaMethod(const SchemaMethod& from) : impl(new SchemaMethodImpl(*(from.impl))) {}
+SchemaMethod::~SchemaMethod() { delete impl; }
+void SchemaMethod::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); }
+void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); }
+const char* SchemaMethod::getName() const { return impl->getName().c_str(); }
+const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); }
+int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); }
+const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); }
+
+SchemaProperty::SchemaProperty(const char* name, Typecode typecode) : impl(new SchemaPropertyImpl(name, typecode)) {}
+SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {}
+SchemaProperty::SchemaProperty(const SchemaProperty& from) : impl(new SchemaPropertyImpl(*(from.impl))) {}
+SchemaProperty::~SchemaProperty() { delete impl; }
+void SchemaProperty::setAccess(Access access) { impl->setAccess(access); }
+void SchemaProperty::setIndex(bool val) { impl->setIndex(val); }
+void SchemaProperty::setOptional(bool val) { impl->setOptional(val); }
+void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); }
+void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); }
+const char* SchemaProperty::getName() const { return impl->getName().c_str(); }
+Typecode SchemaProperty::getType() const { return impl->getType(); }
+Access SchemaProperty::getAccess() const { return impl->getAccess(); }
+bool SchemaProperty::isIndex() const { return impl->isIndex(); }
+bool SchemaProperty::isOptional() const { return impl->isOptional(); }
+const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); }
+const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); }
+
+SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) : impl(new SchemaStatisticImpl(name, typecode)) {}
+SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {}
+SchemaStatistic::SchemaStatistic(const SchemaStatistic& from) : impl(new SchemaStatisticImpl(*(from.impl))) {}
+SchemaStatistic::~SchemaStatistic() { delete impl; }
+void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); }
+void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); }
+const char* SchemaStatistic::getName() const { return impl->getName().c_str(); }
+Typecode SchemaStatistic::getType() const { return impl->getType(); }
+const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); }
+const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); }
+
+SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {}
+SchemaClassKey::SchemaClassKey(const SchemaClassKey& from) : impl(new SchemaClassKeyImpl(*(from.impl))) {}
+SchemaClassKey::~SchemaClassKey() { delete impl; }
+const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); }
+const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); }
+const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); }
+const char* SchemaClassKey::asString() const { return impl->str().c_str(); }
+bool SchemaClassKey::operator==(const SchemaClassKey& other) const { return *impl == *(other.impl); }
+bool SchemaClassKey::operator<(const SchemaClassKey& other) const { return *impl < *(other.impl); }
+
+SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) : impl(new SchemaObjectClassImpl(package, name)) {}
+SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {}
+SchemaObjectClass::SchemaObjectClass(const SchemaObjectClass& from) : impl(new SchemaObjectClassImpl(*(from.impl))) {}
+SchemaObjectClass::~SchemaObjectClass() { delete impl; }
+void SchemaObjectClass::addProperty(const SchemaProperty* property) { impl->addProperty(property); }
+void SchemaObjectClass::addStatistic(const SchemaStatistic* statistic) { impl->addStatistic(statistic); }
+void SchemaObjectClass::addMethod(const SchemaMethod* method) { impl->addMethod(method); }
+const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); }
+int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); }
+int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); }
+int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); }
+const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); }
+const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); }
+const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); }
+
+SchemaEventClass::SchemaEventClass(const char* package, const char* name, Severity s) : impl(new SchemaEventClassImpl(package, name, s)) {}
+SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {}
+SchemaEventClass::SchemaEventClass(const SchemaEventClass& from) : impl(new SchemaEventClassImpl(*(from.impl))) {}
+SchemaEventClass::~SchemaEventClass() { delete impl; }
+void SchemaEventClass::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); }
+void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); }
+const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); }
+Severity SchemaEventClass::getSeverity() const { return impl->getSeverity(); }
+int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); }
+const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); }
+
diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.h b/qpid/cpp/src/qmf/engine/SchemaImpl.h
new file mode 100644
index 0000000000..8b079a5ec6
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/SchemaImpl.h
@@ -0,0 +1,227 @@
+#ifndef _QmfEngineSchemaImpl_
+#define _QmfEngineSchemaImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/Schema.h"
+#include "qpid/framing/Buffer.h"
+
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace qmf {
+namespace engine {
+
+ // TODO: Destructors for schema classes
+ // TODO: Add "frozen" attribute for schema classes so they can't be modified after
+ // they've been registered.
+
+ class SchemaHash {
+ uint8_t hash[16];
+ public:
+ SchemaHash();
+ void encode(qpid::framing::Buffer& buffer) const;
+ void decode(qpid::framing::Buffer& buffer);
+ void update(const char* data, uint32_t len);
+ void update(uint8_t data);
+ void update(const std::string& data) { update(data.c_str(), data.size()); }
+ void update(Typecode t) { update((uint8_t) t); }
+ void update(Direction d) { update((uint8_t) d); }
+ void update(Access a) { update((uint8_t) a); }
+ void update(bool b) { update((uint8_t) (b ? 1 : 0)); }
+ const uint8_t* get() const { return hash; }
+ bool operator==(const SchemaHash& other) const;
+ bool operator<(const SchemaHash& other) const;
+ bool operator>(const SchemaHash& other) const;
+ };
+
+ struct SchemaArgumentImpl {
+ std::string name;
+ Typecode typecode;
+ Direction dir;
+ std::string unit;
+ std::string description;
+
+ SchemaArgumentImpl(const char* n, Typecode t) : name(n), typecode(t), dir(DIR_IN) {}
+ SchemaArgumentImpl(qpid::framing::Buffer& buffer);
+ static SchemaArgument* factory(qpid::framing::Buffer& buffer);
+ void encode(qpid::framing::Buffer& buffer) const;
+ void setDirection(Direction d) { dir = d; }
+ void setUnit(const char* val) { unit = val; }
+ void setDesc(const char* desc) { description = desc; }
+ const std::string& getName() const { return name; }
+ Typecode getType() const { return typecode; }
+ Direction getDirection() const { return dir; }
+ const std::string& getUnit() const { return unit; }
+ const std::string& getDesc() const { return description; }
+ void updateHash(SchemaHash& hash) const;
+ };
+
+ struct SchemaMethodImpl {
+ std::string name;
+ std::string description;
+ std::vector<const SchemaArgument*> arguments;
+
+ SchemaMethodImpl(const char* n) : name(n) {}
+ SchemaMethodImpl(qpid::framing::Buffer& buffer);
+ static SchemaMethod* factory(qpid::framing::Buffer& buffer);
+ void encode(qpid::framing::Buffer& buffer) const;
+ void addArgument(const SchemaArgument* argument);
+ void setDesc(const char* desc) { description = desc; }
+ const std::string& getName() const { return name; }
+ const std::string& getDesc() const { return description; }
+ int getArgumentCount() const { return arguments.size(); }
+ const SchemaArgument* getArgument(int idx) const;
+ void updateHash(SchemaHash& hash) const;
+ };
+
+ struct SchemaPropertyImpl {
+ std::string name;
+ Typecode typecode;
+ Access access;
+ bool index;
+ bool optional;
+ std::string unit;
+ std::string description;
+
+ SchemaPropertyImpl(const char* n, Typecode t) : name(n), typecode(t), access(ACCESS_READ_ONLY), index(false), optional(false) {}
+ SchemaPropertyImpl(qpid::framing::Buffer& buffer);
+ static SchemaProperty* factory(qpid::framing::Buffer& buffer);
+ void encode(qpid::framing::Buffer& buffer) const;
+ void setAccess(Access a) { access = a; }
+ void setIndex(bool val) { index = val; }
+ void setOptional(bool val) { optional = val; }
+ void setUnit(const char* val) { unit = val; }
+ void setDesc(const char* desc) { description = desc; }
+ const std::string& getName() const { return name; }
+ Typecode getType() const { return typecode; }
+ Access getAccess() const { return access; }
+ bool isIndex() const { return index; }
+ bool isOptional() const { return optional; }
+ const std::string& getUnit() const { return unit; }
+ const std::string& getDesc() const { return description; }
+ void updateHash(SchemaHash& hash) const;
+ };
+
+ struct SchemaStatisticImpl {
+ std::string name;
+ Typecode typecode;
+ std::string unit;
+ std::string description;
+
+ SchemaStatisticImpl(const char* n, Typecode t) : name(n), typecode(t) {}
+ SchemaStatisticImpl(qpid::framing::Buffer& buffer);
+ static SchemaStatistic* factory(qpid::framing::Buffer& buffer);
+ void encode(qpid::framing::Buffer& buffer) const;
+ void setUnit(const char* val) { unit = val; }
+ void setDesc(const char* desc) { description = desc; }
+ const std::string& getName() const { return name; }
+ Typecode getType() const { return typecode; }
+ const std::string& getUnit() const { return unit; }
+ const std::string& getDesc() const { return description; }
+ void updateHash(SchemaHash& hash) const;
+ };
+
+ struct SchemaClassKeyImpl {
+ const std::string& package;
+ const std::string& name;
+ const SchemaHash& hash;
+ mutable std::string repr;
+
+ // The *Container elements are only used if there isn't an external place to
+ // store these values.
+ std::string packageContainer;
+ std::string nameContainer;
+ SchemaHash hashContainer;
+
+ SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash);
+ SchemaClassKeyImpl(qpid::framing::Buffer& buffer);
+ static SchemaClassKey* factory(const std::string& package, const std::string& name, const SchemaHash& hash);
+ static SchemaClassKey* factory(qpid::framing::Buffer& buffer);
+
+ const std::string& getPackageName() const { return package; }
+ const std::string& getClassName() const { return name; }
+ const uint8_t* getHash() const { return hash.get(); }
+
+ void encode(qpid::framing::Buffer& buffer) const;
+ bool operator==(const SchemaClassKeyImpl& other) const;
+ bool operator<(const SchemaClassKeyImpl& other) const;
+ const std::string& str() const;
+ };
+
+ struct SchemaObjectClassImpl {
+ std::string package;
+ std::string name;
+ mutable SchemaHash hash;
+ mutable bool hasHash;
+ std::auto_ptr<SchemaClassKey> classKey;
+ std::vector<const SchemaProperty*> properties;
+ std::vector<const SchemaStatistic*> statistics;
+ std::vector<const SchemaMethod*> methods;
+
+ SchemaObjectClassImpl(const char* p, const char* n) :
+ package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)) {}
+ SchemaObjectClassImpl(qpid::framing::Buffer& buffer);
+ static SchemaObjectClass* factory(qpid::framing::Buffer& buffer);
+
+ void encode(qpid::framing::Buffer& buffer) const;
+ void addProperty(const SchemaProperty* property);
+ void addStatistic(const SchemaStatistic* statistic);
+ void addMethod(const SchemaMethod* method);
+
+ const SchemaClassKey* getClassKey() const;
+ int getPropertyCount() const { return properties.size(); }
+ int getStatisticCount() const { return statistics.size(); }
+ int getMethodCount() const { return methods.size(); }
+ const SchemaProperty* getProperty(int idx) const;
+ const SchemaStatistic* getStatistic(int idx) const;
+ const SchemaMethod* getMethod(int idx) const;
+ };
+
+ struct SchemaEventClassImpl {
+ std::string package;
+ std::string name;
+ mutable SchemaHash hash;
+ mutable bool hasHash;
+ std::auto_ptr<SchemaClassKey> classKey;
+ std::string description;
+ Severity severity;
+ std::vector<const SchemaArgument*> arguments;
+
+ SchemaEventClassImpl(const char* p, const char* n, Severity sev) :
+ package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)), severity(sev) {}
+ SchemaEventClassImpl(qpid::framing::Buffer& buffer);
+ static SchemaEventClass* factory(qpid::framing::Buffer& buffer);
+
+ void encode(qpid::framing::Buffer& buffer) const;
+ void addArgument(const SchemaArgument* argument);
+ void setDesc(const char* desc) { description = desc; }
+
+ const SchemaClassKey* getClassKey() const;
+ Severity getSeverity() const { return severity; }
+ int getArgumentCount() const { return arguments.size(); }
+ const SchemaArgument* getArgument(int idx) const;
+ };
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.cpp b/qpid/cpp/src/qmf/engine/SequenceManager.cpp
new file mode 100644
index 0000000000..4a4644a8b9
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/SequenceManager.cpp
@@ -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.
+ */
+
+#include "qmf/engine/SequenceManager.h"
+
+using namespace std;
+using namespace qmf::engine;
+using namespace qpid::sys;
+
+SequenceManager::SequenceManager() : nextSequence(1) {}
+
+void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx)
+{
+ unsolicitedContext = ctx;
+}
+
+uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx)
+{
+ Mutex::ScopedLock _lock(lock);
+ if (ctx.get() == 0)
+ ctx = unsolicitedContext;
+ uint32_t seq = nextSequence;
+ while (contextMap.find(seq) != contextMap.end())
+ seq = seq < 0xFFFFFFFF ? seq + 1 : 1;
+ nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1;
+ contextMap[seq] = ctx;
+ ctx->reserve();
+ return seq;
+}
+
+void SequenceManager::release(uint32_t sequence)
+{
+ Mutex::ScopedLock _lock(lock);
+
+ if (sequence == 0) {
+ if (unsolicitedContext.get() != 0)
+ unsolicitedContext->release();
+ return;
+ }
+
+ map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence);
+ if (iter != contextMap.end()) {
+ if (iter->second != 0)
+ iter->second->release();
+ contextMap.erase(iter);
+ }
+}
+
+void SequenceManager::releaseAll()
+{
+ Mutex::ScopedLock _lock(lock);
+ contextMap.clear();
+}
+
+void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, const string& routingKey, qpid::framing::Buffer& buffer)
+{
+ Mutex::ScopedLock _lock(lock);
+ bool done;
+
+ if (sequence == 0) {
+ if (unsolicitedContext.get() != 0) {
+ done = unsolicitedContext->handleMessage(opcode, sequence, routingKey, buffer);
+ if (done)
+ unsolicitedContext->release();
+ }
+ return;
+ }
+
+ map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence);
+ if (iter != contextMap.end()) {
+ if (iter->second != 0) {
+ done = iter->second->handleMessage(opcode, sequence, routingKey, buffer);
+ if (done) {
+ iter->second->release();
+ contextMap.erase(iter);
+ }
+ }
+ }
+}
+
diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.h b/qpid/cpp/src/qmf/engine/SequenceManager.h
new file mode 100644
index 0000000000..9e47e38610
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/SequenceManager.h
@@ -0,0 +1,68 @@
+#ifndef _QmfEngineSequenceManager_
+#define _QmfEngineSequenceManager_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+namespace qpid {
+ namespace framing {
+ class Buffer;
+ }
+}
+
+namespace qmf {
+namespace engine {
+
+ class SequenceContext {
+ public:
+ typedef boost::shared_ptr<SequenceContext> Ptr;
+ SequenceContext() {}
+ virtual ~SequenceContext() {}
+
+ virtual void reserve() = 0;
+ virtual bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer) = 0;
+ virtual void release() = 0;
+ };
+
+ class SequenceManager {
+ public:
+ SequenceManager();
+
+ void setUnsolicitedContext(SequenceContext::Ptr ctx);
+ uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr());
+ void release(uint32_t sequence);
+ void releaseAll();
+ void dispatch(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer);
+
+ private:
+ mutable qpid::sys::Mutex lock;
+ uint32_t nextSequence;
+ SequenceContext::Ptr unsolicitedContext;
+ std::map<uint32_t, SequenceContext::Ptr> contextMap;
+ };
+
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.cpp b/qpid/cpp/src/qmf/engine/ValueImpl.cpp
new file mode 100644
index 0000000000..f9ebbf5028
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ValueImpl.cpp
@@ -0,0 +1,571 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/engine/ValueImpl.h"
+#include <qpid/framing/FieldValue.h>
+#include <qpid/framing/FieldTable.h>
+#include <qpid/framing/List.h>
+#include <qpid/log/Statement.h>
+
+using namespace std;
+using namespace qmf::engine;
+//using qpid::framing::Buffer;
+//using qpid::framing::FieldTable;
+//using qpid::framing::FieldValue;
+using namespace qpid::framing;
+
+ValueImpl::ValueImpl(Typecode t, Buffer& buf) : typecode(t)
+{
+ uint64_t first;
+ uint64_t second;
+ FieldTable ft;
+ List fl;
+
+ switch (typecode) {
+ case TYPE_UINT8 : value.u32 = (uint32_t) buf.getOctet(); break;
+ case TYPE_UINT16 : value.u32 = (uint32_t) buf.getShort(); break;
+ case TYPE_UINT32 : value.u32 = (uint32_t) buf.getLong(); break;
+ case TYPE_UINT64 : value.u64 = buf.getLongLong(); break;
+ case TYPE_SSTR : buf.getShortString(stringVal); break;
+ case TYPE_LSTR : buf.getMediumString(stringVal); break;
+ case TYPE_ABSTIME : value.s64 = buf.getLongLong(); break;
+ case TYPE_DELTATIME : value.u64 = buf.getLongLong(); break;
+ case TYPE_BOOL : value.boolVal = (buf.getOctet() != 0); break;
+ case TYPE_FLOAT : value.floatVal = buf.getFloat(); break;
+ case TYPE_DOUBLE : value.doubleVal = buf.getDouble(); break;
+ case TYPE_INT8 : value.s32 = (int32_t) ((int8_t) buf.getOctet()); break;
+ case TYPE_INT16 : value.s32 = (int32_t) ((int16_t) buf.getShort()); break;
+ case TYPE_INT32 : value.s32 = (int32_t) buf.getLong(); break;
+ case TYPE_INT64 : value.s64 = buf.getLongLong(); break;
+ case TYPE_UUID : buf.getBin128(value.uuidVal); break;
+ case TYPE_REF:
+ first = buf.getLongLong();
+ second = buf.getLongLong();
+ refVal.impl->setValue(first, second);
+ break;
+
+ case TYPE_MAP:
+ ft.decode(buf);
+ initMap(ft);
+ break;
+
+ case TYPE_LIST:
+ fl.decode(buf);
+ initList(fl);
+ break;
+
+ case TYPE_ARRAY:
+ case TYPE_OBJECT:
+ default:
+ break;
+ }
+}
+
+ValueImpl::ValueImpl(Typecode t, Typecode at) : typecode(t), valid(false), arrayTypecode(at)
+{
+}
+
+ValueImpl::ValueImpl(Typecode t) : typecode(t)
+{
+ ::memset(&value, 0, sizeof(value));
+}
+
+Value* ValueImpl::factory(Typecode t, Buffer& b)
+{
+ ValueImpl* impl(new ValueImpl(t, b));
+ return new Value(impl);
+}
+
+Value* ValueImpl::factory(Typecode t)
+{
+ ValueImpl* impl(new ValueImpl(t));
+ return new Value(impl);
+}
+
+ValueImpl::~ValueImpl()
+{
+}
+
+void ValueImpl::initMap(const FieldTable& ft)
+{
+ for (FieldTable::ValueMap::const_iterator iter = ft.begin();
+ iter != ft.end(); iter++) {
+ const string& name(iter->first);
+ const FieldValue& fvalue(*iter->second);
+ uint8_t amqType = fvalue.getType();
+
+ if (amqType == 0x32) {
+ Value* subval(new Value(TYPE_UINT64));
+ subval->setUint64(fvalue.get<int64_t>());
+ insert(name.c_str(), subval);
+ } else if ((amqType & 0xCF) == 0x02) {
+ Value* subval(new Value(TYPE_UINT32));
+ switch (amqType) {
+ case 0x02 : subval->setUint(fvalue.get<int>()); break;
+ case 0x12 : subval->setUint(fvalue.get<int>()); break;
+ case 0x22 : subval->setUint(fvalue.get<int>()); break;
+ }
+ insert(name.c_str(), subval);
+ } else if (amqType == 0x31) { // int64
+ Value* subval(new Value(TYPE_INT64));
+ subval->setInt64(fvalue.get<int64_t>());
+ insert(name.c_str(), subval);
+ } else if ((amqType & 0xCF) == 0x01) { // 0x01:int8, 0x11:int16, 0x21:int21
+ Value* subval(new Value(TYPE_INT32));
+ subval->setInt((int32_t)fvalue.get<int>());
+ insert(name.c_str(), subval);
+ } else if (amqType == 0x85 || amqType == 0x95) {
+ Value* subval(new Value(TYPE_LSTR));
+ subval->setString(fvalue.get<string>().c_str());
+ insert(name.c_str(), subval);
+ } else if (amqType == 0x23 || amqType == 0x33) {
+ Value* subval(new Value(TYPE_DOUBLE));
+ subval->setDouble(fvalue.get<double>());
+ insert(name.c_str(), subval);
+ } else if (amqType == 0xa8) {
+ FieldTable subFt;
+ bool valid = qpid::framing::getEncodedValue<FieldTable>(iter->second, subFt);
+ if (valid) {
+ Value* subval(new Value(TYPE_MAP));
+ subval->impl->initMap(subFt);
+ insert(name.c_str(), subval);
+ }
+ } else if (amqType == 0xa9) {
+ List subList;
+ bool valid = qpid::framing::getEncodedValue<List>(iter->second, subList);
+ if (valid) {
+ Value* subval(new Value(TYPE_LIST));
+ subval->impl->initList(subList);
+ insert(name.c_str(), subval);
+ }
+ } else if (amqType == 0x08) {
+ Value* subval(new Value(TYPE_BOOL));
+ subval->setBool(fvalue.get<int>() ? true : false);
+ insert(name.c_str(), subval);
+ } else {
+ QPID_LOG(error, "Unable to decode unsupported AMQP typecode=" << amqType << " map index=" << name);
+ }
+ }
+}
+
+void ValueImpl::mapToFieldTable(FieldTable& ft) const
+{
+ FieldTable subFt;
+
+ for (map<string, Value>::const_iterator iter = mapVal.begin();
+ iter != mapVal.end(); iter++) {
+ const string& name(iter->first);
+ const Value& subval(iter->second);
+
+ switch (subval.getType()) {
+ case TYPE_UINT8:
+ case TYPE_UINT16:
+ case TYPE_UINT32:
+ ft.setUInt64(name, (uint64_t) subval.asUint());
+ break;
+ case TYPE_UINT64:
+ case TYPE_DELTATIME:
+ ft.setUInt64(name, subval.asUint64());
+ break;
+ case TYPE_SSTR:
+ case TYPE_LSTR:
+ ft.setString(name, subval.asString());
+ break;
+ case TYPE_INT64:
+ case TYPE_ABSTIME:
+ ft.setInt64(name, subval.asInt64());
+ break;
+ case TYPE_BOOL:
+ ft.set(name, FieldTable::ValuePtr(new BoolValue(subval.asBool())));
+ break;
+ case TYPE_FLOAT:
+ ft.setFloat(name, subval.asFloat());
+ break;
+ case TYPE_DOUBLE:
+ ft.setDouble(name, subval.asDouble());
+ break;
+ case TYPE_INT8:
+ case TYPE_INT16:
+ case TYPE_INT32:
+ ft.setInt(name, subval.asInt());
+ break;
+ case TYPE_MAP:
+ subFt.clear();
+ subval.impl->mapToFieldTable(subFt);
+ ft.setTable(name, subFt);
+ break;
+ case TYPE_LIST:
+ {
+ List subList;
+ subval.impl->listToFramingList(subList);
+ ft.set(name,
+ ::qpid::framing::FieldTable::ValuePtr(
+ new ListValue(
+ subList)));
+ } break;
+ case TYPE_ARRAY:
+ case TYPE_OBJECT:
+ case TYPE_UUID:
+ case TYPE_REF:
+ default:
+ break;
+ }
+ }
+ }
+
+
+void ValueImpl::initList(const List& fl)
+{
+ for (List::const_iterator iter = fl.begin();
+ iter != fl.end(); iter++) {
+ const FieldValue& fvalue(*iter->get());
+ uint8_t amqType = fvalue.getType();
+
+ if (amqType == 0x32) {
+ Value* subval(new Value(TYPE_UINT64));
+ subval->setUint64(fvalue.get<int64_t>());
+ appendToList(subval);
+ } else if ((amqType & 0xCF) == 0x02) {
+ Value* subval(new Value(TYPE_UINT32));
+ switch (amqType) {
+ case 0x02 : subval->setUint(fvalue.get<int>()); break; // uint8
+ case 0x12 : subval->setUint(fvalue.get<int>()); break; // uint16
+ case 0x22 : subval->setUint(fvalue.get<int>()); break; // uint32
+ }
+ appendToList(subval);
+ } else if (amqType == 0x31) { // int64
+ Value* subval(new Value(TYPE_INT64));
+ subval->setInt64(fvalue.get<int64_t>());
+ appendToList(subval);
+ } else if ((amqType & 0xCF) == 0x01) { // 0x01:int8, 0x11:int16, 0x21:int32
+ Value* subval(new Value(TYPE_INT32));
+ subval->setInt((int32_t)fvalue.get<int>());
+ appendToList(subval);
+ } else if (amqType == 0x85 || amqType == 0x95) {
+ Value* subval(new Value(TYPE_LSTR));
+ subval->setString(fvalue.get<string>().c_str());
+ appendToList(subval);
+ } else if (amqType == 0x23 || amqType == 0x33) {
+ Value* subval(new Value(TYPE_DOUBLE));
+ subval->setDouble(fvalue.get<double>());
+ appendToList(subval);
+ } else if (amqType == 0xa8) {
+ FieldTable subFt;
+ bool valid = qpid::framing::getEncodedValue<FieldTable>(*iter, subFt);
+ if (valid) {
+ Value* subval(new Value(TYPE_MAP));
+ subval->impl->initMap(subFt);
+ appendToList(subval);
+ }
+ } else if (amqType == 0xa9) {
+ List subList;
+ bool valid = qpid::framing::getEncodedValue<List>(*iter, subList);
+ if (valid) {
+ Value *subVal(new Value(TYPE_LIST));
+ subVal->impl->initList(subList);
+ appendToList(subVal);
+ }
+ } else if (amqType == 0x08) {
+ Value* subval(new Value(TYPE_BOOL));
+ subval->setBool(fvalue.get<int>() ? true : false);
+ appendToList(subval);
+ } else {
+ QPID_LOG(error, "Unable to decode unsupported AMQP typecode =" << amqType);
+ }
+ }
+}
+
+void ValueImpl::listToFramingList(List& fl) const
+{
+ for (vector<Value>::const_iterator iter = vectorVal.begin();
+ iter != vectorVal.end(); iter++) {
+ const Value& subval(*iter);
+
+ switch (subval.getType()) {
+ case TYPE_UINT8:
+ case TYPE_UINT16:
+ case TYPE_UINT32:
+ fl.push_back(List::ValuePtr(new Unsigned64Value((uint64_t) subval.asUint())));
+ break;
+ case TYPE_UINT64:
+ case TYPE_DELTATIME:
+ fl.push_back(List::ValuePtr(new Unsigned64Value(subval.asUint64())));
+ break;
+ case TYPE_SSTR:
+ case TYPE_LSTR:
+ fl.push_back(List::ValuePtr(new Str16Value(subval.asString())));
+ break;
+ case TYPE_INT64:
+ case TYPE_ABSTIME:
+ fl.push_back(List::ValuePtr(new Integer64Value(subval.asInt64())));
+ break;
+ case TYPE_BOOL:
+ fl.push_back(List::ValuePtr(new BoolValue(subval.asBool() ? 1 : 0)));
+ break;
+ case TYPE_FLOAT:
+ fl.push_back(List::ValuePtr(new FloatValue(subval.asFloat())));
+ break;
+ case TYPE_DOUBLE:
+ fl.push_back(List::ValuePtr(new DoubleValue(subval.asDouble())));
+ break;
+ case TYPE_INT8:
+ case TYPE_INT16:
+ case TYPE_INT32:
+ fl.push_back(List::ValuePtr(new IntegerValue(subval.asInt())));
+ break;
+ case TYPE_MAP:
+ {
+ FieldTable subFt;
+ subval.impl->mapToFieldTable(subFt);
+ fl.push_back(List::ValuePtr(new FieldTableValue(subFt)));
+
+ } break;
+ case TYPE_LIST:
+ {
+ List subList;
+ subval.impl->listToFramingList(subList);
+ fl.push_back(List::ValuePtr(new ListValue(subList)));
+ } break;
+
+ case TYPE_ARRAY:
+ case TYPE_OBJECT:
+ case TYPE_UUID:
+ case TYPE_REF:
+ default:
+ break;
+ }
+ }
+ }
+
+
+
+void ValueImpl::encode(Buffer& buf) const
+{
+ FieldTable ft;
+ List fl;
+
+ switch (typecode) {
+ case TYPE_UINT8 : buf.putOctet((uint8_t) value.u32); break;
+ case TYPE_UINT16 : buf.putShort((uint16_t) value.u32); break;
+ case TYPE_UINT32 : buf.putLong(value.u32); break;
+ case TYPE_UINT64 : buf.putLongLong(value.u64); break;
+ case TYPE_SSTR : buf.putShortString(stringVal); break;
+ case TYPE_LSTR : buf.putMediumString(stringVal); break;
+ case TYPE_ABSTIME : buf.putLongLong(value.s64); break;
+ case TYPE_DELTATIME : buf.putLongLong(value.u64); break;
+ case TYPE_BOOL : buf.putOctet(value.boolVal ? 1 : 0); break;
+ case TYPE_FLOAT : buf.putFloat(value.floatVal); break;
+ case TYPE_DOUBLE : buf.putDouble(value.doubleVal); break;
+ case TYPE_INT8 : buf.putOctet((uint8_t) value.s32); break;
+ case TYPE_INT16 : buf.putShort((uint16_t) value.s32); break;
+ case TYPE_INT32 : buf.putLong(value.s32); break;
+ case TYPE_INT64 : buf.putLongLong(value.s64); break;
+ case TYPE_UUID : buf.putBin128(value.uuidVal); break;
+ case TYPE_REF : refVal.impl->encode(buf); break;
+ case TYPE_MAP:
+ mapToFieldTable(ft);
+ ft.encode(buf);
+ break;
+ case TYPE_LIST:
+ listToFramingList(fl);
+ fl.encode(buf);
+ break;
+
+ case TYPE_ARRAY:
+ case TYPE_OBJECT:
+ default:
+ break;
+ }
+}
+
+uint32_t ValueImpl::encodedSize() const
+{
+ FieldTable ft;
+ List fl;
+
+ switch (typecode) {
+ case TYPE_UINT8 :
+ case TYPE_BOOL :
+ case TYPE_INT8 : return 1;
+
+ case TYPE_UINT16 :
+ case TYPE_INT16 : return 2;
+
+ case TYPE_UINT32 :
+ case TYPE_INT32 :
+ case TYPE_FLOAT : return 4;
+
+ case TYPE_UINT64 :
+ case TYPE_INT64 :
+ case TYPE_DOUBLE :
+ case TYPE_ABSTIME :
+ case TYPE_DELTATIME : return 8;
+
+ case TYPE_UUID :
+ case TYPE_REF : return 16;
+
+ case TYPE_SSTR : return 1 + stringVal.size();
+ case TYPE_LSTR : return 2 + stringVal.size();
+ case TYPE_MAP:
+ mapToFieldTable(ft);
+ return ft.encodedSize();
+
+ case TYPE_LIST:
+ listToFramingList(fl);
+ return fl.encodedSize();
+
+ case TYPE_ARRAY:
+ case TYPE_OBJECT:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+bool ValueImpl::keyInMap(const char* key) const
+{
+ return typecode == TYPE_MAP && mapVal.count(key) > 0;
+}
+
+Value* ValueImpl::byKey(const char* key)
+{
+ if (keyInMap(key)) {
+ map<string, Value>::iterator iter = mapVal.find(key);
+ if (iter != mapVal.end())
+ return &iter->second;
+ }
+ return 0;
+}
+
+const Value* ValueImpl::byKey(const char* key) const
+{
+ if (keyInMap(key)) {
+ map<string, Value>::const_iterator iter = mapVal.find(key);
+ if (iter != mapVal.end())
+ return &iter->second;
+ }
+ return 0;
+}
+
+void ValueImpl::deleteKey(const char* key)
+{
+ mapVal.erase(key);
+}
+
+void ValueImpl::insert(const char* key, Value* val)
+{
+ pair<string, Value> entry(key, *val);
+ mapVal.insert(entry);
+}
+
+const char* ValueImpl::key(uint32_t idx) const
+{
+ map<string, Value>::const_iterator iter = mapVal.begin();
+ for (uint32_t i = 0; i < idx; i++) {
+ if (iter == mapVal.end())
+ break;
+ iter++;
+ }
+
+ if (iter == mapVal.end())
+ return 0;
+ else
+ return iter->first.c_str();
+}
+
+Value* ValueImpl::arrayItem(uint32_t)
+{
+ return 0;
+}
+
+void ValueImpl::appendToArray(Value*)
+{
+}
+
+void ValueImpl::deleteArrayItem(uint32_t)
+{
+}
+
+
+//==================================================================
+// Wrappers
+//==================================================================
+
+Value::Value(const Value& from) : impl(new ValueImpl(*(from.impl))) {}
+Value::Value(Typecode t, Typecode at) : impl(new ValueImpl(t, at)) {}
+Value::Value(ValueImpl* i) : impl(i) {}
+Value::~Value() { delete impl;}
+
+Typecode Value::getType() const { return impl->getType(); }
+bool Value::isNull() const { return impl->isNull(); }
+void Value::setNull() { impl->setNull(); }
+bool Value::isObjectId() const { return impl->isObjectId(); }
+const ObjectId& Value::asObjectId() const { return impl->asObjectId(); }
+void Value::setObjectId(const ObjectId& oid) { impl->setObjectId(oid); }
+bool Value::isUint() const { return impl->isUint(); }
+uint32_t Value::asUint() const { return impl->asUint(); }
+void Value::setUint(uint32_t val) { impl->setUint(val); }
+bool Value::isInt() const { return impl->isInt(); }
+int32_t Value::asInt() const { return impl->asInt(); }
+void Value::setInt(int32_t val) { impl->setInt(val); }
+bool Value::isUint64() const { return impl->isUint64(); }
+uint64_t Value::asUint64() const { return impl->asUint64(); }
+void Value::setUint64(uint64_t val) { impl->setUint64(val); }
+bool Value::isInt64() const { return impl->isInt64(); }
+int64_t Value::asInt64() const { return impl->asInt64(); }
+void Value::setInt64(int64_t val) { impl->setInt64(val); }
+bool Value::isString() const { return impl->isString(); }
+const char* Value::asString() const { return impl->asString(); }
+void Value::setString(const char* val) { impl->setString(val); }
+bool Value::isBool() const { return impl->isBool(); }
+bool Value::asBool() const { return impl->asBool(); }
+void Value::setBool(bool val) { impl->setBool(val); }
+bool Value::isFloat() const { return impl->isFloat(); }
+float Value::asFloat() const { return impl->asFloat(); }
+void Value::setFloat(float val) { impl->setFloat(val); }
+bool Value::isDouble() const { return impl->isDouble(); }
+double Value::asDouble() const { return impl->asDouble(); }
+void Value::setDouble(double val) { impl->setDouble(val); }
+bool Value::isUuid() const { return impl->isUuid(); }
+const uint8_t* Value::asUuid() const { return impl->asUuid(); }
+void Value::setUuid(const uint8_t* val) { impl->setUuid(val); }
+bool Value::isObject() const { return impl->isObject(); }
+const Object* Value::asObject() const { return impl->asObject(); }
+void Value::setObject(Object* val) { impl->setObject(val); }
+bool Value::isMap() const { return impl->isMap(); }
+bool Value::keyInMap(const char* key) const { return impl->keyInMap(key); }
+Value* Value::byKey(const char* key) { return impl->byKey(key); }
+const Value* Value::byKey(const char* key) const { return impl->byKey(key); }
+void Value::deleteKey(const char* key) { impl->deleteKey(key); }
+void Value::insert(const char* key, Value* val) { impl->insert(key, val); }
+uint32_t Value::keyCount() const { return impl->keyCount(); }
+const char* Value::key(uint32_t idx) const { return impl->key(idx); }
+bool Value::isList() const { return impl->isList(); }
+uint32_t Value::listItemCount() const { return impl->listItemCount(); }
+Value* Value::listItem(uint32_t idx) { return impl->listItem(idx); }
+void Value::appendToList(Value* val) { impl->appendToList(val); }
+void Value::deleteListItem(uint32_t idx) { impl->deleteListItem(idx); }
+bool Value::isArray() const { return impl->isArray(); }
+Typecode Value::arrayType() const { return impl->arrayType(); }
+uint32_t Value::arrayItemCount() const { return impl->arrayItemCount(); }
+Value* Value::arrayItem(uint32_t idx) { return impl->arrayItem(idx); }
+void Value::appendToArray(Value* val) { impl->appendToArray(val); }
+void Value::deleteArrayItem(uint32_t idx) { impl->deleteArrayItem(idx); }
+
diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.h b/qpid/cpp/src/qmf/engine/ValueImpl.h
new file mode 100644
index 0000000000..8de8c5329f
--- /dev/null
+++ b/qpid/cpp/src/qmf/engine/ValueImpl.h
@@ -0,0 +1,166 @@
+#ifndef _QmfEngineValueImpl_
+#define _QmfEngineValueImpl_
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <qmf/engine/Value.h>
+#include <qmf/engine/ObjectIdImpl.h>
+#include <qmf/engine/Object.h>
+#include <qpid/framing/Buffer.h>
+#include <string>
+#include <string.h>
+#include <map>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace framing {
+ class FieldTable;
+ class List;
+}
+}
+
+namespace qmf {
+namespace engine {
+
+ // TODO: set valid flag on all value settors
+ // TODO: add a modified flag and accessors
+
+ struct ValueImpl {
+ const Typecode typecode;
+ bool valid;
+
+ ObjectId refVal;
+ std::string stringVal;
+ std::auto_ptr<Object> objectVal;
+ std::map<std::string, Value> mapVal;
+ std::vector<Value> vectorVal;
+ Typecode arrayTypecode;
+
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ int32_t s32;
+ int64_t s64;
+ bool boolVal;
+ float floatVal;
+ double doubleVal;
+ uint8_t uuidVal[16];
+ } value;
+
+ ValueImpl(const ValueImpl& from) :
+ typecode(from.typecode), valid(from.valid), refVal(from.refVal), stringVal(from.stringVal),
+ objectVal(from.objectVal.get() ? new Object(*(from.objectVal)) : 0),
+ mapVal(from.mapVal), vectorVal(from.vectorVal), arrayTypecode(from.arrayTypecode),
+ value(from.value) {}
+
+ ValueImpl(Typecode t, Typecode at);
+ ValueImpl(Typecode t, qpid::framing::Buffer& b);
+ ValueImpl(Typecode t);
+ static Value* factory(Typecode t, qpid::framing::Buffer& b);
+ static Value* factory(Typecode t);
+ ~ValueImpl();
+
+ void encode(qpid::framing::Buffer& b) const;
+ uint32_t encodedSize() const;
+
+ Typecode getType() const { return typecode; }
+ bool isNull() const { return !valid; }
+ void setNull() { valid = false; }
+
+ bool isObjectId() const { return typecode == TYPE_REF; }
+ const ObjectId& asObjectId() const { return refVal; }
+ void setObjectId(const ObjectId& o) { refVal = o; } // TODO
+
+ bool isUint() const { return typecode >= TYPE_UINT8 && typecode <= TYPE_UINT32; }
+ uint32_t asUint() const { return value.u32; }
+ void setUint(uint32_t val) { value.u32 = val; }
+
+ bool isInt() const { return typecode >= TYPE_INT8 && typecode <= TYPE_INT32; }
+ int32_t asInt() const { return value.s32; }
+ void setInt(int32_t val) { value.s32 = val; }
+
+ bool isUint64() const { return typecode == TYPE_UINT64 || typecode == TYPE_DELTATIME; }
+ uint64_t asUint64() const { return value.u64; }
+ void setUint64(uint64_t val) { value.u64 = val; }
+
+ bool isInt64() const { return typecode == TYPE_INT64 || typecode == TYPE_ABSTIME; }
+ int64_t asInt64() const { return value.s64; }
+ void setInt64(int64_t val) { value.s64 = val; }
+
+ bool isString() const { return typecode == TYPE_SSTR || typecode == TYPE_LSTR; }
+ const char* asString() const { return stringVal.c_str(); }
+ void setString(const char* val) { stringVal = val; }
+
+ bool isBool() const { return typecode == TYPE_BOOL; }
+ bool asBool() const { return value.boolVal; }
+ void setBool(bool val) { value.boolVal = val; }
+
+ bool isFloat() const { return typecode == TYPE_FLOAT; }
+ float asFloat() const { return value.floatVal; }
+ void setFloat(float val) { value.floatVal = val; }
+
+ bool isDouble() const { return typecode == TYPE_DOUBLE; }
+ double asDouble() const { return value.doubleVal; }
+ void setDouble(double val) { value.doubleVal = val; }
+
+ bool isUuid() const { return typecode == TYPE_UUID; }
+ const uint8_t* asUuid() const { return value.uuidVal; }
+ void setUuid(const uint8_t* val) { ::memcpy(value.uuidVal, val, 16); }
+
+ bool isObject() const { return typecode == TYPE_OBJECT; }
+ Object* asObject() const { return objectVal.get(); }
+ void setObject(Object* val) { objectVal.reset(val); }
+
+ bool isMap() const { return typecode == TYPE_MAP; }
+ bool keyInMap(const char* key) const;
+ Value* byKey(const char* key);
+ const Value* byKey(const char* key) const;
+ void deleteKey(const char* key);
+ void insert(const char* key, Value* val);
+ uint32_t keyCount() const { return mapVal.size(); }
+ const char* key(uint32_t idx) const;
+
+ bool isList() const { return typecode == TYPE_LIST; }
+ uint32_t listItemCount() const { return vectorVal.size(); }
+ Value* listItem(uint32_t idx) { return idx < listItemCount() ? &vectorVal[idx] : 0; }
+ const Value* listItem(uint32_t idx) const { return idx < listItemCount() ? &vectorVal[idx] : 0; }
+ void appendToList(Value* val) { vectorVal.push_back(*val); }
+ void deleteListItem(uint32_t idx) { if (idx < listItemCount()) vectorVal.erase(vectorVal.begin()+idx); }
+
+ bool isArray() const { return typecode == TYPE_ARRAY; }
+ Typecode arrayType() const { return arrayTypecode; }
+ uint32_t arrayItemCount() const { return 0; }
+ Value* arrayItem(uint32_t idx);
+ void appendToArray(Value* val);
+ void deleteArrayItem(uint32_t idx);
+
+ private:
+ void mapToFieldTable(qpid::framing::FieldTable& ft) const;
+ void initMap(const qpid::framing::FieldTable& ft);
+
+ void listToFramingList(qpid::framing::List& fl) const;
+ void initList(const qpid::framing::List& fl);
+ };
+}
+}
+
+#endif
+
diff --git a/qpid/cpp/src/qmf/exceptions.cpp b/qpid/cpp/src/qmf/exceptions.cpp
new file mode 100644
index 0000000000..be212f62f7
--- /dev/null
+++ b/qpid/cpp/src/qmf/exceptions.cpp
@@ -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.
+ *
+ */
+#include "qmf/exceptions.h"
+
+namespace qmf {
+
+ QmfException::QmfException(const std::string& msg) : qpid::types::Exception(msg) {}
+ QmfException::~QmfException() throw() {}
+
+ KeyNotFound::KeyNotFound(const std::string& msg) : QmfException("Key Not Found: " + msg) {}
+ KeyNotFound::~KeyNotFound() throw() {}
+
+ IndexOutOfRange::IndexOutOfRange() : QmfException("Index out-of-range") {}
+ IndexOutOfRange::~IndexOutOfRange() throw() {}
+
+ OperationTimedOut::OperationTimedOut() : QmfException("Timeout Expired") {}
+ OperationTimedOut::~OperationTimedOut() throw() {}
+}
+
diff --git a/qpid/cpp/src/qmfc.mk b/qpid/cpp/src/qmfc.mk
new file mode 100644
index 0000000000..e445a538a1
--- /dev/null
+++ b/qpid/cpp/src/qmfc.mk
@@ -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.
+#
+
+#
+# qmf console library makefile fragment, to be included in Makefile.am
+#
+lib_LTLIBRARIES += libqmfconsole.la
+
+# Public header files.
+nobase_include_HEADERS += \
+ ../include/qpid/console/Agent.h \
+ ../include/qpid/console/Broker.h \
+ ../include/qpid/console/ClassKey.h \
+ ../include/qpid/console/ConsoleImportExport.h \
+ ../include/qpid/console/ConsoleListener.h \
+ ../include/qpid/console/Event.h \
+ ../include/qpid/console/Object.h \
+ ../include/qpid/console/ObjectId.h \
+ ../include/qpid/console/Package.h \
+ ../include/qpid/console/Schema.h \
+ ../include/qpid/console/SequenceManager.h \
+ ../include/qpid/console/SessionManager.h \
+ ../include/qpid/console/Value.h
+
+libqmfconsole_la_SOURCES = \
+ qpid/console/Agent.cpp \
+ qpid/console/Broker.cpp \
+ qpid/console/ClassKey.cpp \
+ qpid/console/Event.cpp \
+ qpid/console/Object.cpp \
+ qpid/console/ObjectId.cpp \
+ qpid/console/Package.cpp \
+ qpid/console/Schema.cpp \
+ qpid/console/SequenceManager.cpp \
+ qpid/console/SessionManager.cpp \
+ qpid/console/Value.cpp
+
+libqmfconsole_la_LIBADD = libqpidclient.la
+
+QMFCONSOLE_VERSION_INFO = 2:0:0
+libqmfconsole_la_LDFLAGS = -version-info $(QMFCONSOLE_VERSION_INFO)
diff --git a/qpid/cpp/src/qpid.pc.in b/qpid/cpp/src/qpid.pc.in
new file mode 100644
index 0000000000..87d368f20c
--- /dev/null
+++ b/qpid/cpp/src/qpid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: qpid
+Version: @VERSION@
+Description: Qpid C++ client library
+Requires:
+Libs: -L${libdir} -lqpidmessaging @LIBS@
+Cflags: -I${includedir}
diff --git a/qpid/cpp/src/qpid/Address.cpp b/qpid/cpp/src/qpid/Address.cpp
new file mode 100644
index 0000000000..e2b2dfbcdf
--- /dev/null
+++ b/qpid/cpp/src/qpid/Address.cpp
@@ -0,0 +1,38 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/Address.h"
+#include "qpid/client/ConnectionSettings.h"
+
+#include <ostream>
+
+using namespace std;
+
+namespace qpid {
+
+const string Address::TCP("tcp");
+
+ostream& operator<<(ostream& os, const Address& a) {
+ return os << a.protocol << ":" << a.host << ":" << a.port;
+}
+
+bool operator==(const Address& x, const Address& y) {
+ return y.protocol==x.protocol && y.host==x.host && y.port == x.port;
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/BufferRef.h b/qpid/cpp/src/qpid/BufferRef.h
new file mode 100644
index 0000000000..bfe1f9ebaa
--- /dev/null
+++ b/qpid/cpp/src/qpid/BufferRef.h
@@ -0,0 +1,70 @@
+#ifndef QPID_BUFFERREF_H
+#define QPID_BUFFERREF_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+
+/** Template for mutable or const buffer references */
+template <class T> class BufferRefT {
+ public:
+ BufferRefT() : begin_(0), end_(0) {}
+
+ BufferRefT(boost::intrusive_ptr<RefCounted> c, T* begin, T* end) :
+ counter(c), begin_(begin), end_(end) {}
+
+ template <class U> BufferRefT(const BufferRefT<U>& other) :
+ counter(other.counter), begin_(other.begin_), end_(other.end_) {}
+
+ T* begin() const { return begin_; }
+ T* end() const { return end_; }
+
+ /** Return a sub-buffer of the current buffer */
+ BufferRefT sub_buffer(T* begin, T* end) {
+ assert(begin_ <= begin && begin <= end_);
+ assert(begin_ <= end && end <= end_);
+ assert(begin <= end);
+ return BufferRefT(counter, begin, end);
+ }
+
+ private:
+ boost::intrusive_ptr<RefCounted> counter;
+ T* begin_;
+ T* end_;
+};
+
+/**
+ * Reference to a mutable ref-counted buffer.
+ */
+typedef BufferRefT<char> BufferRef;
+
+/**
+ * Reference to a const ref-counted buffer.
+ */
+typedef BufferRefT<const char> ConstBufferRef;
+
+} // namespace qpid
+
+#endif /*!QPID_BUFFERREF_H*/
diff --git a/qpid/cpp/src/qpid/DataDir.cpp b/qpid/cpp/src/qpid/DataDir.cpp
new file mode 100644
index 0000000000..ad732052ab
--- /dev/null
+++ b/qpid/cpp/src/qpid/DataDir.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/DataDir.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/FileSysDir.h"
+#include "qpid/sys/LockFile.h"
+
+namespace qpid {
+
+DataDir::DataDir (std::string path) :
+ enabled (!path.empty ()),
+ dirPath (path)
+{
+ if (!enabled)
+ {
+ QPID_LOG (info, "No data directory - Disabling persistent configuration");
+ return;
+ }
+
+ sys::FileSysDir dir(dirPath);
+ if (!dir.exists())
+ dir.mkdir();
+ std::string lockFileName(path);
+ lockFileName += "/lock";
+ lockFile = std::auto_ptr<sys::LockFile>(new sys::LockFile(lockFileName, true));
+}
+
+DataDir::~DataDir () {}
+
+} // namespace qpid
+
diff --git a/qpid/cpp/src/qpid/DataDir.h b/qpid/cpp/src/qpid/DataDir.h
new file mode 100644
index 0000000000..828299f3ba
--- /dev/null
+++ b/qpid/cpp/src/qpid/DataDir.h
@@ -0,0 +1,54 @@
+#ifndef QPID_DATADIR_H
+#define QPID_DATADIR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include <memory>
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+
+ namespace sys {
+ class LockFile;
+ }
+
+/**
+ * DataDir class.
+ */
+class DataDir
+{
+ const bool enabled;
+ const std::string dirPath;
+ std::auto_ptr<qpid::sys::LockFile> lockFile;
+
+ public:
+
+ QPID_COMMON_EXTERN DataDir (std::string path);
+ QPID_COMMON_EXTERN ~DataDir ();
+
+ bool isEnabled() { return enabled; }
+ const std::string& getPath() { return dirPath; }
+};
+
+} // namespace qpid
+
+#endif /*!QPID_DATADIR_H*/
diff --git a/qpid/cpp/src/qpid/DisableExceptionLogging.h b/qpid/cpp/src/qpid/DisableExceptionLogging.h
new file mode 100644
index 0000000000..04a9240513
--- /dev/null
+++ b/qpid/cpp/src/qpid/DisableExceptionLogging.h
@@ -0,0 +1,39 @@
+#ifndef QPID_DISABLEEXCEPTIONLOGGING_H
+#define QPID_DISABLEEXCEPTIONLOGGING_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+
+/**
+ * Temporarily disable logging in qpid::Exception constructor.
+ * Used by log::Logger to avoid logging exceptions during Logger construction.
+ */
+struct DisableExceptionLogging
+{
+ QPID_COMMON_EXTERN DisableExceptionLogging();
+ QPID_COMMON_EXTERN ~DisableExceptionLogging();
+};
+} // namespace qpid
+
+#endif /*!QPID_DISABLEEXCEPTIONLOGGING_H*/
diff --git a/qpid/cpp/src/qpid/Exception.cpp b/qpid/cpp/src/qpid/Exception.cpp
new file mode 100644
index 0000000000..a6696f06e1
--- /dev/null
+++ b/qpid/cpp/src/qpid/Exception.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include "qpid/DisableExceptionLogging.h"
+#include <typeinfo>
+#include <assert.h>
+#include <string.h>
+
+namespace qpid {
+
+// Note on static initialization order: if an exception is constructed
+// in a static constructor before disableExceptionLogging has been
+// initialized, the worst that can happen is we lose an exception log
+// message. Since we shouldn't be throwing a lot of exceptions during
+// static construction this seems safe.
+static bool disableExceptionLogging = false;
+
+DisableExceptionLogging::DisableExceptionLogging() { disableExceptionLogging = true; }
+DisableExceptionLogging::~DisableExceptionLogging() { disableExceptionLogging = false; }
+
+Exception::Exception(const std::string& msg) throw() : message(msg) {
+ if (disableExceptionLogging) return;
+ QPID_LOG_IF(debug, !msg.empty(), "Exception constructed: " << message);
+}
+
+Exception::~Exception() throw() {}
+
+std::string Exception::getPrefix() const { return ""; }
+
+std::string Exception::getMessage() const { return message; }
+
+const char* Exception::what() const throw() {
+ // Construct the what string the first time it is needed.
+ if (whatStr.empty()) {
+ whatStr = getPrefix();
+ if (!whatStr.empty()) whatStr += ": ";
+ whatStr += message;
+ }
+ return whatStr.c_str();
+}
+
+ClosedException::ClosedException(const std::string& msg)
+ : Exception(msg) {}
+
+std::string ClosedException::getPrefix() const { return "Closed"; }
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/Modules.cpp b/qpid/cpp/src/qpid/Modules.cpp
new file mode 100644
index 0000000000..727e05d212
--- /dev/null
+++ b/qpid/cpp/src/qpid/Modules.cpp
@@ -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.
+ *
+ */
+
+#include "config.h"
+#include "qpid/Modules.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Shlib.h"
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace fs=boost::filesystem;
+
+namespace {
+
+// CMake sets QPID_MODULE_SUFFIX; Autoconf doesn't, so assume Linux .so
+#ifndef QPID_MODULE_SUFFIX
+#define QPID_MODULE_SUFFIX ".so"
+#endif
+
+inline std::string& suffix() {
+ static std::string s(QPID_MODULE_SUFFIX);
+ return s;
+}
+
+bool isShlibName(const std::string& name) {
+ return name.find (suffix()) == name.length() - suffix().length();
+}
+
+}
+
+namespace qpid {
+
+ModuleOptions::ModuleOptions(const std::string& defaultModuleDir)
+ : qpid::Options("Module options"), loadDir(defaultModuleDir), noLoad(false)
+{
+ addOptions()
+ ("module-dir", optValue(loadDir, "DIR"), "Load all shareable modules in this directory")
+ ("load-module", optValue(load, "FILE"), "Specifies additional module(s) to be loaded")
+ ("no-module-dir", optValue(noLoad), "Don't load modules from module directory");
+}
+
+void tryShlib(const char* libname_, bool noThrow) {
+ std::string libname(libname_);
+ if (!isShlibName(libname)) libname += suffix();
+ try {
+ sys::Shlib shlib(libname);
+ }
+ catch (const std::exception& /*e*/) {
+ if (!noThrow)
+ throw;
+ }
+}
+
+void loadModuleDir (std::string dirname, bool isDefault)
+{
+ fs::path dirPath (dirname, fs::native);
+
+ if (!fs::exists (dirPath))
+ {
+ if (isDefault)
+ return;
+ throw Exception ("Directory not found: " + dirname);
+ }
+ if (!fs::is_directory(dirPath))
+ {
+ throw Exception ("Invalid value for module-dir: " + dirname + " is not a directory");
+ }
+
+ fs::directory_iterator endItr;
+ for (fs::directory_iterator itr (dirPath); itr != endItr; ++itr)
+ {
+ if (!fs::is_directory(*itr) && isShlibName(itr->string()))
+ tryShlib (itr->string().data(), true);
+ }
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/Modules.h b/qpid/cpp/src/qpid/Modules.h
new file mode 100644
index 0000000000..159dd156c1
--- /dev/null
+++ b/qpid/cpp/src/qpid/Modules.h
@@ -0,0 +1,44 @@
+#ifndef QPID_MODULES_H
+#define QPID_MODULES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Options.h"
+#include <string>
+#include <vector>
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+
+struct ModuleOptions : public qpid::Options {
+ std::string loadDir;
+ std::vector<std::string> load;
+ bool noLoad;
+ QPID_COMMON_EXTERN ModuleOptions(const std::string& defaultModuleDir);
+};
+
+QPID_COMMON_EXTERN void tryShlib(const char* libname, bool noThrow);
+QPID_COMMON_EXTERN void loadModuleDir (std::string dirname, bool isDefault);
+
+} // namespace qpid
+
+#endif /*!QPID_MODULES_H*/
diff --git a/qpid/cpp/src/qpid/Options.cpp b/qpid/cpp/src/qpid/Options.cpp
new file mode 100644
index 0000000000..4b13e349f5
--- /dev/null
+++ b/qpid/cpp/src/qpid/Options.cpp
@@ -0,0 +1,200 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/Options.h"
+#include "qpid/Exception.h"
+
+#include <boost/bind.hpp>
+
+#include <fstream>
+#include <algorithm>
+#include <iostream>
+
+namespace qpid {
+
+using namespace std;
+
+
+namespace {
+
+struct EnvOptMapper {
+ static bool matchChar(char env, char opt) {
+ return (env==toupper(opt)) || (strchr("-.", opt) && env=='_');
+ }
+
+ static bool matchStr(const string& env, boost::shared_ptr<po::option_description> desc) {
+ return desc->long_name().size() == env.size() &&
+ std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar);
+ }
+
+ static bool matchCase(const string& env, boost::shared_ptr<po::option_description> desc) {
+ return env == desc->long_name();
+ }
+
+ EnvOptMapper(const Options& o) : opts(o) {}
+
+ string operator()(const string& envVar) {
+ static const std::string prefix("QPID_");
+ if (envVar.substr(0, prefix.size()) == prefix) {
+ string env = envVar.substr(prefix.size());
+ typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs;
+ OptDescs::const_iterator i =
+ find_if(opts.options().begin(), opts.options().end(), boost::bind(matchStr, env, _1));
+ if (i != opts.options().end())
+ return (*i)->long_name();
+ }
+ return string();
+ }
+
+
+ bool
+ isComment ( string const & str )
+ {
+ size_t i = str.find_first_not_of ( " \t" );
+
+ if ( i == string::npos )
+ return true;
+
+ return str[i] == '#';
+ }
+
+
+ string configFileLine (string& line) {
+
+ if ( isComment ( line ) )
+ return string();
+
+ size_t pos = line.find ('=');
+ if (pos == string::npos)
+ return string();
+ string key = line.substr (0, pos);
+#if (BOOST_VERSION >= 103300)
+ typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs;
+ OptDescs::const_iterator i =
+ find_if(opts.options().begin(), opts.options().end(), boost::bind(matchCase, key, _1));
+ if (i != opts.options().end())
+ return string (line) + "\n";
+ else
+ return string();
+#else
+ // Use 'count' to see if this option exists. Using 'find' will SEGV or hang
+ // if the option has not been defined yet.
+ if ( opts.count(key.c_str()) > 0 )
+ return string ( line ) + "\n";
+ else
+ return string ( );
+#endif
+ }
+
+ const Options& opts;
+};
+
+}
+std::string prettyArg(const std::string& name, const std::string& value) {
+ return value.empty() ? name+" " : name+" ("+value+") ";
+}
+
+Options::Options(const string& name) :
+ po::options_description(name)
+{
+}
+
+
+
+
+
+void Options::parse(int argc, char const* const* argv, const std::string& configFile, bool allowUnknown)
+{
+ string defaultConfigFile = configFile; // May be changed by env/cmdline
+ string parsing;
+ try {
+ po::variables_map vm;
+ parsing="command line options";
+ if (argc > 0 && argv != 0) {
+ if (allowUnknown) {
+ // This hideous workaround is required because boost 1.33 has a bug
+ // that causes 'allow_unregistered' to not work.
+ po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)).
+ options(*this).allow_unregistered();
+ po::parsed_options opts = clp.run();
+ po::parsed_options filtopts = clp.run();
+ filtopts.options.clear ();
+ for (std::vector< po::basic_option<char> >::iterator i = opts.options.begin();
+ i != opts.options.end(); i++)
+ if (!i->unregistered)
+ filtopts.options.push_back (*i);
+ po::store(filtopts, vm);
+
+ }
+ else
+ po::store(po::parse_command_line(argc, const_cast<char**>(argv), *this), vm);
+ }
+ parsing="environment variables";
+ po::store(po::parse_environment(*this, EnvOptMapper(*this)), vm);
+ po::notify(vm); // configFile may be updated from arg/env options.
+ if (!configFile.empty()) {
+ parsing="configuration file "+configFile;
+ ifstream conf(configFile.c_str());
+ if (conf.good()) {
+ // Remove this hack when we get a stable version of boost that
+ // can allow unregistered options in config files.
+ EnvOptMapper mapper(*this);
+ stringstream filtered;
+
+ while (!conf.eof()) {
+ string line;
+ getline (conf, line);
+ filtered << mapper.configFileLine (line);
+ }
+
+ po::store(po::parse_config_file(filtered, *this), vm);
+ // End of hack
+ }
+ else {
+ // No error if default configfile is missing/unreadable
+ // but complain for non-default config file.
+ if (configFile != defaultConfigFile)
+ throw Exception("cannot read configuration file "
+ +configFile);
+ }
+ }
+ po::notify(vm);
+ }
+ catch (const std::exception& e) {
+ ostringstream msg;
+ msg << "Error in " << parsing << ": " << e.what() << endl;
+#if (BOOST_VERSION >= 103300)
+ if (find_nothrow("help", false))
+ msg << "Use --help to see valid options" << endl;
+#endif
+ throw Exception(msg.str());
+ }
+}
+
+CommonOptions::CommonOptions(const string& name, const string& configfile)
+ : Options(name), config(configfile)
+{
+ addOptions()
+ ("help,h", optValue(help), "Displays the help message")
+ ("version,v", optValue(version), "Displays version information")
+ ("config", optValue(config, "FILE"), "Reads configuration from FILE");
+}
+
+
+} // namespace qpid
+
diff --git a/qpid/cpp/src/qpid/Plugin.cpp b/qpid/cpp/src/qpid/Plugin.cpp
new file mode 100644
index 0000000000..196b5c2333
--- /dev/null
+++ b/qpid/cpp/src/qpid/Plugin.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+
+namespace qpid {
+
+namespace {
+
+Plugin::Plugins& thePlugins() {
+ // This is a single threaded singleton implementation so
+ // it is important to be sure that the first use of this
+ // singleton is when the program is still single threaded
+ static Plugin::Plugins plugins;
+ return plugins;
+}
+
+void invoke(boost::function<void()> f) { f(); }
+
+} // namespace
+
+Plugin::Target::~Target() { finalize(); }
+
+void Plugin::Target::finalize() {
+ std::for_each(finalizers.begin(), finalizers.end(), invoke);
+ finalizers.clear();
+}
+
+void Plugin::Target::addFinalizer(const boost::function<void()>& f) {
+ finalizers.push_back(f);
+}
+
+namespace {
+bool initBefore(const Plugin* a, const Plugin* b) {
+ return a->initOrder() < b->initOrder();
+}
+}
+
+Plugin::Plugin() {
+ // Register myself.
+ thePlugins().push_back(this);
+ std::sort(thePlugins().begin(), thePlugins().end(), &initBefore);
+}
+
+Plugin::~Plugin() {}
+
+Options* Plugin::getOptions() { return 0; }
+
+const Plugin::Plugins& Plugin::getPlugins() { return thePlugins(); }
+
+namespace {
+template <class F> void each_plugin(const F& f) {
+ std::for_each(Plugin::getPlugins().begin(), Plugin::getPlugins().end(), f);
+}
+}
+
+void Plugin::addOptions(Options& opts) {
+ for (Plugins::const_iterator i = getPlugins().begin(); i != getPlugins().end(); ++i) {
+ if ((*i)->getOptions())
+ opts.add(*(*i)->getOptions());
+ }
+}
+
+int Plugin::initOrder() const { return DEFAULT_INIT_ORDER; }
+
+void Plugin::earlyInitAll(Target& t) {
+ each_plugin(boost::bind(&Plugin::earlyInitialize, _1, boost::ref(t)));
+}
+
+void Plugin::initializeAll(Target& t) {
+ each_plugin(boost::bind(&Plugin::initialize, _1, boost::ref(t)));
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/Plugin.h b/qpid/cpp/src/qpid/Plugin.h
new file mode 100644
index 0000000000..4e057872b9
--- /dev/null
+++ b/qpid/cpp/src/qpid/Plugin.h
@@ -0,0 +1,129 @@
+#ifndef QPID_PLUGIN_H
+#define QPID_PLUGIN_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+#include <vector>
+#include "qpid/CommonImportExport.h"
+
+/**@file Generic plug-in framework. */
+
+namespace qpid {
+struct Options;
+
+/**
+ * Plug-in base class.
+ */
+class Plugin : private boost::noncopyable {
+ public:
+ typedef std::vector<Plugin*> Plugins;
+ /** Default value returned by initOrder() */
+ static const int DEFAULT_INIT_ORDER=1000;
+
+ /**
+ * Base interface for targets that can receive plug-ins.
+ * Also allows plug-ins to attach a a function to be called
+ * when the target is 'finalized'.
+ */
+ class Target : private boost::noncopyable
+ {
+ public:
+ /** Calls finalize() if not already called. */
+ QPID_COMMON_EXTERN virtual ~Target();
+
+ /** Run all the finalizers */
+ QPID_COMMON_EXTERN void finalize();
+
+ /** Add a function to run when finalize() is called */
+ QPID_COMMON_EXTERN void addFinalizer(const boost::function<void()>&);
+
+ private:
+ std::vector<boost::function<void()> > finalizers;
+ };
+
+ /**
+ * Constructor registers the plug-in to appear in getPlugins().
+ *
+ * A concrete Plugin is instantiated as a global or static
+ * member variable in a library so it is registered during
+ * initialization when the library is loaded.
+ */
+ QPID_COMMON_EXTERN Plugin();
+
+ QPID_COMMON_EXTERN virtual ~Plugin();
+
+ /**
+ * Configuration options for the plugin.
+ * Then will be updated during option parsing by the host program.
+ *
+ * @return An options group or 0 for no options. Default returns 0.
+ * Plugin retains ownership of return value.
+ */
+ QPID_COMMON_EXTERN virtual Options* getOptions();
+
+ /**
+ * Initialize Plugin functionality on a Target, called before
+ * initializing the target.
+ *
+ * Plugins should ignore targets they don't recognize.
+ *
+ * Called before the target itself is initialized.
+ */
+ virtual void earlyInitialize(Target&) = 0;
+
+ /**
+ * Initialize Plugin functionality on a Target. Called after
+ * initializing the target.
+ *
+ * Plugins should ignore targets they don't recognize.
+ *
+ * Called after the target is fully initialized.
+ */
+ virtual void initialize(Target&) = 0;
+
+ /**
+ * Initialization order. If a plugin does not override this, it
+ * returns DEFAULT_INIT_ORDER. Plugins that need to be initialized
+ * earlier/later than normal can override initOrder to return
+ * a lower/higher value than DEFAULT_INIT_ORDER.
+ */
+ QPID_COMMON_EXTERN virtual int initOrder() const;
+
+ /** List of registered Plugin objects.
+ * Caller must not delete plugin pointers.
+ */
+ QPID_COMMON_EXTERN static const Plugins& getPlugins();
+
+ /** Call earlyInitialize() on all registered plugins */
+ QPID_COMMON_EXTERN static void earlyInitAll(Target&);
+
+ /** Call initialize() on all registered plugins */
+ QPID_COMMON_EXTERN static void initializeAll(Target&);
+
+ /** For each registered plugin, add plugin.getOptions() to opts. */
+ QPID_COMMON_EXTERN static void addOptions(Options& opts);
+};
+
+} // namespace qpid
+
+#endif /*!QPID_PLUGIN_H*/
diff --git a/qpid/cpp/src/qpid/RefCounted.h b/qpid/cpp/src/qpid/RefCounted.h
new file mode 100644
index 0000000000..f9e0107103
--- /dev/null
+++ b/qpid/cpp/src/qpid/RefCounted.h
@@ -0,0 +1,63 @@
+#ifndef QPID_REFCOUNTED_H
+#define QPID_REFCOUNTED_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/utility.hpp>
+#include <boost/detail/atomic_count.hpp>
+
+namespace qpid {
+
+/**
+ * Reference-counted base class.
+ * Note: this class isn't copyable - you must copy the intrusive_ptr that points
+ * to the class that has mixed this in not the class itself (as that would sidestep
+ * the reference counting)
+ */
+class RefCounted : boost::noncopyable {
+ mutable boost::detail::atomic_count count;
+
+public:
+ RefCounted() : count(0) {}
+ void addRef() const { ++count; }
+ void release() const { if (--count==0) released(); }
+ long refCount() { return count; }
+
+protected:
+ virtual ~RefCounted() {};
+ // Allow subclasses to over-ride behavior when refcount reaches 0.
+ virtual void released() const { delete this; }
+};
+
+
+} // namespace qpid
+
+// intrusive_ptr support.
+namespace boost {
+template <typename T>
+inline void intrusive_ptr_add_ref(const T* p) { p->qpid::RefCounted::addRef(); }
+template <typename T>
+inline void intrusive_ptr_release(const T* p) { p->qpid::RefCounted::release(); }
+}
+
+
+#endif /*!QPID_REFCOUNTED_H*/
diff --git a/qpid/cpp/src/qpid/RefCountedBuffer.cpp b/qpid/cpp/src/qpid/RefCountedBuffer.cpp
new file mode 100644
index 0000000000..40d620f7ad
--- /dev/null
+++ b/qpid/cpp/src/qpid/RefCountedBuffer.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/RefCountedBuffer.h"
+#include <new>
+
+namespace qpid {
+
+void RefCountedBuffer::released() const {
+ this->~RefCountedBuffer();
+ ::delete[] reinterpret_cast<const char*>(this);
+}
+
+BufferRef RefCountedBuffer::create(size_t n) {
+ char* store=::new char[n+sizeof(RefCountedBuffer)];
+ new(store) RefCountedBuffer;
+ char* start = store+sizeof(RefCountedBuffer);
+ return BufferRef(
+ boost::intrusive_ptr<RefCounted>(reinterpret_cast<RefCountedBuffer*>(store)),
+ start, start+n);
+}
+
+} // namespace qpid
+
+
diff --git a/qpid/cpp/src/qpid/RefCountedBuffer.h b/qpid/cpp/src/qpid/RefCountedBuffer.h
new file mode 100644
index 0000000000..f0ea86130b
--- /dev/null
+++ b/qpid/cpp/src/qpid/RefCountedBuffer.h
@@ -0,0 +1,44 @@
+#ifndef QPID_REFCOUNTEDBUFFER_H
+#define QPID_REFCOUNTEDBUFFER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/RefCounted.h>
+#include <qpid/BufferRef.h>
+
+namespace qpid {
+
+/**
+ * Reference-counted byte buffer. No alignment guarantees.
+ */
+class RefCountedBuffer : public RefCounted {
+ public:
+ /** Create a reference counted buffer of size n */
+ static BufferRef create(size_t n);
+
+ protected:
+ void released() const;
+};
+
+} // namespace qpid
+
+#endif /*!QPID_REFCOUNTEDBUFFER_H*/
diff --git a/qpid/cpp/src/qpid/Sasl.h b/qpid/cpp/src/qpid/Sasl.h
new file mode 100644
index 0000000000..9a9d61b037
--- /dev/null
+++ b/qpid/cpp/src/qpid/Sasl.h
@@ -0,0 +1,60 @@
+#ifndef QPID_SASL_H
+#define QPID_SASL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <memory>
+#include <string>
+#include "qpid/sys/IntegerTypes.h"
+
+namespace qpid {
+
+namespace sys {
+class SecurityLayer;
+struct SecuritySettings;
+}
+
+/**
+ * Interface to SASL support. This class is implemented by platform-specific
+ * SASL providers.
+ */
+class Sasl
+{
+ public:
+ /**
+ * Start SASL negotiation with the broker.
+ *
+ * @param mechanisms Comma-separated list of the SASL mechanism the
+ * client supports.
+ * @param externalSecuritySettings security related details from the underlying transport
+ */
+ virtual std::string start(const std::string& mechanisms,
+ const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0;
+ virtual std::string step(const std::string& challenge) = 0;
+ virtual std::string getMechanism() = 0;
+ virtual std::string getUserId() = 0;
+ virtual std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(uint16_t maxFrameSize) = 0;
+ virtual ~Sasl() {}
+};
+} // namespace qpid
+
+#endif /*!QPID_SASL_H*/
diff --git a/qpid/cpp/src/qpid/SaslFactory.cpp b/qpid/cpp/src/qpid/SaslFactory.cpp
new file mode 100644
index 0000000000..f117404028
--- /dev/null
+++ b/qpid/cpp/src/qpid/SaslFactory.cpp
@@ -0,0 +1,429 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid//SaslFactory.h"
+#include <map>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef HAVE_SASL
+
+namespace qpid {
+
+//Null implementation
+
+SaslFactory::SaslFactory() {}
+
+SaslFactory::~SaslFactory() {}
+
+SaslFactory& SaslFactory::getInstance()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (!instance.get()) {
+ instance = std::auto_ptr<SaslFactory>(new SaslFactory());
+ }
+ return *instance;
+}
+
+std::auto_ptr<Sasl> SaslFactory::create( const std::string &, const std::string &, const std::string &, const std::string &, int, int, bool )
+{
+ return std::auto_ptr<Sasl>();
+}
+
+qpid::sys::Mutex SaslFactory::lock;
+std::auto_ptr<SaslFactory> SaslFactory::instance;
+
+} // namespace qpid
+
+#else
+
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
+#include "qpid/log/Statement.h"
+#include <sasl/sasl.h>
+#include <strings.h>
+
+namespace qpid {
+
+using qpid::sys::SecurityLayer;
+using qpid::sys::SecuritySettings;
+using qpid::sys::cyrus::CyrusSecurityLayer;
+using qpid::framing::InternalErrorException;
+
+const size_t MAX_LOGIN_LENGTH = 50;
+
+struct CyrusSaslSettings
+{
+ CyrusSaslSettings ( ) :
+ username ( std::string(0) ),
+ password ( std::string(0) ),
+ service ( std::string(0) ),
+ host ( std::string(0) ),
+ minSsf ( 0 ),
+ maxSsf ( 0 )
+ {
+ }
+
+ CyrusSaslSettings ( const std::string & user, const std::string & password, const std::string & service, const std::string & host, int minSsf, int maxSsf ) :
+ username(user),
+ password(password),
+ service(service),
+ host(host),
+ minSsf(minSsf),
+ maxSsf(maxSsf)
+ {
+ }
+
+ std::string username,
+ password,
+ service,
+ host;
+
+ int minSsf,
+ maxSsf;
+};
+
+
+class CyrusSasl : public Sasl
+{
+ public:
+ CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction);
+ ~CyrusSasl();
+ std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings);
+ std::string step(const std::string& challenge);
+ std::string getMechanism();
+ std::string getUserId();
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+ private:
+ sasl_conn_t* conn;
+ sasl_callback_t callbacks[5];//realm, user, authname, password, end-of-list
+ CyrusSaslSettings settings;
+ std::string input;
+ std::string mechanism;
+ char login[MAX_LOGIN_LENGTH];
+
+ /* In some contexts, like running in the broker or as a daemon, console
+ * interaction is impossible. In those cases, we will treat the attempt
+ * to interact as an error. */
+ bool allowInteraction;
+ void interact(sasl_interact_t* client_interact);
+};
+
+//sasl callback functions
+int getUserFromSettings(void *context, int id, const char **result, unsigned *len);
+int getPasswordFromSettings(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret);
+typedef int CallbackProc();
+
+qpid::sys::Mutex SaslFactory::lock;
+std::auto_ptr<SaslFactory> SaslFactory::instance;
+
+SaslFactory::SaslFactory()
+{
+ sasl_callback_t* callbacks = 0;
+ int result = sasl_client_init(callbacks);
+ if (result != SASL_OK) {
+ throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errstring(result, 0, 0)));
+ }
+}
+
+SaslFactory::~SaslFactory()
+{
+ sasl_done();
+}
+
+SaslFactory& SaslFactory::getInstance()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (!instance.get()) {
+ instance = std::auto_ptr<SaslFactory>(new SaslFactory());
+ }
+ return *instance;
+}
+
+std::auto_ptr<Sasl> SaslFactory::create(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction)
+{
+ std::auto_ptr<Sasl> sasl(new CyrusSasl(username, password, serviceName, hostName, minSsf, maxSsf, allowInteraction));
+ return sasl;
+}
+
+CyrusSasl::CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction)
+ : conn(0), settings(username, password, serviceName, hostName, minSsf, maxSsf), allowInteraction(allowInteraction)
+{
+ size_t i = 0;
+
+ callbacks[i].id = SASL_CB_GETREALM;
+ callbacks[i].proc = 0;
+ callbacks[i++].context = 0;
+
+ if (!settings.username.empty()) {
+ callbacks[i].id = SASL_CB_AUTHNAME;
+ callbacks[i].proc = (CallbackProc*) &getUserFromSettings;
+ callbacks[i++].context = &settings;
+
+ callbacks[i].id = SASL_CB_PASS;
+ if (settings.password.empty()) {
+ callbacks[i].proc = 0;
+ callbacks[i++].context = 0;
+ } else {
+ callbacks[i].proc = (CallbackProc*) &getPasswordFromSettings;
+ callbacks[i++].context = &settings;
+ }
+ }
+
+
+ callbacks[i].id = SASL_CB_LIST_END;
+ callbacks[i].proc = 0;
+ callbacks[i++].context = 0;
+}
+
+CyrusSasl::~CyrusSasl()
+{
+ if (conn) {
+ sasl_dispose(&conn);
+ }
+}
+
+namespace {
+ const std::string SSL("ssl");
+}
+
+std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettings* externalSettings)
+{
+ QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << ")");
+ int result = sasl_client_new(settings.service.c_str(),
+ settings.host.c_str(),
+ 0, 0, /* Local and remote IP address strings */
+ callbacks,
+ 0, /* security flags */
+ &conn);
+
+ if (result != SASL_OK) throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn)));
+
+ sasl_security_properties_t secprops;
+
+ if (externalSettings) {
+ sasl_ssf_t external_ssf = (sasl_ssf_t) externalSettings->ssf;
+ if (external_ssf) {
+ int result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &external_ssf);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result));
+ }
+ QPID_LOG(debug, "external SSF detected and set to " << external_ssf);
+ }
+ if (externalSettings->authid.size()) {
+ const char* external_authid = externalSettings->authid.c_str();
+ result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, external_authid);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result));
+ }
+ QPID_LOG(debug, "external auth detected and set to " << external_authid);
+ }
+ }
+
+ secprops.min_ssf = settings.minSsf;
+ secprops.max_ssf = settings.maxSsf;
+ secprops.maxbufsize = 65535;
+
+ QPID_LOG(debug, "min_ssf: " << secprops.min_ssf << ", max_ssf: " << secprops.max_ssf);
+
+ secprops.property_names = 0;
+ secprops.property_values = 0;
+ secprops.security_flags = 0;//TODO: provide means for application to configure these
+
+ result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn)));
+ }
+
+ sasl_interact_t* client_interact = 0;
+ const char *out = 0;
+ unsigned outlen = 0;
+ const char *chosenMechanism = 0;
+
+ do {
+ result = sasl_client_start(conn,
+ mechanisms.c_str(),
+ &client_interact,
+ &out,
+ &outlen,
+ &chosenMechanism);
+
+ if (result == SASL_INTERACT) {
+ interact(client_interact);
+ }
+ } while (result == SASL_INTERACT);
+
+ if (result != SASL_CONTINUE && result != SASL_OK) {
+ throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn)));
+ }
+
+ mechanism = std::string(chosenMechanism);
+ QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << "): selected "
+ << mechanism << " response: '" << std::string(out, outlen) << "'");
+ return std::string(out, outlen);
+}
+
+std::string CyrusSasl::step(const std::string& challenge)
+{
+ sasl_interact_t* client_interact = 0;
+ const char *out = 0;
+ unsigned outlen = 0;
+ int result = 0;
+ do {
+ result = sasl_client_step(conn, /* our context */
+ challenge.data(), /* the data from the server */
+ challenge.size(), /* it's length */
+ &client_interact, /* this should be
+ unallocated and NULL */
+ &out, /* filled in on success */
+ &outlen); /* filled in on success */
+
+ if (result == SASL_INTERACT) {
+ interact(client_interact);
+ }
+ } while (result == SASL_INTERACT);
+
+ std::string response;
+ if (result == SASL_CONTINUE || result == SASL_OK) response = std::string(out, outlen);
+ else if (result != SASL_OK) {
+ throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn)));
+ }
+ QPID_LOG(debug, "CyrusSasl::step(" << challenge << "): " << response);
+ return response;
+}
+
+std::string CyrusSasl::getMechanism()
+{
+ return mechanism;
+}
+
+std::string CyrusSasl::getUserId()
+{
+ int propResult;
+ const void* operName;
+
+ propResult = sasl_getprop(conn, SASL_USERNAME, &operName);
+ if (propResult == SASL_OK)
+ return std::string((const char*) operName);
+
+ return std::string();
+}
+
+void CyrusSasl::interact(sasl_interact_t* client_interact)
+{
+
+ /*
+ In some context console interaction cannot be allowed, such
+ as when this code run as part of a broker, or as a some other
+ daemon. In those cases we will treat the attempt to
+ */
+ if ( ! allowInteraction ) {
+ throw InternalErrorException("interaction disallowed");
+ }
+
+ if (client_interact->id == SASL_CB_PASS) {
+ char* password = getpass(client_interact->prompt);
+ input = std::string(password);
+ client_interact->result = input.data();
+ client_interact->len = input.size();
+ } else {
+ std::cout << client_interact->prompt;
+ if (client_interact->defresult) std::cout << " (" << client_interact->defresult << ")";
+ std::cout << ": ";
+ if (std::cin >> input) {
+ client_interact->result = input.data();
+ client_interact->len = input.size();
+ }
+ }
+
+}
+
+std::auto_ptr<SecurityLayer> CyrusSasl::getSecurityLayer(uint16_t maxFrameSize)
+{
+ const void* value(0);
+ int result = sasl_getprop(conn, SASL_SSF, &value);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn)));
+ }
+ uint ssf = *(reinterpret_cast<const unsigned*>(value));
+ std::auto_ptr<SecurityLayer> securityLayer;
+ if (ssf) {
+ QPID_LOG(info, "Installing security layer, SSF: "<< ssf);
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(conn, maxFrameSize));
+ }
+ return securityLayer;
+}
+
+int getUserFromSettings(void* context, int /*id*/, const char** result, unsigned* /*len*/)
+{
+ if (context) {
+ *result = ((CyrusSaslSettings*) context)->username.c_str();
+ QPID_LOG(debug, "getUserFromSettings(): " << (*result));
+ return SASL_OK;
+ } else {
+ return SASL_FAIL;
+ }
+}
+
+namespace {
+// Global map of secrets allocated for SASL connections via callback
+// to getPasswordFromSettings. Ensures secrets are freed.
+class SecretsMap {
+ typedef std::map<sasl_conn_t*, void*> Map;
+ Map map;
+ public:
+ void keep(sasl_conn_t* conn, void* secret) {
+ Map::iterator i = map.find(conn);
+ if (i != map.end()) free(i->second);
+ map[conn] = secret;
+ }
+
+ ~SecretsMap() {
+ for (Map::iterator i = map.begin(); i != map.end(); ++i)
+ free(i->second);
+ }
+};
+SecretsMap getPasswordFromSettingsSecrets;
+}
+
+int getPasswordFromSettings(sasl_conn_t* conn, void* context, int /*id*/, sasl_secret_t** psecret)
+{
+ if (context) {
+ size_t length = ((CyrusSaslSettings*) context)->password.size();
+ sasl_secret_t* secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t) + length);
+ getPasswordFromSettingsSecrets.keep(conn, secret);
+ secret->len = length;
+ memcpy(secret->data, ((CyrusSaslSettings*) context)->password.data(), length);
+ *psecret = secret;
+ return SASL_OK;
+ } else {
+ return SASL_FAIL;
+ }
+}
+
+} // namespace qpid
+
+#endif
diff --git a/qpid/cpp/src/qpid/SaslFactory.h b/qpid/cpp/src/qpid/SaslFactory.h
new file mode 100644
index 0000000000..8554597147
--- /dev/null
+++ b/qpid/cpp/src/qpid/SaslFactory.h
@@ -0,0 +1,47 @@
+#ifndef QPID_SASLFACTORY_H
+#define QPID_SASLFACTORY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/Sasl.h"
+#include "qpid/sys/Mutex.h"
+#include <memory>
+
+namespace qpid {
+
+/**
+ * Factory for instances of the Sasl interface through which Sasl
+ * support is provided to a ConnectionHandler.
+ */
+class SaslFactory
+{
+ public:
+ QPID_COMMON_EXTERN std::auto_ptr<Sasl> create(const std::string & userName, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction=true );
+ QPID_COMMON_EXTERN static SaslFactory& getInstance();
+ QPID_COMMON_EXTERN ~SaslFactory();
+ private:
+ SaslFactory();
+ static qpid::sys::Mutex lock;
+ static std::auto_ptr<SaslFactory> instance;
+};
+} // namespace qpid
+
+#endif /*!QPID_SASLFACTORY_H*/
diff --git a/qpid/cpp/src/qpid/Serializer.h b/qpid/cpp/src/qpid/Serializer.h
new file mode 100644
index 0000000000..a8ded9f5e0
--- /dev/null
+++ b/qpid/cpp/src/qpid/Serializer.h
@@ -0,0 +1,197 @@
+#ifndef QPID_SERIALIZER_H
+#define QPID_SERIALIZER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <limits>
+#include <algorithm>
+#include "qpid/Exception.h" // FIXME aconway 2008-04-03: proper exception class.
+
+namespace qpid {
+
+/**
+ * Overload for types that do not provide a serialize() member.
+ * It should retrun a wrapper holding a reference to t that implements
+ * serialize()
+ */
+template <class T> T& serializable(T& t) { return t; }
+
+/** Serialize std::pair */
+template <class T, class U> struct SerializablePair {
+ std::pair<T,U>& value;
+ SerializablePair(std::pair<T,U>& x) : value(x) {}
+ template <class S> void serialize(S& s) { s(value.first)(value.second); }
+};
+
+template <class T, class U>
+SerializablePair<T,U> serializable(std::pair<T,U>& p) {
+ return SerializablePair<T,U>(p);
+}
+
+/**
+ * Base class for all serializers.
+ * Derived serializers inherit from either Encoder or Decoder.
+ * Serializers can be used as functors or static_visitors.
+ */
+template <class Derived> class Serializer {
+ public:
+ /** Temporarily set a lower relative limit on the serializer */
+ class ScopedLimit {
+ public:
+ ScopedLimit(Serializer& s, size_t l)
+ : serializer(s), save(serializer.setLimit(l)) {}
+
+ ~ScopedLimit() { serializer.setAbsLimit(save); }
+
+ private:
+ Serializer& serializer;
+ size_t save;
+ };
+
+ static size_t maxLimit() { return std::numeric_limits<size_t>::max(); }
+
+ Serializer() : bytes(0), limit(maxLimit()) {}
+
+ typedef Derived& result_type; // unary functor requirement.
+
+ /** Wrapper functor to pass serializer functors by reference. */
+ template <class S> struct Ref {
+ typedef typename S::result_type result_type;
+ S& s;
+ Ref(S& ss) : s(ss) {}
+ template <class T> result_type operator()(T& x) { return s(x); }
+ template <class T> result_type operator()(const T& x) { return s(x); }
+ };
+
+ /** Reference wrapper to pass serializers by reference,
+ * e.g. to std:: functions that take functors.
+ */
+ template <class S> static Ref<S> ref(S& s) { return Ref<S>(s); }
+
+ /** Generic rule to serialize an iterator range */
+ template <class Iter> Derived& operator()(Iter begin, Iter end) {
+ std::for_each(begin, end, ref(this->self()));
+ return self();
+ }
+
+ /** Set limit relative to current position.
+ * @return old absolute limit.
+ */
+ size_t setLimit(size_t n) {
+ size_t l=limit;
+ limit = bytes+n;
+ return l;
+ }
+
+ /** Get the max number of bytes that can be processed under the
+ * current limit.
+ */
+ size_t bytesRemaining() const {
+ return limit - bytes;
+ }
+ /** Set absolute limit. */
+ void setAbsLimit(size_t n) {
+ limit = n;
+ if (bytes > limit)
+ throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception.
+ }
+
+ protected:
+ Derived& self() { return *static_cast<Derived*>(this); }
+ void addBytes(size_t n) {
+ size_t newBytes=bytes+n;
+ if (newBytes > limit)
+ throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception.
+ bytes = newBytes;
+ }
+
+ private:
+ void checkLimit() {
+ }
+
+ size_t bytes; // how many bytes serialized.
+ size_t limit; // bytes may not exceed this limit.
+};
+
+/**
+ * Base class for encoders, provides generic encode functions.
+ *
+ * A derived encoder must provide operator(const T&) to encode all
+ * primitive types T.
+ */
+template <class Derived> class EncoderBase : public Serializer<Derived> {
+ public:
+ using Serializer<Derived>::operator();
+ using Serializer<Derived>::self;
+
+ /** Default op() for non-primitive types. */
+ template <class T> Derived& operator()(const T& t) {
+ serializable(const_cast<T&>(t)).serialize(self()); return self();
+ }
+
+ /** Split serialize() into encode()/decode() */
+ template <class T> Derived& split(const T& t) {
+ t.encode(self()); return self();
+ }
+};
+
+/**
+ * Base class for decoders, provides generic decode functions.
+ *
+ * A derived encoder must provide operator(T&) to encode all
+ * primitive types T.
+ */
+template <class Derived> class DecoderBase : public Serializer<Derived> {
+ public:
+ using Serializer<Derived>::operator();
+ using Serializer<Derived>::self;
+
+ /** Default op() for non-primitive types. */
+ template <class T> Derived& operator()(T& t) {
+
+ serializable(t).serialize(self()); return self();
+ }
+
+ /** Split serialize() into encode()/decode() */
+ template <class T> Derived& split(T& t) {
+ t.decode(self()); return self();
+ }
+};
+
+/** Serialize a type by converting it to/from another type.
+ * To serialize type Foo by converting to/from type Bar create
+ * a serializable() overload like this:
+ *
+ * SerializeAs<Foo,Bar> serializable(Foo& t) { return SerializeAs<Foo,Bar>(t); }
+ */
+template <class Type, class AsType>
+struct SerializeAs {
+ Type& value;
+ SerializeAs(Type & t) : value(t) {}
+ template <class S> void serialize(S& s) { s.split(*this); }
+ template <class S> void encode(S& s) const { s(AsType(value)); }
+ template <class S> void decode(S& s) { AsType x; s(x); value=Type(x); }
+};
+
+} // namespace qpid
+
+#endif /*!QPID_SERIALIZER_H*/
diff --git a/qpid/cpp/src/qpid/SessionId.cpp b/qpid/cpp/src/qpid/SessionId.cpp
new file mode 100644
index 0000000000..c7e83f83d7
--- /dev/null
+++ b/qpid/cpp/src/qpid/SessionId.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/SessionId.h"
+#include <sstream>
+
+namespace qpid {
+
+SessionId::SessionId(const std::string& u, const std::string& n) : userId(u), name(n) {}
+
+bool SessionId::operator<(const SessionId& id) const {
+ return userId < id.userId || (userId == id.userId && name < id.name);
+}
+
+bool SessionId::operator==(const SessionId& id) const {
+ return id.name == name && id.userId == userId;
+}
+
+std::ostream& operator<<(std::ostream& o, const SessionId& id) {
+ return o << id.getUserId() << "." << id.getName();
+}
+
+std::string SessionId::str() const {
+ std::ostringstream o;
+ o << *this;
+ return o.str();
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/SessionState.cpp b/qpid/cpp/src/qpid/SessionState.cpp
new file mode 100644
index 0000000000..e5019604d2
--- /dev/null
+++ b/qpid/cpp/src/qpid/SessionState.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/SessionState.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <numeric>
+
+namespace qpid {
+using framing::AMQFrame;
+using framing::NotImplementedException;
+using framing::InvalidArgumentException;
+using framing::IllegalStateException;
+using framing::ResourceLimitExceededException;
+using framing::InternalErrorException;
+using framing::FramingErrorException;
+
+namespace {
+bool isControl(const AMQFrame& f) {
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL;
+}
+bool isCommand(const AMQFrame& f) {
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND;
+}
+} // namespace
+
+SessionPoint::SessionPoint(SequenceNumber c, uint64_t o) : command(c), offset(o) {}
+
+// TODO aconway 2008-05-22: Do complete frame sequence validity check here,
+// currently duplicated betwen broker and client session impl.
+//
+void SessionPoint::advance(const AMQFrame& f) {
+ if (isControl(f)) return; // Ignore controls.
+ if (f.isFirstSegment() && f.isFirstFrame()) {
+ if (offset != 0)
+ throw FramingErrorException(QPID_MSG("Unexpected command start frame."));
+ if (!isCommand(f))
+ throw FramingErrorException(
+ QPID_MSG("Command start frame has invalid type" << f.getBody()->type()));
+ if (f.isLastSegment() && f.isLastFrame())
+ ++command; // Single-frame command.
+ else
+ offset += f.encodedSize();
+ }
+ else { // continuation frame for partial command
+ if (offset == 0)
+ throw FramingErrorException(QPID_MSG("Unexpected command continuation frame."));
+ if (f.isLastSegment() && f.isLastFrame()) {
+ ++command;
+ offset = 0;
+ }
+ else {
+ // TODO aconway 2008-04-24: if we go to support for partial
+ // command replay, then it may be better to record the unframed
+ // data size in a command point rather than the framed size so
+ // that the relationship of fragment offsets to the replay
+ // list can be computed more easily.
+ //
+ offset += f.encodedSize();
+ }
+ }
+}
+
+bool SessionPoint::operator<(const SessionPoint& x) const {
+ return command < x.command || (command == x.command && offset < x.offset);
+}
+
+bool SessionPoint::operator==(const SessionPoint& x) const {
+ return command == x.command && offset == x.offset;
+}
+
+
+SessionState::SendState::SendState() : unflushedSize(), replaySize(), bytesSinceKnownCompleted() {}
+
+SessionState::ReceiveState::ReceiveState() : bytesSinceKnownCompleted() {}
+
+uint32_t SessionState::getTimeout() const { return timeout; }
+void SessionState::setTimeout(uint32_t seconds) { timeout = seconds; }
+
+SessionPoint SessionState::senderGetCommandPoint() { return sender.sendPoint; }
+SequenceSet SessionState::senderGetIncomplete() const { return sender.incomplete; }
+SessionPoint SessionState::senderGetReplayPoint() const { return sender.replayPoint; }
+
+SessionState::ReplayRange SessionState::senderExpected(const SessionPoint& expect) {
+ if (expect < sender.replayPoint || sender.sendPoint < expect)
+ throw InvalidArgumentException(QPID_MSG(getId() << ": expected command-point out of range."));
+ QPID_LOG(debug, getId() << ": sender expected point moved to " << expect);
+ ReplayList::iterator i = sender.replayList.begin();
+ SessionPoint p = sender.replayPoint;
+ while (i != sender.replayList.end() && p.command < expect.command)
+ p.advance(*i++);
+ assert(p.command == expect.command);
+ return boost::make_iterator_range(i, sender.replayList.end());
+}
+
+void SessionState::senderRecord(const AMQFrame& f) {
+ if (isControl(f)) return; // Ignore control frames.
+ QPID_LOG(trace, getId() << ": sent cmd " << sender.sendPoint.command << ": " << *f.getBody());
+
+ stateful = true;
+ if (timeout) sender.replayList.push_back(f);
+ sender.unflushedSize += f.encodedSize();
+ sender.bytesSinceKnownCompleted += f.encodedSize();
+ sender.replaySize += f.encodedSize();
+ sender.incomplete += sender.sendPoint.command;
+ sender.sendPoint.advance(f);
+ if (config.replayHardLimit && config.replayHardLimit < sender.replaySize)
+ throw ResourceLimitExceededException("Replay buffer exceeeded hard limit");
+}
+
+static const uint32_t SPONTANEOUS_REQUEST_INTERVAL = 65536;
+
+bool SessionState::senderNeedFlush() const {
+ return (sender.sendPoint.command % SPONTANEOUS_REQUEST_INTERVAL == 0) ||
+ (config.replayFlushLimit && sender.unflushedSize >= config.replayFlushLimit);
+}
+
+void SessionState::senderRecordFlush() {
+ sender.flushPoint = sender.sendPoint;
+ sender.unflushedSize = 0;
+}
+
+bool SessionState::senderNeedKnownCompleted() const {
+ return config.replayFlushLimit && sender.bytesSinceKnownCompleted >= config.replayFlushLimit;
+}
+
+void SessionState::senderRecordKnownCompleted() {
+ sender.bytesSinceKnownCompleted = 0;
+}
+
+void SessionState::senderConfirmed(const SessionPoint& confirmed) {
+ if (confirmed > sender.sendPoint)
+ throw InvalidArgumentException(QPID_MSG(getId() << ": confirmed < " << confirmed << " but only sent < " << sender.sendPoint));
+ QPID_LOG(debug, getId() << ": sender confirmed point moved to " << confirmed);
+ ReplayList::iterator i = sender.replayList.begin();
+ while (i != sender.replayList.end() && sender.replayPoint.command < confirmed.command) {
+ sender.replayPoint.advance(*i);
+ assert(sender.replayPoint <= sender.sendPoint);
+ sender.replaySize -= i->encodedSize();
+ if (sender.replayPoint > sender.flushPoint)
+ sender.unflushedSize -= i->encodedSize();
+ ++i;
+ }
+ if (sender.replayPoint > sender.flushPoint)
+ sender.flushPoint = sender.replayPoint;
+ sender.replayList.erase(sender.replayList.begin(), i);
+ assert(sender.replayPoint.offset == 0);
+}
+
+void SessionState::senderCompleted(const SequenceSet& commands) {
+ if (commands.empty()) return;
+ QPID_LOG(debug, getId() << ": sender marked completed: " << commands);
+ sender.incomplete -= commands;
+ // Completion implies confirmation but we don't handle out-of-order
+ // confirmation, so confirm up to the end of the first contiguous range of commands.
+ senderConfirmed(SessionPoint(commands.rangesBegin()->end()));
+}
+
+void SessionState::receiverSetCommandPoint(const SessionPoint& point) {
+ if (hasState() && point > receiver.received)
+ throw InvalidArgumentException(QPID_MSG(getId() << ": Command-point out of range."));
+ QPID_LOG(debug, getId() << ": receiver command-point set to: " << point);
+ receiver.expected = point;
+ if (receiver.expected > receiver.received)
+ receiver.received = receiver.expected;
+}
+
+bool SessionState::receiverRecord(const AMQFrame& f) {
+ if (receiverTrackingDisabled) return true; //Very nasty hack for push bridges
+ if (isControl(f)) return true; // Ignore control frames.
+ stateful = true;
+ receiver.expected.advance(f);
+ receiver.bytesSinceKnownCompleted += f.encodedSize();
+ bool firstTime = receiver.expected > receiver.received;
+ if (firstTime) {
+ receiver.received = receiver.expected;
+ receiver.incomplete += receiverGetCurrent();
+ }
+ QPID_LOG(trace, getId() << ": recv cmd " << receiverGetCurrent() << ": " << *f.getBody());
+ if (!firstTime) QPID_LOG(trace, "Ignoring duplicate frame.");
+ return firstTime;
+}
+
+void SessionState::receiverCompleted(SequenceNumber command, bool cumulative) {
+ if (receiverTrackingDisabled) return; //Very nasty hack for push bridges
+ assert(receiver.incomplete.contains(command)); // Internal error to complete command twice.
+ SequenceNumber first =cumulative ? receiver.incomplete.front() : command;
+ SequenceNumber last = command;
+ receiver.unknownCompleted.add(first, last);
+ receiver.incomplete.remove(first, last);
+ QPID_LOG(debug, getId() << ": receiver marked completed: " << command
+ << " incomplete: " << receiver.incomplete
+ << " unknown-completed: " << receiver.unknownCompleted);
+}
+
+void SessionState::receiverKnownCompleted(const SequenceSet& commands) {
+ if (!commands.empty() && commands.back() > receiver.received.command)
+ throw InvalidArgumentException(QPID_MSG(getId() << ": Known-completed has invalid commands."));
+ receiver.bytesSinceKnownCompleted=0;
+ receiver.unknownCompleted -= commands;
+ QPID_LOG(debug, getId() << ": receiver known completed: " << commands << " unknown: " << receiver.unknownCompleted);
+}
+
+bool SessionState::receiverNeedKnownCompleted() const {
+ return (receiver.expected.command % SPONTANEOUS_REQUEST_INTERVAL == 0) ||
+ (config.replayFlushLimit && receiver.bytesSinceKnownCompleted >= config.replayFlushLimit);
+}
+
+const SessionPoint& SessionState::receiverGetExpected() const { return receiver.expected; }
+const SessionPoint& SessionState::receiverGetReceived() const { return receiver.received; }
+const SequenceSet& SessionState::receiverGetUnknownComplete() const { return receiver.unknownCompleted; }
+const SequenceSet& SessionState::receiverGetIncomplete() const { return receiver.incomplete; }
+
+SequenceNumber SessionState::receiverGetCurrent() const {
+ SequenceNumber current = receiver.expected.command;
+ if (receiver.expected.offset == 0)
+ --current;
+ return current;
+}
+
+SessionState::Configuration::Configuration(size_t flush, size_t hard) :
+ replayFlushLimit(flush), replayHardLimit(hard) {}
+
+SessionState::SessionState(const SessionId& i, const Configuration& c)
+ : id(i), timeout(0), config(c), stateful(false), receiverTrackingDisabled(false)
+{
+ QPID_LOG(debug, "SessionState::SessionState " << id << ": " << this);
+}
+
+bool SessionState::hasState() const { return stateful; }
+
+SessionState::~SessionState() {}
+
+std::ostream& operator<<(std::ostream& o, const SessionPoint& p) {
+ return o << "(" << p.command.getValue() << "+" << p.offset << ")";
+}
+
+void SessionState::setState(
+ const SequenceNumber& replayStart,
+ const SequenceNumber& sendCommandPoint,
+ const SequenceSet& sentIncomplete,
+ const SequenceNumber& expected,
+ const SequenceNumber& received,
+ const SequenceSet& unknownCompleted,
+ const SequenceSet& receivedIncomplete
+)
+{
+ sender.replayPoint = replayStart;
+ sender.flushPoint = sendCommandPoint;
+ sender.sendPoint = sendCommandPoint;
+ sender.unflushedSize = 0;
+ sender.replaySize = 0; // Replay list will be updated separately.
+ sender.incomplete = sentIncomplete;
+ sender.bytesSinceKnownCompleted = 0;
+
+ receiver.expected = expected;
+ receiver.received = received;
+ receiver.unknownCompleted = unknownCompleted;
+ receiver.incomplete = receivedIncomplete;
+ receiver.bytesSinceKnownCompleted = 0;
+}
+
+void SessionState::disableReceiverTracking() { receiverTrackingDisabled = true; }
+void SessionState::enableReceiverTracking() { receiverTrackingDisabled = false; }
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/SessionState.h b/qpid/cpp/src/qpid/SessionState.h
new file mode 100644
index 0000000000..02853b1143
--- /dev/null
+++ b/qpid/cpp/src/qpid/SessionState.h
@@ -0,0 +1,235 @@
+#ifndef QPID_SESSIONSTATE_H
+#define QPID_SESSIONSTATE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/SessionId.h>
+#include <qpid/framing/SequenceNumber.h>
+#include <qpid/framing/SequenceSet.h>
+#include <qpid/framing/AMQFrame.h>
+#include <qpid/framing/FrameHandler.h>
+#include <boost/operators.hpp>
+#include <boost/range/iterator_range.hpp>
+#include <vector>
+#include <iosfwd>
+#include <qpid/CommonImportExport.h>
+
+namespace qpid {
+using framing::SequenceNumber;
+using framing::SequenceSet;
+
+/** A point in the session. Points to command id + offset */
+struct SessionPoint : boost::totally_ordered1<SessionPoint> {
+ QPID_COMMON_EXTERN SessionPoint(SequenceNumber command = 0, uint64_t offset = 0);
+
+ SequenceNumber command;
+ uint64_t offset;
+
+ /** Advance past frame f */
+ QPID_COMMON_EXTERN void advance(const framing::AMQFrame& f);
+
+ QPID_COMMON_EXTERN bool operator<(const SessionPoint&) const;
+ QPID_COMMON_EXTERN bool operator==(const SessionPoint&) const;
+};
+
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const SessionPoint&);
+
+/**
+ * Support for session idempotence barrier and resume as defined in
+ * AMQP 0-10.
+ *
+ * We only issue/use contiguous confirmations, out-of-order confirmation
+ * is ignored. Out of order completion is fully supported.
+ *
+ * Raises NotImplemented if the command point is set greater than the
+ * max currently received command data, either explicitly via
+ * session.command-point or implicitly via session.gap.
+ *
+ * Partial replay is not supported, replay always begins on a command
+ * boundary, and we never confirm partial commands.
+ *
+ * The SessionPoint data structure does store offsets so this class
+ * could be extended to support partial replay without
+ * source-incompatbile API changes.
+ */
+class SessionState {
+ typedef std::vector<framing::AMQFrame> ReplayList;
+
+ public:
+
+ typedef boost::iterator_range<ReplayList::iterator> ReplayRange;
+
+ struct Configuration {
+ QPID_COMMON_EXTERN Configuration(size_t flush=1024*1024, size_t hard=0);
+ size_t replayFlushLimit; // Flush when the replay list >= N bytes. 0 disables.
+ size_t replayHardLimit; // Kill session if replay list > N bytes. 0 disables.
+ };
+
+ QPID_COMMON_EXTERN SessionState(const SessionId& =SessionId(), const Configuration& =Configuration());
+
+ QPID_COMMON_EXTERN virtual ~SessionState();
+
+ bool hasState() const;
+
+ const SessionId& getId() const { return id; }
+
+ QPID_COMMON_EXTERN virtual uint32_t getTimeout() const;
+ QPID_COMMON_EXTERN virtual void setTimeout(uint32_t seconds);
+
+ bool operator==(const SessionId& other) const { return id == other; }
+ bool operator==(const SessionState& other) const { return id == other.id; }
+
+ // ==== Functions for sender state.
+
+ /** Record frame f for replay. Should not be called during replay. */
+ QPID_COMMON_EXTERN virtual void senderRecord(const framing::AMQFrame& f);
+
+ /** @return true if we should send flush for confirmed and completed commands. */
+ QPID_COMMON_EXTERN virtual bool senderNeedFlush() const;
+
+ /** Called when flush for confirmed and completed commands is sent to peer. */
+ QPID_COMMON_EXTERN virtual void senderRecordFlush();
+
+ /** True if we should reply to the next incoming completed command */
+ QPID_COMMON_EXTERN virtual bool senderNeedKnownCompleted() const;
+
+ /** Called when knownCompleted is sent to peer. */
+ QPID_COMMON_EXTERN virtual void senderRecordKnownCompleted();
+
+ /** Called when the peer confirms up to comfirmed. */
+ QPID_COMMON_EXTERN virtual void senderConfirmed(const SessionPoint& confirmed);
+
+ /** Called when the peer indicates commands completed */
+ QPID_COMMON_EXTERN virtual void senderCompleted(const SequenceSet& commands);
+
+ /** Point from which the next new (not replayed) data will be sent. */
+ QPID_COMMON_EXTERN virtual SessionPoint senderGetCommandPoint();
+
+ /** Set of outstanding incomplete commands */
+ QPID_COMMON_EXTERN virtual SequenceSet senderGetIncomplete() const;
+
+ /** Point from which we can replay. */
+ QPID_COMMON_EXTERN virtual SessionPoint senderGetReplayPoint() const;
+
+ /** Peer expecting commands from this point.
+ *@return Range of frames to be replayed.
+ */
+ QPID_COMMON_EXTERN virtual ReplayRange senderExpected(const SessionPoint& expected);
+
+ // ==== Functions for receiver state
+
+ /** Set the command point. */
+ QPID_COMMON_EXTERN virtual void receiverSetCommandPoint(const SessionPoint& point);
+
+ /** Returns true if frame should be be processed, false if it is a duplicate. */
+ QPID_COMMON_EXTERN virtual bool receiverRecord(const framing::AMQFrame& f);
+
+ /** Command completed locally */
+ QPID_COMMON_EXTERN virtual void receiverCompleted(SequenceNumber command, bool cumulative=false);
+
+ /** Peer has indicated commands are known completed */
+ QPID_COMMON_EXTERN virtual void receiverKnownCompleted(const SequenceSet& commands);
+
+ /** True if the next completed control should set the timely-reply argument
+ * to request a knonw-completed response.
+ */
+ QPID_COMMON_EXTERN virtual bool receiverNeedKnownCompleted() const;
+
+ /** Get the incoming command point */
+ QPID_COMMON_EXTERN virtual const SessionPoint& receiverGetExpected() const;
+
+ /** Get the received high-water-mark, may be > getExpected() during replay */
+ QPID_COMMON_EXTERN virtual const SessionPoint& receiverGetReceived() const;
+
+ /** Completed received commands that the peer may not know about. */
+ QPID_COMMON_EXTERN virtual const SequenceSet& receiverGetUnknownComplete() const;
+
+ /** Incomplete received commands. */
+ QPID_COMMON_EXTERN virtual const SequenceSet& receiverGetIncomplete() const;
+
+ /** ID of the command currently being handled. */
+ QPID_COMMON_EXTERN virtual SequenceNumber receiverGetCurrent() const;
+
+ /** Set the state variables, used to create a session that will resume
+ * from some previously established point.
+ */
+ QPID_COMMON_EXTERN virtual void setState(
+ const SequenceNumber& replayStart,
+ const SequenceNumber& sendCommandPoint,
+ const SequenceSet& sentIncomplete,
+ const SequenceNumber& expected,
+ const SequenceNumber& received,
+ const SequenceSet& unknownCompleted,
+ const SequenceSet& receivedIncomplete
+ );
+
+ /**
+ * So called 'push' bridges work by faking a subscribe request
+ * (and the accompanying flows etc) to the local broker to initiate
+ * the outflow of messages for the bridge.
+ *
+ * As the peer doesn't send these it cannot include them in its
+ * session state. To keep the session state on either side of the
+ * bridge in sync, this hack allows the tracking of state for
+ * received messages to be disabled for the faked commands and
+ * subsequently re-enabled.
+ */
+ QPID_COMMON_EXTERN void disableReceiverTracking();
+ QPID_COMMON_EXTERN void enableReceiverTracking();
+
+ private:
+
+ struct SendState {
+ SendState();
+ // invariant: replayPoint <= flushPoint <= sendPoint
+ SessionPoint replayPoint; // Can replay from this point
+ SessionPoint flushPoint; // Point of last flush
+ SessionPoint sendPoint; // Send from this point
+ ReplayList replayList; // Starts from replayPoint.
+ size_t unflushedSize; // Un-flushed bytes in replay list.
+ size_t replaySize; // Total bytes in replay list.
+ SequenceSet incomplete; // Commands sent and not yet completed.
+ size_t bytesSinceKnownCompleted; // Bytes sent since we last issued a knownCompleted.
+ } sender;
+
+ struct ReceiveState {
+ ReceiveState();
+ SessionPoint expected; // Expected from here
+ SessionPoint received; // Received to here. Invariant: expected <= received.
+ SequenceSet unknownCompleted; // Received & completed, may not not known-complete by peer.
+ SequenceSet incomplete; // Incomplete received commands.
+ size_t bytesSinceKnownCompleted; // Bytes sent since we last issued a knownCompleted.
+ } receiver;
+
+ SessionId id;
+ uint32_t timeout;
+ Configuration config;
+ bool stateful;
+ bool receiverTrackingDisabled;//very nasty hack for 'push' bridges
+};
+
+inline bool operator==(const SessionId& id, const SessionState& s) { return s == id; }
+
+} // namespace qpid
+
+
+#endif /*!QPID_SESSIONSTATE_H*/
diff --git a/qpid/cpp/src/qpid/SharedObject.h b/qpid/cpp/src/qpid/SharedObject.h
new file mode 100644
index 0000000000..852a036ab9
--- /dev/null
+++ b/qpid/cpp/src/qpid/SharedObject.h
@@ -0,0 +1,55 @@
+#ifndef _SharedObject_
+#define _SharedObject_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+ /**
+ * Template to enforce shared object conventions.
+ * Shared object classes should inherit : public qpid::SharedObject
+ * That ensures Foo:
+ * - has typedef boost::shared_ptr<T> shared_ptr
+ * - has virtual destructor
+ * - is boost::noncopyable (no default copy or assign)
+ * - has a protected default constructor.
+ *
+ * Shared objects should not have public constructors.
+ * Make constructors protected and provide public statc create()
+ * functions that return a shared_ptr.
+ */
+ template <class T>
+ class SharedObject : private boost::noncopyable
+ {
+ public:
+ typedef boost::shared_ptr<T> shared_ptr;
+
+ virtual ~SharedObject() {};
+
+ protected:
+ SharedObject() {}
+ };
+}
+
+#endif /*!_SharedObject_*/
diff --git a/qpid/cpp/src/qpid/StringUtils.cpp b/qpid/cpp/src/qpid/StringUtils.cpp
new file mode 100644
index 0000000000..c436441c56
--- /dev/null
+++ b/qpid/cpp/src/qpid/StringUtils.cpp
@@ -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.
+ *
+ */
+#include "qpid/StringUtils.h"
+
+namespace qpid {
+
+using std::string;
+using std::vector;
+
+void split(vector<string>& out, const string& in, const string& delims)
+{
+ string::size_type start = in.find_first_not_of(delims);
+ if (start == string::npos) return;
+
+ string::size_type end = in.find_first_of(delims, start);
+ while (end != string::npos) {
+ out.push_back(in.substr(start, end - start));
+ start = in.find_first_not_of(delims, end);
+ if (start == string::npos) return;
+ end = in.find_first_of(delims, start);
+ }
+ out.push_back(in.substr(start));
+}
+
+vector<string> split(const string& in, const string& delims)
+{
+ vector<string> out;
+ split(out, in, delims);
+ return out;
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/StringUtils.h b/qpid/cpp/src/qpid/StringUtils.h
new file mode 100644
index 0000000000..4130fae017
--- /dev/null
+++ b/qpid/cpp/src/qpid/StringUtils.h
@@ -0,0 +1,45 @@
+#ifndef QPID_STRINGUTILS_H
+#define QPID_STRINGUTILS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/CommonImportExport.h"
+
+#include <string>
+#include <vector>
+
+namespace qpid {
+
+/**
+ * Split 'in' into words using delimiters in 'delims' and put
+ * resulting strings into 'out' vector.
+ */
+QPID_COMMON_EXTERN void split(std::vector<std::string>& out, const std::string& in, const std::string& delims);
+/**
+ * Split 'in' into words using delimiters in 'delims' and return the
+ * resulting strings in a vector.
+ */
+QPID_COMMON_EXTERN std::vector<std::string> split(const std::string& in, const std::string& delims);
+
+} // namespace qpid
+
+#endif /*!QPID_STRINGUTILS_H*/
diff --git a/qpid/cpp/src/qpid/Url.cpp b/qpid/cpp/src/qpid/Url.cpp
new file mode 100644
index 0000000000..ab796f4642
--- /dev/null
+++ b/qpid/cpp/src/qpid/Url.cpp
@@ -0,0 +1,265 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/Url.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/StrError.h"
+#include "qpid/client/Connector.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/lexical_cast.hpp>
+
+#include <algorithm>
+#include <vector>
+#include <string>
+
+#include <string.h>
+
+using namespace std;
+using boost::lexical_cast;
+
+namespace qpid {
+
+class ProtocolTags {
+ public:
+ bool find(const string& tag) {
+ sys::Mutex::ScopedLock l(lock);
+ return std::find(tags.begin(), tags.end(), tag) != tags.end();
+ }
+
+ void add(const string& tag) {
+ sys::Mutex::ScopedLock l(lock);
+ if (std::find(tags.begin(), tags.end(), tag) == tags.end())
+ tags.push_back(tag);
+ }
+
+ static ProtocolTags& instance() {
+ /** First call must be made while program is still single threaded.
+ * This will be the case since tags are registered in static initializers.
+ */
+ static ProtocolTags tags;
+ return tags;
+ }
+
+ private:
+ sys::Mutex lock;
+ vector<string> tags;
+};
+
+Url::Invalid::Invalid(const string& s) : Exception(s) {}
+
+Url Url::getHostNameUrl(uint16_t port) {
+ Address address("tcp", std::string(), port);
+ if (!sys::SystemInfo::getLocalHostname(address))
+ throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
+ return Url(address);
+}
+
+Url Url::getIpAddressesUrl(uint16_t port) {
+ Url url;
+ sys::SystemInfo::getLocalIpAddresses(port, url);
+ return url;
+}
+
+string Url::str() const {
+ if (cache.empty() && !this->empty()) {
+ ostringstream os;
+ os << *this;
+ cache = os.str();
+ }
+ return cache;
+}
+
+ostream& operator<<(ostream& os, const Url& url) {
+ os << "amqp:";
+ if (!url.getUser().empty()) os << url.getUser();
+ if (!url.getPass().empty()) os << "/" << url.getPass();
+ if (!(url.getUser().empty() && url.getPass().empty())) os << "@";
+ Url::const_iterator i = url.begin();
+ if (i!=url.end()) {
+ os << *i++;
+ while (i != url.end())
+ os << "," << *i++;
+ }
+ return os;
+}
+
+static const std::string TCP = "tcp";
+
+/** Simple recursive-descent parser for this grammar:
+url = ["amqp:"][ user ["/" password] "@" ] protocol_addr *("," protocol_addr)
+protocol_addr = tcp_addr / rmda_addr / ssl_addr / .. others plug-in
+tcp_addr = ["tcp:"] host [":" port]
+rdma_addr = "rdma:" host [":" port]
+ssl_addr = "ssl:" host [":" port]
+*/
+class UrlParser {
+ public:
+ UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {}
+ bool parse() {
+ literal("amqp:"); // Optional
+ userPass(); // Optional
+ return list(&UrlParser::protocolAddr, &UrlParser::comma) && i == end;
+ }
+
+ private:
+ typedef bool (UrlParser::*Rule)();
+
+ bool userPass() {
+ const char* at = std::find(i, end, '@');
+ if (at == end) return false;
+ const char* slash = std::find(i, at, '/');
+ url.setUser(string(i, slash));
+ const char* pass = (slash == at) ? slash : slash+1;
+ url.setPass(string(pass, at));
+ i = at+1;
+ return true;
+ }
+
+ bool comma() { return literal(","); }
+
+ bool protocolAddr() {
+ Address addr(Address::TCP, "", Address::AMQP_PORT); // Set up defaults
+ protocolTag(addr.protocol); // Optional
+ bool ok = (host(addr.host) &&
+ (literal(":") ? port(addr.port) : true));
+ if (ok) url.push_back(addr);
+ return ok;
+ }
+
+ bool protocolTag(string& result) {
+ const char* j = std::find(i,end,':');
+ if (j != end) {
+ string tag(i,j);
+ if (ProtocolTags::instance().find(tag)) {
+ i = j+1;
+ result = tag;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // TODO aconway 2008-11-20: this does not fully implement
+ // http://www.ietf.org/rfc/rfc3986.txt. Works for DNS names and
+ // ipv4 literals but won't handle ipv6.
+ //
+ bool host(string& h) {
+ const char* start=i;
+ while (unreserved() || pctEncoded())
+ ;
+ if (start == i) return false;//host is required
+ else h.assign(start, i);
+ return true;
+ }
+
+ bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); }
+
+ bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); }
+
+ bool hexDigit() { return i < end && ::strchr("01234567890abcdefABCDEF", *i) && advance(); }
+
+ bool port(uint16_t& p) { return decimalInt(p); }
+
+ template <class IntType> bool decimalInt(IntType& n) {
+ const char* start = i;
+ while (decDigit())
+ ;
+ try {
+ n = lexical_cast<IntType>(string(start, i));
+ return true;
+ } catch(...) { return false; }
+ }
+
+ bool decDigit() { return i < end && ::isdigit(*i) && advance(); }
+
+ bool literal(const char* s) {
+ int n = ::strlen(s);
+ if (n <= end-i && equal(s, s+n, i)) return advance(n);
+ return false;
+ };
+
+ bool noop() { return true; }
+
+ /** List of item, separated by separator, with min & max bounds. */
+ bool list(Rule item, Rule separator, size_t min=0, size_t max=UNLIMITED) {
+ assert(max > 0);
+ assert(max >= min);
+ if (!(this->*item)()) return min == 0; // Empty list.
+ size_t n = 1;
+ while (n < max && i < end) {
+ if (!(this->*separator)()) break;
+ if (i == end || !(this->*item)()) return false; // Separator with no item.
+ ++n;
+ }
+ return n >= min;
+ }
+
+ /** List of items with no separator */
+ bool list(Rule item, size_t min=0, size_t max=UNLIMITED) { return list(item, &UrlParser::noop, min, max); }
+
+ bool advance(size_t n=1) {
+ if (i+n > end) return false;
+ i += n;
+ return true;
+ }
+
+ static const size_t UNLIMITED = size_t(~1);
+ static const std::string LOCALHOST;
+
+ Url& url;
+ const char* text;
+ const char* end;
+ const char* i;
+};
+
+const string UrlParser::LOCALHOST("127.0.0.1");
+
+void Url::parse(const char* url) {
+ parseNoThrow(url);
+ if (empty())
+ throw Url::Invalid(QPID_MSG("Invalid URL: " << url));
+}
+
+void Url::parseNoThrow(const char* url) {
+ cache.clear();
+ if (!UrlParser(*this, url).parse())
+ clear();
+}
+
+void Url::throwIfEmpty() const {
+ if (empty())
+ throw Url::Invalid("URL contains no addresses");
+}
+
+std::string Url::getUser() const { return user; }
+std::string Url::getPass() const { return pass; }
+void Url::setUser(const std::string& s) { user = s; }
+void Url::setPass(const std::string& s) { pass = s; }
+
+std::istream& operator>>(std::istream& is, Url& url) {
+ std::string s;
+ is >> s;
+ url.parse(s);
+ return is;
+}
+
+void Url::addProtocol(const std::string& tag) { ProtocolTags::instance().add(tag); }
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/Version.h b/qpid/cpp/src/qpid/Version.h
new file mode 100755
index 0000000000..b4805a3757
--- /dev/null
+++ b/qpid/cpp/src/qpid/Version.h
@@ -0,0 +1,40 @@
+#ifndef QPID_VERSION_H
+#define QPID_VERSION_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <string>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#else
+# error "config.h not generated"
+#endif
+
+namespace qpid {
+ const std::string product = PACKAGE_NAME;
+ const std::string version = PACKAGE_VERSION;
+# if HAVE_SASL
+ const std::string saslName = BROKER_SASL_NAME;
+# else
+ const std::string saslName = "qpidd-no-sasl";
+# endif
+}
+
+#endif /*!QPID_VERSION_H*/
diff --git a/qpid/cpp/src/qpid/acl/Acl.cpp b/qpid/cpp/src/qpid/acl/Acl.cpp
new file mode 100644
index 0000000000..4b3dda7962
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/Acl.cpp
@@ -0,0 +1,191 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/Acl.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/acl/AclValidator.h"
+#include "qpid/sys/Mutex.h"
+
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/log/Logger.h"
+#include "qpid/types/Variant.h"
+#include "qmf/org/apache/qpid/acl/Package.h"
+#include "qmf/org/apache/qpid/acl/EventAllow.h"
+#include "qmf/org/apache/qpid/acl/EventDeny.h"
+#include "qmf/org/apache/qpid/acl/EventFileLoaded.h"
+#include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h"
+
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility/in_place_factory.hpp>
+
+using namespace std;
+using namespace qpid::acl;
+using qpid::broker::Broker;
+using namespace qpid::sys;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+namespace _qmf = qmf::org::apache::qpid::acl;
+
+Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0)
+{
+
+ agent = broker->getManagementAgent();
+
+ if (agent != 0){
+ _qmf::Package packageInit(agent);
+ mgmtObject = new _qmf::Acl (agent, this, broker);
+ agent->addObject (mgmtObject);
+ }
+ std::string errorString;
+ if (!readAclFile(errorString)){
+ throw Exception("Could not read ACL file " + errorString);
+ if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0);
+ }
+ QPID_LOG(info, "ACL Plugin loaded");
+ if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1);
+}
+
+ bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params)
+ {
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ // add real ACL check here...
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,name,params);
+
+
+ return result(aclreslt, id, action, objType, name);
+ }
+
+ bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey)
+ {
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ // only use dataLocal here...
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,ExchangeName,RoutingKey);
+
+ return result(aclreslt, id, action, objType, ExchangeName);
+ }
+
+
+ bool Acl::result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name)
+ {
+ switch (aclreslt)
+ {
+ case ALLOWLOG:
+ QPID_LOG(info, "ACL Allow id:" << id <<" action:" << AclHelper::getActionStr(action) <<
+ " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name );
+ agent->raiseEvent(_qmf::EventAllow(id, AclHelper::getActionStr(action),
+ AclHelper::getObjectTypeStr(objType),
+ name, types::Variant::Map()));
+ case ALLOW:
+ return true;
+ case DENY:
+ if (mgmtObject!=0) mgmtObject->inc_aclDenyCount();
+ return false;
+ case DENYLOG:
+ if (mgmtObject!=0) mgmtObject->inc_aclDenyCount();
+ default:
+ QPID_LOG(info, "ACL Deny id:" << id << " action:" << AclHelper::getActionStr(action) << " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name);
+ agent->raiseEvent(_qmf::EventDeny(id, AclHelper::getActionStr(action),
+ AclHelper::getObjectTypeStr(objType),
+ name, types::Variant::Map()));
+ return false;
+ }
+ return false;
+ }
+
+ bool Acl::readAclFile(std::string& errorText)
+ {
+ // only set transferAcl = true if a rule implies the use of ACL on transfer, else keep false for performance reasons.
+ return readAclFile(aclValues.aclFile, errorText);
+ }
+
+ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
+ boost::shared_ptr<AclData> d(new AclData);
+ AclReader ar;
+ if (ar.read(aclFile, d)){
+ agent->raiseEvent(_qmf::EventFileLoadFailed("", ar.getError()));
+ errorText = ar.getError();
+ QPID_LOG(error,ar.getError());
+ return false;
+ }
+
+ AclValidator validator;
+ validator.validate(d);
+
+ {
+ Mutex::ScopedLock locker(dataLock);
+ data = d;
+ }
+ transferAcl = data->transferAcl; // any transfer ACL
+
+ if (data->transferAcl){
+ QPID_LOG(debug,"Transfer ACL is Enabled!");
+ }
+
+ data->aclSource = aclFile;
+ if (mgmtObject!=0){
+ mgmtObject->set_transferAcl(transferAcl?1:0);
+ mgmtObject->set_policyFile(aclFile);
+ sys::AbsTime now = sys::AbsTime::now();
+ int64_t ns = sys::Duration(sys::EPOCH, now);
+ mgmtObject->set_lastAclLoad(ns);
+ agent->raiseEvent(_qmf::EventFileLoaded(""));
+ }
+ return true;
+ }
+
+ Acl::~Acl(){}
+
+ ManagementObject* Acl::GetManagementObject(void) const
+ {
+ return (ManagementObject*) mgmtObject;
+ }
+
+ Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, string& text)
+ {
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+ QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]");
+
+ switch (methodId)
+ {
+ case _qmf::Acl::METHOD_RELOADACLFILE :
+ readAclFile(text);
+ if (text.empty())
+ status = Manageable::STATUS_OK;
+ else
+ status = Manageable::STATUS_USER;
+ break;
+ }
+
+ return status;
+}
diff --git a/qpid/cpp/src/qpid/acl/Acl.h b/qpid/cpp/src/qpid/acl/Acl.h
new file mode 100644
index 0000000000..77f43838de
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/Acl.h
@@ -0,0 +1,86 @@
+#ifndef QPID_ACL_ACL_H
+#define QPID_ACL_ACL_H
+
+
+/*
+ *
+ * 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.
+ *
+ */
+
+
+
+#include "qpid/acl/AclReader.h"
+#include "qpid/RefCounted.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/acl/Acl.h"
+#include "qpid/sys/Mutex.h"
+
+#include <map>
+#include <string>
+
+
+namespace qpid {
+namespace broker {
+class Broker;
+}
+
+namespace acl {
+
+struct AclValues {
+ std::string aclFile;
+};
+
+
+class Acl : public broker::AclModule, public RefCounted, public management::Manageable
+{
+
+private:
+ acl::AclValues aclValues;
+ broker::Broker* broker;
+ bool transferAcl;
+ boost::shared_ptr<AclData> data;
+ qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
+ qpid::management::ManagementAgent* agent;
+ mutable qpid::sys::Mutex dataLock;
+
+public:
+ Acl (AclValues& av, broker::Broker& b);
+
+ void initialize();
+
+ inline virtual bool doTransferAcl() {return transferAcl;};
+
+ // create specilied authorise methods for cases that need faster matching as needed.
+ virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0);
+ virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName,const std::string& RoutingKey);
+
+ virtual ~Acl();
+private:
+ bool result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name);
+ bool readAclFile(std::string& errorText);
+ bool readAclFile(std::string& aclFile, std::string& errorText);
+ virtual qpid::management::ManagementObject* GetManagementObject(void) const;
+ virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+
+};
+
+
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACL_H
diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp
new file mode 100644
index 0000000000..658529b270
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclData.cpp
@@ -0,0 +1,261 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclData.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace acl {
+
+AclData::AclData():decisionMode(qpid::acl::DENY),transferAcl(false),aclSource("UNKNOWN")
+{
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){
+ actionList[cnt]=0;
+ }
+
+}
+
+void AclData::clear ()
+{
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){
+ if (actionList[cnt]){
+ for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++)
+ delete actionList[cnt][cnt1];
+ }
+ delete[] actionList[cnt];
+ }
+
+}
+
+bool AclData::matchProp(const std::string & src, const std::string& src1)
+{
+ // allow wildcard on the end of strings...
+ if (src.data()[src.size()-1]=='*') {
+ return (src.compare(0, src.size()-1, src1, 0,src.size()-1 ) == 0);
+ } else {
+ return (src.compare(src1)==0) ;
+ }
+}
+
+AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType,
+ const std::string& name, std::map<Property, std::string>* params) {
+
+ QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action)
+ << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name
+ << " with params " << AclHelper::propertyMapToString(params));
+
+ AclResult aclresult = decisionMode;
+ if (actionList[action] && actionList[action][objType]) {
+ AclData::actObjItr itrRule = actionList[action][objType]->find(id);
+ if (itrRule == actionList[action][objType]->end())
+ itrRule = actionList[action][objType]->find("*");
+
+ if (itrRule != actionList[action][objType]->end()) {
+
+ QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first );
+
+ //loop the vector
+ for (ruleSetItr i = itrRule->second.begin(); i < itrRule->second.end(); i++) {
+ QPID_LOG(debug, "ACL: checking rule " << i->toString());
+ // loop the names looking for match
+ bool match = true;
+ for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) {
+ //match name is exists first
+ if (pMItr->first == acl::PROP_NAME) {
+ if (matchProp(pMItr->second, name)){
+ QPID_LOG(debug, "ACL: name '" << name << "' matched with name '"
+ << pMItr->second << "' given in the rule");
+ }else{
+ match = false;
+ QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '"
+ << pMItr->second << "' given in the rule");
+ }
+ } else if (params) { //match pMItr against params
+ propertyMapItr paramItr = params->find(pMItr->first);
+ if (paramItr == params->end()) {
+ match = false;
+ QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '"
+ << AclHelper::getPropertyStr(pMItr->first) << "'");
+ }else if ( pMItr->first == acl::PROP_MAXQUEUECOUNT || pMItr->first == acl::PROP_MAXQUEUESIZE ) {
+ if ( pMItr->first == paramItr->first ) {
+
+ uint64_t aclMax = 0;
+ uint64_t paramMax = 0;
+
+ try{
+ aclMax = boost::lexical_cast<uint64_t>(pMItr->second);
+ }catch(const boost::bad_lexical_cast&){
+ match = false;
+ QPID_LOG(error,"Error evaluating rule. " <<
+ "Illegal value given in ACL source <" << aclSource <<
+ "> for property '" <<
+ AclHelper::getPropertyStr(pMItr->first) << "' : " <<
+ boost::lexical_cast<std::string>(pMItr->second));
+ break;
+ }
+
+ try{
+ paramMax = boost::lexical_cast<uint64_t>(paramItr->second);
+ }catch(const boost::bad_lexical_cast&){
+ match = false;
+ QPID_LOG(error,"Error evaluating rule. " <<
+ "Illegal value given in lookup for property '" <<
+ AclHelper::getPropertyStr(pMItr->first) << "' : " <<
+ boost::lexical_cast<std::string>(paramItr->second));
+ break;
+ }
+
+ QPID_LOG(debug, "ACL: Numeric comparison for property " <<
+ AclHelper::getPropertyStr(paramItr->first) <<
+ " (value given in lookup = " <<
+ boost::lexical_cast<std::string>(paramItr->second) <<
+ ", value give in rule = " <<
+ boost::lexical_cast<std::string>(pMItr->second) << " )");
+
+ if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)){
+ match = decisionMode == qpid::acl::ALLOW ;
+ QPID_LOG(debug, "ACL: Limit exceeded and match=" <<
+ (match ? "true": "false") <<
+ " as decision mode is " << AclHelper::getAclResultStr(decisionMode));
+ }
+ }
+ }else if (matchProp(pMItr->second, paramItr->second)) {
+ QPID_LOG(debug, "ACL: the pair("
+ << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second
+ << ") given in lookup matched the pair("
+ << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule");
+ } else {
+ QPID_LOG(debug, "ACL: the pair("
+ << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second
+ << ") given in lookup doesn't match the pair("
+ << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule");
+ match = false;
+ }
+ }
+ }
+ if (match)
+ {
+ aclresult = getACLResult(i->logOnly, i->log);
+ QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+ }
+ }
+ }
+ }
+
+ QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+}
+
+AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey)
+{
+
+ QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action)
+ << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " exchange name:" << name
+ << " with routing key " << RoutingKey);
+
+ AclResult aclresult = decisionMode;
+
+ if (actionList[action] && actionList[action][objType]){
+ AclData::actObjItr itrRule = actionList[action][objType]->find(id);
+
+ if (itrRule == actionList[action][objType]->end())
+ itrRule = actionList[action][objType]->find("*");
+
+ if (itrRule != actionList[action][objType]->end() ) {
+
+ QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first );
+
+ //loop the vector
+ for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) {
+ QPID_LOG(debug, "ACL: checking rule " << i->toString());
+
+ // loop the names looking for match
+ bool match =true;
+ for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++)
+ {
+ //match name is exists first
+ if (pMItr->first == acl::PROP_NAME){
+ if (matchProp(pMItr->second, name)){
+ QPID_LOG(debug, "ACL: name '" << name << "' matched with name '"
+ << pMItr->second << "' given in the rule");
+
+ }else{
+ match= false;
+ QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '"
+ << pMItr->second << "' given in the rule");
+ }
+ }else if (pMItr->first == acl::PROP_ROUTINGKEY){
+ if (matchProp(pMItr->second, RoutingKey)){
+ QPID_LOG(debug, "ACL: name '" << name << "' matched with routing_key '"
+ << pMItr->second << "' given in the rule");
+ }else{
+ match= false;
+ QPID_LOG(debug, "ACL: name '" << name << "' didn't match with routing_key '"
+ << pMItr->second << "' given in the rule");
+ }
+ }
+ }
+ if (match){
+ aclresult = getACLResult(i->logOnly, i->log);
+ QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+ }
+ }
+ }
+ }
+ QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+
+}
+
+
+AclResult AclData::getACLResult(bool logOnly, bool log)
+{
+ switch (decisionMode)
+ {
+ case qpid::acl::ALLOWLOG:
+ case qpid::acl::ALLOW:
+ if (logOnly) return qpid::acl::ALLOWLOG;
+ if (log)
+ return qpid::acl::DENYLOG;
+ else
+ return qpid::acl::DENY;
+
+
+ case qpid::acl::DENYLOG:
+ case qpid::acl::DENY:
+ if (logOnly) return qpid::acl::DENYLOG;
+ if (log)
+ return qpid::acl::ALLOWLOG;
+ else
+ return qpid::acl::ALLOW;
+ }
+
+ QPID_LOG(error, "ACL Decision Failed, setting DENY");
+ return qpid::acl::DENY;
+}
+
+AclData::~AclData()
+{
+ clear();
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/acl/AclData.h b/qpid/cpp/src/qpid/acl/AclData.h
new file mode 100644
index 0000000000..efd3b60145
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclData.h
@@ -0,0 +1,84 @@
+#ifndef QPID_ACL_ACLDATA_H
+#define QPID_ACL_ACLDATA_H
+
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/AclModule.h"
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+class AclData {
+
+
+public:
+
+ typedef std::map<qpid::acl::Property, std::string> propertyMap;
+ typedef propertyMap::const_iterator propertyMapItr;
+ struct rule {
+
+ bool log;
+ bool logOnly; // this is a rule is to log only
+
+ // key value map
+ //??
+ propertyMap props;
+
+
+ rule (propertyMap& p):log(false),logOnly(false),props(p) {};
+
+ std::string toString () const {
+ std::ostringstream ruleStr;
+ ruleStr << "[log=" << log << ", logOnly=" << logOnly << " props{";
+ for (propertyMapItr pMItr = props.begin(); pMItr != props.end(); pMItr++) {
+ ruleStr << " " << AclHelper::getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second;
+ }
+ ruleStr << " }]";
+ return ruleStr.str();
+ }
+ };
+ typedef std::vector<rule> ruleSet;
+ typedef ruleSet::const_iterator ruleSetItr;
+ typedef std::map<std::string, ruleSet > actionObject; // user
+ typedef actionObject::iterator actObjItr;
+ typedef actionObject* aclAction;
+
+ // Action*[] -> Object*[] -> map<user -> set<Rule> >
+ aclAction* actionList[qpid::acl::ACTIONSIZE];
+ qpid::acl::AclResult decisionMode; // determines if the rule set is a deny or allow mode.
+ bool transferAcl;
+ std::string aclSource;
+
+ AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0);
+ AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey);
+ AclResult getACLResult(bool logOnly, bool log);
+
+ bool matchProp(const std::string & src, const std::string& src1);
+ void clear ();
+
+ AclData();
+ virtual ~AclData();
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLDATA_H
diff --git a/qpid/cpp/src/qpid/acl/AclPlugin.cpp b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
new file mode 100644
index 0000000000..e4d721ea44
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
@@ -0,0 +1,96 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <sstream>
+#include "qpid/acl/Acl.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility/in_place_factory.hpp>
+
+namespace qpid {
+namespace acl {
+
+using namespace std;
+
+/** Note separating options from values to work around boost version differences.
+ * Old boost takes a reference to options objects, but new boost makes a copy.
+ * New boost allows a shared_ptr but that's not compatible with old boost.
+ */
+struct AclOptions : public Options {
+ AclValues& values;
+
+ AclOptions(AclValues& v) : Options("ACL Options"), values(v) {
+ addOptions()
+ ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir");
+ }
+};
+
+struct AclPlugin : public Plugin {
+
+ AclValues values;
+ AclOptions options;
+ boost::intrusive_ptr<Acl> acl;
+
+ AclPlugin() : options(values) {}
+
+ Options* getOptions() { return &options; }
+
+ void init(broker::Broker& b) {
+ if (values.aclFile.empty()){
+ QPID_LOG(info, "Policy file not specified. ACL Disabled, no ACL checking being done!");
+ return;
+ }
+
+ if (acl) throw Exception("ACL plugin cannot be initialized twice in one process.");
+
+ if (values.aclFile.at(0) != '/' && !b.getDataDir().getPath().empty()) {
+ std::ostringstream oss;
+ oss << b.getDataDir().getPath() << "/" << values.aclFile;
+ values.aclFile = oss.str();
+ }
+
+ acl = new Acl(values, b);
+ b.setAcl(acl.get());
+ b.addFinalizer(boost::bind(&AclPlugin::shutdown, this));
+ }
+
+ template <class T> bool init(Plugin::Target& target) {
+ T* t = dynamic_cast<T*>(&target);
+ if (t) init(*t);
+ return t;
+ }
+
+ void earlyInitialize(Plugin::Target&) {}
+
+ void initialize(Plugin::Target& target) {
+ init<broker::Broker>(target);
+ }
+
+ void shutdown() { acl = 0; }
+};
+
+static AclPlugin instance; // Static initialization.
+
+// For test purposes.
+boost::intrusive_ptr<Acl> getGlobalAcl() { return instance.acl; }
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp
new file mode 100644
index 0000000000..31c69e69b5
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclReader.cpp
@@ -0,0 +1,581 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclReader.h"
+
+#include <cctype>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+
+#include <iomanip> // degug
+#include <iostream> // debug
+
+#define ACL_FORMAT_ERR_LOG_PREFIX "ACL format error: " << fileName << ":" << lineNumber << ": "
+
+namespace qpid {
+namespace acl {
+
+AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups) : res(r), actionAll(true), objStatus(NONE) {
+ processName(n, groups);
+}
+AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a) : res(r), actionAll(false), action(a), objStatus(NONE) {
+ processName(n, groups);
+}
+
+void AclReader::aclRule::setObjectType(const ObjectType o) {
+ objStatus = VALUE;
+ object = o;
+}
+
+void AclReader::aclRule::setObjectTypeAll() {
+ objStatus = ALL;
+}
+
+bool AclReader::aclRule::addProperty(const Property p, const std::string v) {
+ return props.insert(propNvPair(p, v)).second;
+}
+
+bool AclReader::aclRule::validate(const AclHelper::objectMapPtr& /*validationMap*/) {
+ // TODO - invalid rules won't ever be called in real life...
+ return true;
+}
+
+// Debug aid
+std::string AclReader::aclRule::toString() {
+ std::ostringstream oss;
+ oss << AclHelper::getAclResultStr(res) << " [";
+ for (nsCitr itr = names.begin(); itr != names.end(); itr++) {
+ if (itr != names.begin()) oss << ", ";
+ oss << *itr;
+ }
+ oss << "]";
+ if (actionAll) {
+ oss << " *";
+ } else {
+ oss << " " << AclHelper::getActionStr(action);
+ }
+ if (objStatus == ALL) {
+ oss << " *";
+ } else if (objStatus == VALUE) {
+ oss << " " << AclHelper::getObjectTypeStr(object);
+ }
+ for (pmCitr i=props.begin(); i!=props.end(); i++) {
+ oss << " " << AclHelper::getPropertyStr(i->first) << "=" << i->second;
+ }
+ return oss.str();
+}
+
+void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) {
+ d->clear();
+ QPID_LOG(debug, "ACL Load Rules");
+ int cnt = rules.size();
+ bool foundmode = false;
+
+ for (rlCitr i = rules.end(); cnt; cnt--) {
+ i--;
+ QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2)
+ << cnt << " " << (*i)->toString());
+
+ if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1
+ && (*((*i)->names.begin())).compare("*") == 0) {
+ d->decisionMode = (*i)->res;
+ QPID_LOG(debug, "ACL FoundMode "
+ << AclHelper::getAclResultStr(d->decisionMode));
+ foundmode = true;
+ } else {
+ AclData::rule rule((*i)->props);
+ bool addrule = true;
+
+ switch ((*i)->res) {
+ case qpid::acl::ALLOWLOG:
+ rule.log = true;
+ if (d->decisionMode == qpid::acl::ALLOW ||
+ d->decisionMode == qpid::acl::ALLOWLOG)
+ rule.logOnly = true;
+ break;
+ case qpid::acl::ALLOW:
+ if (d->decisionMode == qpid::acl::ALLOW ||
+ d->decisionMode == qpid::acl::ALLOWLOG)
+ addrule = false;
+ break;
+ case qpid::acl::DENYLOG:
+ rule.log = true;
+ if (d->decisionMode == qpid::acl::DENY ||
+ d->decisionMode == qpid::acl::DENYLOG)
+ rule.logOnly = true;
+ break;
+ case qpid::acl::DENY:
+ if (d->decisionMode == qpid::acl::DENY ||
+ d->decisionMode == qpid::acl::DENYLOG)
+ addrule = false;
+ break;
+ default:
+ throw Exception("Invalid ACL Result loading rules.");
+ }
+
+ // Action -> Object -> map<user -> set<Rule> >
+ if (addrule) {
+ std::ostringstream actionstr;
+ for (int acnt = ((*i)->actionAll ? 0 : (*i)->action);
+ acnt < acl::ACTIONSIZE;
+ (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) {
+
+ if (acnt == acl::ACT_PUBLISH)
+ d->transferAcl = true; // we have transfer ACL
+
+ actionstr << AclHelper::getActionStr((Action) acnt) << ",";
+
+ //find the Action, create if not exist
+ if (d->actionList[acnt] == NULL) {
+ d->actionList[acnt] =
+ new AclData::aclAction[qpid::acl::OBJECTSIZE];
+ for (int j = 0; j < qpid::acl::OBJECTSIZE; j++)
+ d->actionList[acnt][j] = NULL;
+ }
+
+ // optimize this loop to limit to valid options only!!
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0
+ : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+
+ //find the Object, create if not exist
+ if (d->actionList[acnt][ocnt] == NULL)
+ d->actionList[acnt][ocnt] =
+ new AclData::actionObject;
+
+ // add users and Rule to object set
+ bool allNames = false;
+ // check to see if names.begin is '*'
+ if ((*(*i)->names.begin()).compare("*") == 0)
+ allNames = true;
+
+ for (nsCitr itr = (allNames ? names.begin()
+ : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+
+ AclData::actObjItr itrRule =
+ d->actionList[acnt][ocnt]->find(*itr);
+
+ if (itrRule == d->actionList[acnt][ocnt]->end()) {
+ AclData::ruleSet rSet;
+ rSet.push_back(rule);
+ d->actionList[acnt][ocnt]->insert
+ (make_pair(std::string(*itr), rSet));
+ } else {
+ // TODO add code to check for dead rules
+ // allow peter create queue name=tmp <-- dead rule!!
+ // allow peter create queue
+
+ itrRule->second.push_back(rule);
+ }
+ }
+
+ }
+ }
+
+ std::ostringstream objstr;
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+ objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ",";
+ }
+
+ bool allNames = ((*(*i)->names.begin()).compare("*") == 0);
+ std::ostringstream userstr;
+ for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+ userstr << *itr << ",";
+ }
+
+ QPID_LOG(debug, "ACL: Adding actions {" <<
+ actionstr.str().substr(0,actionstr.str().length()-1)
+ << "} to objects {" <<
+ objstr.str().substr(0,objstr.str().length()-1)
+ << "} with props " <<
+ AclHelper::propertyMapToString(&rule.props)
+ << " for users {" <<
+ userstr.str().substr(0,userstr.str().length()-1)
+ << "}" );
+ } else {
+ QPID_LOG(debug, "ACL Skipping based on Mode:"
+ << AclHelper::getAclResultStr(d->decisionMode));
+ }
+ }
+
+ }
+
+}
+
+
+void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) {
+ if (name.compare("all") == 0) {
+ names.insert("*");
+ } else {
+ gmCitr itr = groups.find(name);
+ if (itr == groups.end()) {
+ names.insert(name);
+ } else {
+ names.insert(itr->second->begin(), itr->second->end());
+ }
+ }
+}
+
+AclReader::AclReader() : lineNumber(0), contFlag(false), validationMap(new AclHelper::objectMap) {
+ AclHelper::loadValidationMap(validationMap);
+ names.insert("*");
+}
+
+AclReader::~AclReader() {}
+
+std::string AclReader::getError() {
+ return errorStream.str();
+}
+
+int AclReader::read(const std::string& fn, boost::shared_ptr<AclData> d) {
+ fileName = fn;
+ lineNumber = 0;
+ char buff[1024];
+ std::ifstream ifs(fn.c_str(), std::ios_base::in);
+ if (!ifs.good()) {
+ errorStream << "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ return -1;
+ }
+ try {
+ bool err = false;
+ while (ifs.good()) {
+ ifs.getline(buff, 1024);
+ lineNumber++;
+ if (std::strlen(buff) > 0 && buff[0] != '#') // Ignore blank lines and comments
+ err |= !processLine(buff);
+ }
+ if (!ifs.eof())
+ {
+ errorStream << "Unable to read ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ ifs.close();
+ return -2;
+ }
+ ifs.close();
+ if (err) return -3;
+ QPID_LOG(notice, "Read ACL file \"" << fn << "\"");
+ } catch (const std::exception& e) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what();
+ ifs.close();
+ return -4;
+ } catch (...) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": Unknown exception";
+ ifs.close();
+ return -5;
+ }
+ printNames();
+ printRules();
+ loadDecisionData(d);
+
+ return 0;
+}
+
+bool AclReader::processLine(char* line) {
+ bool ret = false;
+ std::vector<std::string> toks;
+
+ // Check for continuation
+ char* contCharPtr = std::strrchr(line, '\\');
+ bool cont = contCharPtr != 0;
+ if (cont) *contCharPtr = 0;
+
+ int numToks = tokenize(line, toks);
+
+ if (cont && numToks == 0){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line \"" << lineNumber << "\" contains an illegal extension.";
+ return false;
+ }
+
+ if (numToks && (toks[0].compare("group") == 0 || contFlag)) {
+ ret = processGroupLine(toks, cont);
+ } else if (numToks && toks[0].compare("acl") == 0) {
+ ret = processAclLine(toks);
+ } else {
+ // Check for whitespace only line, ignore these
+ bool ws = true;
+ for (unsigned i=0; i<std::strlen(line) && ws; i++) {
+ if (!std::isspace(line[i])) ws = false;
+ }
+ if (ws) {
+ ret = true;
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Non-continuation line must start with \"group\" or \"acl\".";
+ ret = false;
+ }
+ }
+ contFlag = cont;
+ return ret;
+}
+
+int AclReader::tokenize(char* line, std::vector<std::string>& toks) {
+ const char* tokChars = " \t\n\f\v\r";
+ int cnt = 0;
+ char* cp = std::strtok(line, tokChars);
+ while (cp != 0) {
+ toks.push_back(std::string(cp));
+ cnt++;
+ cp = std::strtok(0, tokChars);
+ }
+ return cnt;
+}
+
+// Return true if the line is successfully processed without errors
+// If cont is true, then groupName must be set to the continuation group name
+bool AclReader::processGroupLine(tokList& toks, const bool cont) {
+ const unsigned toksSize = toks.size();
+
+ if (contFlag) {
+ gmCitr citr = groups.find(groupName);
+ for (unsigned i = 0; i < toksSize; i++) {
+ if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ } else {
+ const unsigned minimumSize = (cont ? 2 : 3);
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for group definition.";
+ return false;
+ }
+ if (!isValidGroupName(toks[1])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Group name \"" << toks[1] << "\" contains illegal characters.";
+ return false;
+ }
+ gmCitr citr = addGroup(toks[1]);
+ if (citr == groups.end()) return false;
+ for (unsigned i = 2; i < toksSize; i++) {
+ if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ }
+ return true;
+}
+
+// Return true if sucessfully added group
+AclReader::gmCitr AclReader::addGroup(const std::string& newGroupName) {
+ gmCitr citr = groups.find(newGroupName);
+ if (citr != groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Duplicate group name \"" << newGroupName << "\".";
+ return groups.end();
+ }
+ groupPair p(newGroupName, nameSetPtr(new nameSet));
+ gmRes res = groups.insert(p);
+ assert(res.second);
+ groupName = newGroupName;
+ return res.first;
+}
+
+void AclReader::addName(const std::string& name, nameSetPtr groupNameSet) {
+ gmCitr citr = groups.find(name);
+ if (citr != groups.end() && citr->first != name){
+ // This is a previously defined group: add all the names in that group to this group
+ groupNameSet->insert(citr->second->begin(), citr->second->end());
+ } else {
+ // Not a known group name
+ groupNameSet->insert(name);
+ addName(name);
+ }
+}
+
+void AclReader::addName(const std::string& name) {
+ names.insert(name);
+}
+
+// Debug aid
+void AclReader::printNames() const {
+ QPID_LOG(debug, "Group list: " << groups.size() << " groups found:" );
+ std::string tmp;
+ for (gmCitr i=groups.begin(); i!= groups.end(); i++) {
+ tmp += " \"";
+ tmp += i->first;
+ tmp += "\":";
+ for (nsCitr j=i->second->begin(); j!=i->second->end(); j++) {
+ tmp += " ";
+ tmp += *j;
+ }
+ QPID_LOG(debug, tmp);
+ tmp.clear();
+ }
+ QPID_LOG(debug, "Name list: " << names.size() << " names found:" );
+ tmp.clear();
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ tmp += " ";
+ tmp += *k;
+ }
+ QPID_LOG(debug, tmp);
+}
+
+bool AclReader::processAclLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ if (toksSize < 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for acl definition.";
+ return false;
+ }
+
+ AclResult res;
+ try {
+ res = AclHelper::getAclResult(toks[1]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown ACL permission \"" << toks[1] << "\".";
+ return false;
+ }
+
+ bool actionAllFlag = toks[3].compare("all") == 0;
+ bool userAllFlag = toks[2].compare("all") == 0;
+ Action action;
+ if (actionAllFlag) {
+
+ if (userAllFlag && toksSize > 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Tokens found after action \"all\".";
+ return false;
+ }
+ action = ACT_CONSUME; // dummy; compiler must initialize action for this code path
+ } else {
+ try {
+ action = AclHelper::getAction(toks[3]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown action \"" << toks[3] << "\".";
+ return false;
+ }
+ }
+
+ // Create rule obj; then add object (if any) and properties (if any)
+ aclRulePtr rule;
+ if (actionAllFlag) {
+ rule.reset(new aclRule(res, toks[2], groups));
+ } else {
+ rule.reset(new aclRule(res, toks[2], groups, action));
+ }
+
+ if (toksSize >= 5) { // object name-value pair
+ if (toks[4].compare("all") == 0) {
+ rule->setObjectTypeAll();
+ } else {
+ try {
+ rule->setObjectType(AclHelper::getObjectType(toks[4]));
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown object \"" << toks[4] << "\".";
+ return false;
+ }
+ }
+ }
+
+ if (toksSize >= 6) { // property name-value pair(s)
+ for (unsigned i=5; i<toksSize; i++) {
+ nvPair propNvp = splitNameValuePair(toks[i]);
+ if (propNvp.second.size() == 0) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ <<", Badly formed property name-value pair \""
+ << propNvp.first << "\". (Must be name=value)";
+ return false;
+ }
+ Property prop;
+ try {
+ prop = AclHelper::getProperty(propNvp.first);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown property \"" << propNvp.first << "\".";
+ return false;
+ }
+ rule->addProperty(prop, propNvp.second);
+ }
+ }
+ // Check if name (toks[2]) is group; if not, add as name of individual
+ if (toks[2].compare("all") != 0) {
+ if (groups.find(toks[2]) == groups.end()) {
+ addName(toks[2]);
+ }
+ }
+
+ // If rule validates, add to rule list
+ if (!rule->validate(validationMap)) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Invalid object/action/property combination.";
+ return false;
+ }
+ rules.push_back(rule);
+
+ return true;
+}
+
+// Debug aid
+void AclReader::printRules() const {
+ QPID_LOG(debug, "Rule list: " << rules.size() << " ACL rules found:");
+ int cnt = 0;
+ for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, " " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString());
+ }
+}
+
+// Static function
+// Return true if the name is well-formed (ie contains legal characters)
+bool AclReader::isValidGroupName(const std::string& name) {
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_') return false;
+ }
+ return true;
+}
+
+// Static function
+// Split name-value pair around '=' char of the form "name=value"
+AclReader::nvPair AclReader::splitNameValuePair(const std::string& nvpString) {
+ std::size_t pos = nvpString.find("=");
+ if (pos == std::string::npos || pos == nvpString.size() - 1) {
+ return nvPair(nvpString, "");
+ }
+ return nvPair(nvpString.substr(0, pos), nvpString.substr(pos+1));
+}
+
+// Returns true if a username has the name@realm format
+bool AclReader::isValidUserName(const std::string& name){
+ size_t pos = name.find('@');
+ if ( pos == std::string::npos || pos == name.length() -1){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username '" << name << "' must contain a realm";
+ return false;
+ }
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_' && ch != '@' && ch != '.' && ch != '/'){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username \"" << name << "\" contains illegal characters.";
+ return false;
+ }
+ }
+ return true;
+}
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclReader.h b/qpid/cpp/src/qpid/acl/AclReader.h
new file mode 100644
index 0000000000..62c6f38f37
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclReader.h
@@ -0,0 +1,118 @@
+#ifndef QPID_ACL_ACLREADER_H
+#define QPID_ACL_ACLREADER_H
+
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <sstream>
+#include "qpid/acl/AclData.h"
+#include "qpid/broker/AclModule.h"
+
+namespace qpid {
+namespace acl {
+
+class AclReader {
+ typedef std::set<std::string> nameSet;
+ typedef nameSet::const_iterator nsCitr;
+ typedef boost::shared_ptr<nameSet> nameSetPtr;
+
+ typedef std::pair<std::string, nameSetPtr> groupPair;
+ typedef std::map<std::string, nameSetPtr> groupMap;
+ typedef groupMap::const_iterator gmCitr;
+ typedef std::pair<gmCitr, bool> gmRes;
+
+ typedef std::pair<Property, std::string> propNvPair;
+ typedef std::map<Property, std::string> propMap;
+ typedef propMap::const_iterator pmCitr;
+
+ class aclRule {
+ public:
+ enum objectStatus {NONE, VALUE, ALL};
+ AclResult res;
+ nameSet names;
+ bool actionAll; // True if action is set to keyword "all"
+ Action action; // Ignored if action is set to keyword "all"
+ objectStatus objStatus;
+ ObjectType object; // Ignored for all status values except VALUE
+ propMap props;
+ public:
+ aclRule(const AclResult r, const std::string n, const groupMap& groups); // action = "all"
+ aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a);
+ void setObjectType(const ObjectType o);
+ void setObjectTypeAll();
+ bool addProperty(const Property p, const std::string v);
+ bool validate(const AclHelper::objectMapPtr& validationMap);
+ std::string toString(); // debug aid
+ private:
+ void processName(const std::string& name, const groupMap& groups);
+ };
+ typedef boost::shared_ptr<aclRule> aclRulePtr;
+ typedef std::vector<aclRulePtr> ruleList;
+ typedef ruleList::const_iterator rlCitr;
+
+ typedef std::vector<std::string> tokList;
+ typedef tokList::const_iterator tlCitr;
+
+ typedef std::set<std::string> keywordSet;
+ typedef keywordSet::const_iterator ksCitr;
+ typedef std::pair<std::string, std::string> nvPair; // Name-Value pair
+
+ std::string fileName;
+ int lineNumber;
+ bool contFlag;
+ std::string groupName;
+ nameSet names;
+ groupMap groups;
+ ruleList rules;
+ AclHelper::objectMapPtr validationMap;
+ std::ostringstream errorStream;
+
+ public:
+ AclReader();
+ virtual ~AclReader();
+ int read(const std::string& fn, boost::shared_ptr<AclData> d);
+ std::string getError();
+
+ private:
+ bool processLine(char* line);
+ void loadDecisionData( boost::shared_ptr<AclData> d);
+ int tokenize(char* line, tokList& toks);
+
+ bool processGroupLine(tokList& toks, const bool cont);
+ gmCitr addGroup(const std::string& groupName);
+ void addName(const std::string& name, nameSetPtr groupNameSet);
+ void addName(const std::string& name);
+ void printNames() const; // debug aid
+
+ bool processAclLine(tokList& toks);
+ void printRules() const; // debug aid
+ bool isValidUserName(const std::string& name);
+
+ static bool isValidGroupName(const std::string& name);
+ static nvPair splitNameValuePair(const std::string& nvpString);
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLREADER_H
diff --git a/qpid/cpp/src/qpid/acl/AclValidator.cpp b/qpid/cpp/src/qpid/acl/AclValidator.cpp
new file mode 100644
index 0000000000..57b68e520a
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclValidator.cpp
@@ -0,0 +1,150 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclValidator.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <numeric>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){
+}
+
+bool AclValidator::IntPropertyType::validate(const std::string& val) {
+ int64_t v;
+ try
+ {
+ v = boost::lexical_cast<int64_t>(val);
+ }catch(const boost::bad_lexical_cast&){
+ return 0;
+ }
+
+ if (v < min || v >= max){
+ return 0;
+ }else{
+ return 1;
+ }
+}
+
+std::string AclValidator::IntPropertyType::allowedValues() {
+ return "values should be between " +
+ boost::lexical_cast<std::string>(min) + " and " +
+ boost::lexical_cast<std::string>(max);
+}
+
+AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){
+}
+
+bool AclValidator::EnumPropertyType::validate(const std::string& val) {
+ for (std::vector<std::string>::iterator itr = values.begin(); itr != values.end(); ++itr ){
+ if (val.compare(*itr) == 0){
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+std::string AclValidator::EnumPropertyType::allowedValues() {
+ std::ostringstream oss;
+ oss << "possible values are one of { ";
+ for (std::vector<std::string>::iterator itr = values.begin(); itr != values.end(); itr++ ){
+ oss << "'" << *itr << "' ";
+ }
+ oss << "}";
+ return oss.str();
+}
+
+AclValidator::AclValidator(){
+ validators.insert(Validator(acl::PROP_MAXQUEUESIZE,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))
+ )
+ );
+
+ validators.insert(Validator(acl::PROP_MAXQUEUECOUNT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))
+ )
+ );
+
+ std::string policyTypes[] = {"ring", "ring_strict", "flow_to_disk", "reject"};
+ std::vector<std::string> v(policyTypes, policyTypes + sizeof(policyTypes) / sizeof(std::string));
+ validators.insert(Validator(acl::PROP_POLICYTYPE,
+ boost::shared_ptr<PropertyType>(new EnumPropertyType(v))
+ )
+ );
+
+}
+
+AclValidator::~AclValidator(){
+}
+
+/* Iterate through the data model and validate the parameters. */
+void AclValidator::validate(boost::shared_ptr<AclData> d) {
+
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){
+
+ if (d->actionList[cnt]){
+
+ for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++){
+
+ if (d->actionList[cnt][cnt1]){
+
+ std::for_each(d->actionList[cnt][cnt1]->begin(),
+ d->actionList[cnt][cnt1]->end(),
+ boost::bind(&AclValidator::validateRuleSet, this, _1));
+ }//if
+ }//for
+ }//if
+ }//for
+}
+
+void AclValidator::validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules){
+ std::for_each(rules.second.begin(),
+ rules.second.end(),
+ boost::bind(&AclValidator::validateRule, this, _1));
+}
+
+void AclValidator::validateRule(qpid::acl::AclData::rule& rule){
+ std::for_each(rule.props.begin(),
+ rule.props.end(),
+ boost::bind(&AclValidator::validateProperty, this, _1));
+}
+
+void AclValidator::validateProperty(std::pair<const qpid::acl::Property, std::string>& prop){
+ ValidatorItr itr = validators.find(prop.first);
+ if (itr != validators.end()){
+ QPID_LOG(debug,"Found validator for property " << itr->second->allowedValues());
+
+ if (!itr->second->validate(prop.second)){
+ throw Exception( prop.second + " is not a valid value for '" +
+ AclHelper::getPropertyStr(prop.first) + "', " +
+ itr->second->allowedValues());
+ }
+ }
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/acl/AclValidator.h b/qpid/cpp/src/qpid/acl/AclValidator.h
new file mode 100644
index 0000000000..966e5d326b
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclValidator.h
@@ -0,0 +1,83 @@
+#ifndef QPID_ACL_ACLVALIDATOR_H
+#define QPID_ACL_ACLVALIDATOR_H
+
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/AclModule.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+class AclValidator {
+
+ /* Base Property */
+ class PropertyType{
+
+ public:
+ virtual ~PropertyType(){};
+ virtual bool validate(const std::string& val)=0;
+ virtual std::string allowedValues()=0;
+ };
+
+ class IntPropertyType : public PropertyType{
+ int64_t min;
+ int64_t max;
+
+ public:
+ IntPropertyType(int64_t min,int64_t max);
+ virtual ~IntPropertyType (){};
+ virtual bool validate(const std::string& val);
+ virtual std::string allowedValues();
+ };
+
+ class EnumPropertyType : public PropertyType{
+ std::vector<std::string> values;
+
+ public:
+ EnumPropertyType(std::vector<std::string>& allowed);
+ virtual ~EnumPropertyType (){};
+ virtual bool validate(const std::string& val);
+ virtual std::string allowedValues();
+ };
+
+ typedef std::pair<acl::Property,boost::shared_ptr<PropertyType> > Validator;
+ typedef std::map<acl::Property,boost::shared_ptr<PropertyType> > ValidatorMap;
+ typedef ValidatorMap::iterator ValidatorItr;
+
+ ValidatorMap validators;
+
+public:
+
+ void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules);
+ void validateRule(qpid::acl::AclData::rule& rule);
+ void validateProperty(std::pair<const qpid::acl::Property, std::string>& prop);
+ void validate(boost::shared_ptr<AclData> d);
+ AclValidator();
+ ~AclValidator();
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLVALIDATOR_H
diff --git a/qpid/cpp/src/qpid/acl/management-schema.xml b/qpid/cpp/src/qpid/acl/management-schema.xml
new file mode 100644
index 0000000000..7f48a9be34
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/management-schema.xml
@@ -0,0 +1,44 @@
+<schema package="org.apache.qpid.acl">
+
+<!--
+ * Copyright (c) 2008 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.
+-->
+
+ <class name="Acl">
+ <property name="brokerRef" type="objId" references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/>
+ <property name="policyFile" type="lstr" access="RO" desc="Name of the policy file"/>
+ <property name="enforcingAcl" type="bool" access="RO" desc="Currently Enforcing ACL"/>
+ <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/>
+ <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/>
+ <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
+
+ <method name="reloadACLFile" desc="Reload the ACL file"/>
+ </class>
+
+ <eventArguments>
+ <arg name="action" type="sstr"/>
+ <arg name="arguments" type="map"/>
+ <arg name="objectName" type="sstr"/>
+ <arg name="objectType" type="sstr"/>
+ <arg name="reason" type="lstr"/>
+ <arg name="userId" type="sstr"/>
+ </eventArguments>
+
+ <event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/>
+ <event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/>
+ <event name="fileLoaded" sev="inform" args="userId"/>
+ <event name="fileLoadFailed" sev="error" args="userId, reason"/>
+
+</schema>
diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
new file mode 100644
index 0000000000..633401ef5b
--- /dev/null
+++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -0,0 +1,1390 @@
+
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementObject.h"
+#include "qpid/log/Statement.h"
+#include "qpid/agent/ManagementAgentImpl.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include <list>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <iostream>
+#include <fstream>
+#include <boost/lexical_cast.hpp>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::management;
+using namespace qpid::sys;
+using namespace std;
+using std::stringstream;
+using std::ofstream;
+using std::ifstream;
+using std::string;
+using std::endl;
+using qpid::types::Variant;
+using qpid::amqp_0_10::MapCodec;
+using qpid::amqp_0_10::ListCodec;
+
+namespace {
+ qpid::sys::Mutex lock;
+ bool disabled = false;
+ ManagementAgent* agent = 0;
+ int refCount = 0;
+
+ const string defaultVendorName("vendor");
+ const string defaultProductName("product");
+
+ // Create a valid binding key substring by
+ // replacing all '.' chars with '_'
+ const string keyifyNameStr(const string& name)
+ {
+ string n2 = name;
+
+ size_t pos = n2.find('.');
+ while (pos != n2.npos) {
+ n2.replace(pos, 1, "_");
+ pos = n2.find('.', pos);
+ }
+ return n2;
+ }
+}
+
+ManagementAgent::Singleton::Singleton(bool disableManagement)
+{
+ sys::Mutex::ScopedLock _lock(lock);
+ if (disableManagement && !disabled) {
+ disabled = true;
+ assert(refCount == 0); // can't disable after agent has been allocated
+ }
+ if (refCount == 0 && !disabled)
+ agent = new ManagementAgentImpl();
+ refCount++;
+}
+
+ManagementAgent::Singleton::~Singleton()
+{
+ sys::Mutex::ScopedLock _lock(lock);
+ refCount--;
+ if (refCount == 0 && !disabled) {
+ delete agent;
+ agent = 0;
+ }
+}
+
+ManagementAgent* ManagementAgent::Singleton::getInstance()
+{
+ return agent;
+}
+
+const string ManagementAgentImpl::storeMagicNumber("MA02");
+
+ManagementAgentImpl::ManagementAgentImpl() :
+ interval(10), extThread(false), pipeHandle(0), notifyCallback(0), notifyContext(0),
+ notifyable(0), inCallback(false),
+ initialized(false), connected(false), useMapMsg(false), lastFailure("never connected"),
+ topicExchange("qmf.default.topic"), directExchange("qmf.default.direct"),
+ schemaTimestamp(Duration(EPOCH, now())),
+ publishAllData(true), requestedBrokerBank(0), requestedAgentBank(0),
+ assignedBrokerBank(0), assignedAgentBank(0), bootSequence(0),
+ maxV2ReplyObjs(10), // KAG todo: make this a tuneable parameter
+ connThreadBody(*this), connThread(connThreadBody),
+ pubThreadBody(*this), pubThread(pubThreadBody)
+{
+}
+
+ManagementAgentImpl::~ManagementAgentImpl()
+{
+ // shutdown & cleanup all threads
+ connThreadBody.close();
+ pubThreadBody.close();
+
+ connThread.join();
+ pubThread.join();
+
+ if (pipeHandle) {
+ delete pipeHandle;
+ pipeHandle = 0;
+ }
+}
+
+void ManagementAgentImpl::setName(const string& vendor, const string& product, const string& instance)
+{
+ if (vendor.find(':') != vendor.npos) {
+ throw Exception("vendor string cannot contain a ':' character.");
+ }
+ if (product.find(':') != product.npos) {
+ throw Exception("product string cannot contain a ':' character.");
+ }
+
+ attrMap["_vendor"] = vendor;
+ attrMap["_product"] = product;
+ if (!instance.empty()) {
+ attrMap["_instance"] = instance;
+ }
+}
+
+
+void ManagementAgentImpl::getName(string& vendor, string& product, string& instance)
+{
+ vendor = std::string(attrMap["_vendor"]);
+ product = std::string(attrMap["_product"]);
+ instance = std::string(attrMap["_instance"]);
+}
+
+
+const std::string& ManagementAgentImpl::getAddress()
+{
+ return name_address;
+}
+
+
+void ManagementAgentImpl::init(const string& brokerHost,
+ uint16_t brokerPort,
+ uint16_t intervalSeconds,
+ bool useExternalThread,
+ const string& _storeFile,
+ const string& uid,
+ const string& pwd,
+ const string& mech,
+ const string& proto)
+{
+ management::ConnectionSettings settings;
+ settings.protocol = proto;
+ settings.host = brokerHost;
+ settings.port = brokerPort;
+ settings.username = uid;
+ settings.password = pwd;
+ settings.mechanism = mech;
+ settings.heartbeat = 10;
+ init(settings, intervalSeconds, useExternalThread, _storeFile);
+}
+
+void ManagementAgentImpl::init(const qpid::management::ConnectionSettings& settings,
+ uint16_t intervalSeconds,
+ bool useExternalThread,
+ const string& _storeFile)
+{
+ std::string cfgVendor, cfgProduct, cfgInstance;
+
+ interval = intervalSeconds;
+ extThread = useExternalThread;
+ storeFile = _storeFile;
+ nextObjectId = 1;
+
+ //
+ // Convert from management::ConnectionSettings to client::ConnectionSettings
+ //
+ connectionSettings.protocol = settings.protocol;
+ connectionSettings.host = settings.host;
+ connectionSettings.port = settings.port;
+ connectionSettings.virtualhost = settings.virtualhost;
+ connectionSettings.username = settings.username;
+ connectionSettings.password = settings.password;
+ connectionSettings.mechanism = settings.mechanism;
+ connectionSettings.locale = settings.locale;
+ connectionSettings.heartbeat = settings.heartbeat;
+ connectionSettings.maxChannels = settings.maxChannels;
+ connectionSettings.maxFrameSize = settings.maxFrameSize;
+ connectionSettings.bounds = settings.bounds;
+ connectionSettings.tcpNoDelay = settings.tcpNoDelay;
+ connectionSettings.service = settings.service;
+ connectionSettings.minSsf = settings.minSsf;
+ connectionSettings.maxSsf = settings.maxSsf;
+
+ retrieveData(cfgVendor, cfgProduct, cfgInstance);
+
+ bootSequence++;
+ if ((bootSequence & 0xF000) != 0)
+ bootSequence = 1;
+
+ // setup the agent's name. The name may be set via a call to setName(). If setName()
+ // has not been called, the name can be read from the configuration file. If there is
+ // no name in the configuration file, a unique default name is provided.
+ if (attrMap.empty()) {
+ // setName() never called by application, so use names retrieved from config, otherwise defaults.
+ setName(cfgVendor.empty() ? defaultVendorName : cfgVendor,
+ cfgProduct.empty() ? defaultProductName : cfgProduct,
+ cfgInstance.empty() ? qpid::types::Uuid(true).str() : cfgInstance);
+ } else if (attrMap.find("_instance") == attrMap.end()) {
+ // setName() called, but instance was not specified, use config or generate a uuid
+ setName(attrMap["_vendor"].asString(), attrMap["_product"].asString(),
+ cfgInstance.empty() ? qpid::types::Uuid(true).str() : cfgInstance);
+ }
+
+ name_address = attrMap["_vendor"].asString() + ":" + attrMap["_product"].asString() + ":" + attrMap["_instance"].asString();
+ vendorNameKey = keyifyNameStr(attrMap["_vendor"].asString());
+ productNameKey = keyifyNameStr(attrMap["_product"].asString());
+ instanceNameKey = keyifyNameStr(attrMap["_instance"].asString());
+ attrMap["_name"] = name_address;
+
+ storeData(true);
+
+ QPID_LOG(info, "QMF Agent Initialized: broker=" << settings.host << ":" << settings.port <<
+ " interval=" << intervalSeconds << " storeFile=" << _storeFile << " name=" << name_address);
+
+ initialized = true;
+}
+
+void ManagementAgentImpl::registerClass(const string& packageName,
+ const string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = findOrAddPackage(packageName);
+ addClassLocal(ManagementItem::CLASS_KIND_TABLE, pIter, className, md5Sum, schemaCall);
+}
+
+void ManagementAgentImpl::registerEvent(const string& packageName,
+ const string& eventName,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = findOrAddPackage(packageName);
+ addClassLocal(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall);
+}
+
+// old-style add object: 64bit id - deprecated
+ObjectId ManagementAgentImpl::addObject(ManagementObject* object,
+ uint64_t persistId)
+{
+ std::string key;
+ if (persistId) {
+ key = boost::lexical_cast<std::string>(persistId);
+ }
+ return addObject(object, key, persistId != 0);
+}
+
+
+// new style add object - use this approach!
+ObjectId ManagementAgentImpl::addObject(ManagementObject* object,
+ const std::string& key,
+ bool persistent)
+{
+ sys::Mutex::ScopedLock lock(addLock);
+
+ uint16_t sequence = persistent ? 0 : bootSequence;
+
+ ObjectId objectId(&attachment, 0, sequence);
+ if (key.empty())
+ objectId.setV2Key(*object); // let object generate the key
+ else
+ objectId.setV2Key(key);
+ objectId.setAgentName(name_address);
+
+ object->setObjectId(objectId);
+ newManagementObjects[objectId] = boost::shared_ptr<ManagementObject>(object);
+ return objectId;
+}
+
+
+void ManagementAgentImpl::raiseEvent(const ManagementEvent& event, severity_t severity)
+{
+ static const std::string severityStr[] = {
+ "emerg", "alert", "crit", "error", "warn",
+ "note", "info", "debug"
+ };
+ string content;
+ stringstream key;
+ Variant::Map headers;
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
+ uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+
+ // key << "console.event." << assignedBrokerBank << "." << assignedAgentBank << "." <<
+ // event.getPackageName() << "." << event.getEventName();
+ key << "agent.ind.event." << keyifyNameStr(event.getPackageName())
+ << "." << keyifyNameStr(event.getEventName())
+ << "." << severityStr[sev]
+ << "." << vendorNameKey
+ << "." << productNameKey
+ << "." << instanceNameKey;
+
+ Variant::Map map_;
+ Variant::Map schemaId;
+ Variant::Map values;
+
+ map_["_schema_id"] = mapEncodeSchemaId(event.getPackageName(),
+ event.getEventName(),
+ event.getMd5Sum(),
+ ManagementItem::CLASS_KIND_EVENT);
+ event.mapEncode(values);
+ map_["_values"] = values;
+ map_["_timestamp"] = uint64_t(Duration(EPOCH, now()));
+ map_["_severity"] = sev;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_event";
+ headers["qmf.agent"] = name_address;
+
+ Variant::List list;
+ list.push_back(map_);
+ ListCodec::encode(list, content);
+ }
+
+ connThreadBody.sendBuffer(content, "", headers, topicExchange, key.str(), "amqp/list");
+}
+
+uint32_t ManagementAgentImpl::pollCallbacks(uint32_t callLimit)
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+
+ if (inCallback) {
+ QPID_LOG(critical, "pollCallbacks invoked from the agent's thread!");
+ return 0;
+ }
+
+ for (uint32_t idx = 0; callLimit == 0 || idx < callLimit; idx++) {
+ if (methodQueue.empty())
+ break;
+
+ QueuedMethod* item = methodQueue.front();
+ methodQueue.pop_front();
+ {
+ sys::Mutex::ScopedUnlock unlock(agentLock);
+ invokeMethodRequest(item->body, item->cid, item->replyToExchange, item->replyToKey, item->userId);
+ delete item;
+ }
+ }
+
+ if (pipeHandle != 0) {
+ char rbuf[100];
+ while (pipeHandle->read(rbuf, 100) > 0) ; // Consume all signaling bytes
+ }
+ return methodQueue.size();
+}
+
+int ManagementAgentImpl::getSignalFd()
+{
+ if (extThread) {
+ if (pipeHandle == 0)
+ pipeHandle = new PipeHandle(true);
+ return pipeHandle->getReadHandle();
+ }
+
+ return -1;
+}
+
+void ManagementAgentImpl::setSignalCallback(cb_t callback, void* context)
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+ notifyCallback = callback;
+ notifyContext = context;
+}
+
+void ManagementAgentImpl::setSignalCallback(Notifyable& _notifyable)
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+ notifyable = &_notifyable;
+}
+
+void ManagementAgentImpl::startProtocol()
+{
+ sendHeartbeat();
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ publishAllData = true;
+ }
+}
+
+void ManagementAgentImpl::storeData(bool requested)
+{
+ if (!storeFile.empty()) {
+ ofstream outFile(storeFile.c_str());
+ uint32_t brokerBankToWrite = requested ? requestedBrokerBank : assignedBrokerBank;
+ uint32_t agentBankToWrite = requested ? requestedAgentBank : assignedAgentBank;
+
+ if (outFile.good()) {
+ outFile << storeMagicNumber << " " << brokerBankToWrite << " " <<
+ agentBankToWrite << " " << bootSequence << endl;
+
+ if (attrMap.find("_vendor") != attrMap.end())
+ outFile << "vendor=" << attrMap["_vendor"] << endl;
+ if (attrMap.find("_product") != attrMap.end())
+ outFile << "product=" << attrMap["_product"] << endl;
+ if (attrMap.find("_instance") != attrMap.end())
+ outFile << "instance=" << attrMap["_instance"] << endl;
+
+ outFile.close();
+ }
+ }
+}
+
+void ManagementAgentImpl::retrieveData(std::string& vendor, std::string& product, std::string& inst)
+{
+ vendor.clear();
+ product.clear();
+ inst.clear();
+
+ if (!storeFile.empty()) {
+ ifstream inFile(storeFile.c_str());
+ string mn;
+
+ if (inFile.good()) {
+ inFile >> mn;
+ if (mn == storeMagicNumber) {
+ std::string inText;
+
+ inFile >> requestedBrokerBank;
+ inFile >> requestedAgentBank;
+ inFile >> bootSequence;
+
+ while (inFile.good()) {
+ std::getline(inFile, inText);
+ if (!inText.compare(0, 7, "vendor=")) {
+ vendor = inText.substr(7);
+ QPID_LOG(debug, "read vendor name [" << vendor << "] from configuration file.");
+ } else if (!inText.compare(0, 8, "product=")) {
+ product = inText.substr(8);
+ QPID_LOG(debug, "read product name [" << product << "] from configuration file.");
+ } else if (!inText.compare(0, 9, "instance=")) {
+ inst = inText.substr(9);
+ QPID_LOG(debug, "read instance name [" << inst << "] from configuration file.");
+ }
+ }
+ }
+ inFile.close();
+ }
+ }
+}
+
+void ManagementAgentImpl::sendHeartbeat()
+{
+ static const string addr_key_base("agent.ind.heartbeat.");
+
+ Variant::Map map;
+ Variant::Map headers;
+ string content;
+ std::stringstream addr_key;
+
+ addr_key << addr_key_base << vendorNameKey
+ << "." << productNameKey
+ << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_agent_heartbeat_indication";
+ headers["qmf.agent"] = name_address;
+
+ getHeartbeatContent(map);
+ MapCodec::encode(map, content);
+
+ // Set TTL (in msecs) on outgoing heartbeat indications based on the interval
+ // time to prevent stale heartbeats from getting to the consoles.
+
+ connThreadBody.sendBuffer(content, "", headers, topicExchange, addr_key.str(),
+ "amqp/map", interval * 2 * 1000);
+
+ QPID_LOG(trace, "SENT AgentHeartbeat name=" << name_address);
+}
+
+void ManagementAgentImpl::sendException(const string& rte, const string& rtk, const string& cid,
+ const string& text, uint32_t code)
+{
+ Variant::Map map;
+ Variant::Map headers;
+ Variant::Map values;
+ string content;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_exception";
+ headers["qmf.agent"] = name_address;
+
+ values["error_code"] = code;
+ values["error_text"] = text;
+ map["_values"] = values;
+
+ MapCodec::encode(map, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
+
+ QPID_LOG(trace, "SENT Exception code=" << code <<" text=" << text);
+}
+
+void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, const string& rte, const string& rtk)
+{
+ string packageName;
+ SchemaClassKey key;
+ uint32_t outLen(0);
+ char localBuffer[MA_BUFFER_SIZE];
+ Buffer outBuffer(localBuffer, MA_BUFFER_SIZE);
+ bool found(false);
+
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(key.name);
+ inBuffer.getBin128(key.hash);
+
+ QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name);
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end()) {
+ ClassMap& cMap = pIter->second;
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end()) {
+ SchemaClass& schema = cIter->second;
+ string body;
+
+ encodeHeader(outBuffer, 's', sequence);
+ schema.writeSchemaCall(body);
+ outBuffer.putRawData(body);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ found = true;
+ }
+ }
+ }
+
+ if (found) {
+ connThreadBody.sendBuffer(outBuffer, outLen, rte, rtk);
+ QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name);
+ }
+}
+
+void ManagementAgentImpl::handleConsoleAddedIndication()
+{
+ sys::Mutex::ScopedLock lock(agentLock);
+ publishAllData = true;
+
+ QPID_LOG(trace, "RCVD ConsoleAddedInd");
+}
+
+void ManagementAgentImpl::invokeMethodRequest(const string& body, const string& cid, const string& rte, const string& rtk, const string& userId)
+{
+ string methodName;
+ bool failed = false;
+ Variant::Map inMap;
+ Variant::Map outMap;
+ Variant::Map::const_iterator oid, mid;
+ string content;
+
+ MapCodec::decode(body, inMap);
+
+ if ((oid = inMap.find("_object_id")) == inMap.end() ||
+ (mid = inMap.find("_method_name")) == inMap.end()) {
+ sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
+ Manageable::STATUS_PARAMETER_INVALID);
+ failed = true;
+ } else {
+ string methodName;
+ ObjectId objId;
+ Variant::Map inArgs;
+ Variant::Map callMap;
+
+ try {
+ // conversions will throw if input is invalid.
+ objId = ObjectId(oid->second.asMap());
+ methodName = mid->second.getString();
+
+ mid = inMap.find("_arguments");
+ if (mid != inMap.end()) {
+ inArgs = (mid->second).asMap();
+ }
+
+ QPID_LOG(trace, "Invoking Method: name=" << methodName << " args=" << inArgs);
+
+ boost::shared_ptr<ManagementObject> oPtr;
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ ObjectMap::iterator iter = managementObjects.find(objId);
+ if (iter != managementObjects.end() && !iter->second->isDeleted())
+ oPtr = iter->second;
+ }
+
+ if (oPtr.get() == 0) {
+ sendException(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_UNKNOWN_OBJECT),
+ Manageable::STATUS_UNKNOWN_OBJECT);
+ failed = true;
+ } else {
+ oPtr->doMethod(methodName, inArgs, callMap, userId);
+
+ if (callMap["_status_code"].asUint32() == 0) {
+ outMap["_arguments"] = Variant::Map();
+ for (Variant::Map::const_iterator iter = callMap.begin();
+ iter != callMap.end(); iter++)
+ if (iter->first != "_status_code" && iter->first != "_status_text")
+ outMap["_arguments"].asMap()[iter->first] = iter->second;
+ } else {
+ sendException(rte, rtk, cid, callMap["_status_text"], callMap["_status_code"]);
+ failed = true;
+ }
+ }
+
+ } catch(types::InvalidConversion& e) {
+ sendException(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION);
+ failed = true;
+ }
+ }
+
+ if (!failed) {
+ Variant::Map headers;
+ headers["method"] = "response";
+ headers["qmf.agent"] = name_address;
+ headers["qmf.opcode"] = "_method_response";
+ QPID_LOG(trace, "SENT MethodResponse map=" << outMap);
+ MapCodec::encode(outMap, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
+ }
+}
+
+void ManagementAgentImpl::handleGetQuery(const string& body, const string& cid, const string& rte, const string& rtk)
+{
+ moveNewObjectsLH();
+
+ Variant::Map inMap;
+ Variant::Map::const_iterator i;
+ Variant::Map headers;
+
+ MapCodec::decode(body, inMap);
+ QPID_LOG(trace, "RCVD GetQuery: map=" << inMap << " cid=" << cid);
+
+ headers["method"] = "response";
+ headers["qmf.opcode"] = "_query_response";
+ headers["qmf.agent"] = name_address;
+ headers["partial"] = Variant();
+
+ Variant::List list_;
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oidMap;
+ string content;
+
+ /*
+ * Unpack the _what element of the query. Currently we only support OBJECT queries.
+ */
+ i = inMap.find("_what");
+ if (i == inMap.end()) {
+ sendException(rte, rtk, cid, "_what element missing in Query");
+ return;
+ }
+
+ if (i->second.getType() != qpid::types::VAR_STRING) {
+ sendException(rte, rtk, cid, "_what element is not a string");
+ return;
+ }
+
+ if (i->second.asString() == "OBJECT") {
+ headers["qmf.content"] = "_data";
+ /*
+ * Unpack the _object_id element of the query if it is present. If it is present, find that one
+ * object and return it. If it is not present, send a class-based result.
+ */
+ i = inMap.find("_object_id");
+ if (i != inMap.end() && i->second.getType() == qpid::types::VAR_MAP) {
+ ObjectId objId(i->second.asMap());
+ boost::shared_ptr<ManagementObject> object;
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ ObjectMap::iterator iter = managementObjects.find(objId);
+ if (iter != managementObjects.end())
+ object = iter->second;
+ }
+
+ if (object.get() != 0) {
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ object->mapEncodeValues(values, true, true); // write both stats and properties
+ objId.mapEncode(oidMap);
+ map_["_values"] = values;
+ map_["_object_id"] = oidMap;
+ object->writeTimestamps(map_);
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ object->getMd5Sum());
+ list_.push_back(map_);
+ headers.erase("partial");
+
+ ListCodec::encode(list_, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+ QPID_LOG(trace, "SENT QueryResponse (query by object_id) to=" << rte << "/" << rtk);
+ return;
+ }
+ } else { // match using schema_id, if supplied
+
+ string className;
+ string packageName;
+
+ i = inMap.find("_schema_id");
+ if (i != inMap.end() && i->second.getType() == qpid::types::VAR_MAP) {
+ const Variant::Map& schemaIdMap(i->second.asMap());
+
+ Variant::Map::const_iterator s_iter = schemaIdMap.find("_class_name");
+ if (s_iter != schemaIdMap.end() && s_iter->second.getType() == qpid::types::VAR_STRING)
+ className = s_iter->second.asString();
+
+ s_iter = schemaIdMap.find("_package_name");
+ if (s_iter != schemaIdMap.end() && s_iter->second.getType() == qpid::types::VAR_STRING)
+ packageName = s_iter->second.asString();
+
+ typedef list<boost::shared_ptr<ManagementObject> > StageList;
+ StageList staging;
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ for (ObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second.get();
+ if (object->getClassName() == className &&
+ (packageName.empty() || object->getPackageName() == packageName))
+ staging.push_back(iter->second);
+ }
+ }
+
+ unsigned int objCount = 0;
+ for (StageList::iterator iter = staging.begin(); iter != staging.end(); iter++) {
+ ManagementObject* object = iter->get();
+ if (object->getClassName() == className &&
+ (packageName.empty() || object->getPackageName() == packageName)) {
+
+ values.clear();
+ oidMap.clear();
+ map_.clear();
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ object->mapEncodeValues(values, true, true); // write both stats and properties
+ object->getObjectId().mapEncode(oidMap);
+ map_["_values"] = values;
+ map_["_object_id"] = oidMap;
+ object->writeTimestamps(map_);
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ object->getMd5Sum());
+ list_.push_back(map_);
+
+ if (++objCount >= maxV2ReplyObjs) {
+ objCount = 0;
+ ListCodec::encode(list_, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+ QPID_LOG(trace, "SENT QueryResponse (query by schema_id) to=" << rte << "/" << rtk);
+ content.clear();
+ list_.clear();
+ }
+ }
+ }
+ }
+ }
+
+ // Send last "non-partial" message to indicate CommandComplete
+ headers.erase("partial");
+ ListCodec::encode(list_, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+ QPID_LOG(trace, "SENT QueryResponse (last message, no 'partial' indicator) to=" << rte << "/" << rtk);
+
+ } else if (i->second.asString() == "SCHEMA_ID") {
+ headers["qmf.content"] = "_schema_id";
+ /**
+ * @todo - support for a predicate. For now, send a list of all known schema class keys.
+ */
+ for (PackageMap::iterator pIter = packages.begin();
+ pIter != packages.end(); pIter++) {
+ for (ClassMap::iterator cIter = pIter->second.begin();
+ cIter != pIter->second.end(); cIter++) {
+
+ list_.push_back(mapEncodeSchemaId( pIter->first,
+ cIter->first.name,
+ cIter->first.hash,
+ cIter->second.kind ));
+ }
+ }
+
+ headers.erase("partial");
+ ListCodec::encode(list_, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk, "amqp/list");
+ QPID_LOG(trace, "SENT QueryResponse (SchemaId) to=" << rte << "/" << rtk);
+
+ } else {
+ // Unknown query target
+ sendException(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
+ }
+}
+
+void ManagementAgentImpl::handleLocateRequest(const string&, const string& cid, const string& rte, const string& rtk)
+{
+ QPID_LOG(trace, "RCVD AgentLocateRequest");
+
+ Variant::Map map;
+ Variant::Map headers;
+ string content;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_agent_locate_response";
+ headers["qmf.agent"] = name_address;
+
+ getHeartbeatContent(map);
+ MapCodec::encode(map, content);
+ connThreadBody.sendBuffer(content, cid, headers, rte, rtk);
+
+ QPID_LOG(trace, "SENT AgentLocateResponse replyTo=" << rte << "/" << rtk);
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ publishAllData = true;
+ }
+}
+
+void ManagementAgentImpl::handleMethodRequest(const string& body, const string& cid, const string& rte, const string& rtk, const string& userId)
+{
+ if (extThread) {
+ sys::Mutex::ScopedLock lock(agentLock);
+
+ methodQueue.push_back(new QueuedMethod(cid, rte, rtk, body, userId));
+ if (pipeHandle != 0) {
+ pipeHandle->write("X", 1);
+ } else if (notifyable != 0) {
+ inCallback = true;
+ {
+ sys::Mutex::ScopedUnlock unlock(agentLock);
+ notifyable->notify();
+ }
+ inCallback = false;
+ } else if (notifyCallback != 0) {
+ inCallback = true;
+ {
+ sys::Mutex::ScopedUnlock unlock(agentLock);
+ notifyCallback(notifyContext);
+ }
+ inCallback = false;
+ }
+ } else {
+ invokeMethodRequest(body, cid, rte, rtk, userId);
+ }
+
+ QPID_LOG(trace, "RCVD MethodRequest");
+}
+
+void ManagementAgentImpl::received(Message& msg)
+{
+ string replyToExchange;
+ string replyToKey;
+ framing::MessageProperties mp = msg.getMessageProperties();
+ if (mp.hasReplyTo()) {
+ const framing::ReplyTo& rt = mp.getReplyTo();
+ replyToExchange = rt.getExchange();
+ replyToKey = rt.getRoutingKey();
+ }
+
+ string userId;
+ if (mp.hasUserId())
+ userId = mp.getUserId();
+
+ if (mp.hasAppId() && mp.getAppId() == "qmf2")
+ {
+ string opcode = mp.getApplicationHeaders().getAsString("qmf.opcode");
+ string cid = msg.getMessageProperties().getCorrelationId();
+
+ if (opcode == "_agent_locate_request") handleLocateRequest(msg.getData(), cid, replyToExchange, replyToKey);
+ else if (opcode == "_method_request") handleMethodRequest(msg.getData(), cid, replyToExchange, replyToKey, userId);
+ else if (opcode == "_query_request") handleGetQuery(msg.getData(), cid, replyToExchange, replyToKey);
+ else {
+ QPID_LOG(warning, "Support for QMF V2 Opcode [" << opcode << "] TBD!!!");
+ }
+ return;
+ }
+
+ // old preV2 binary messages
+
+ uint32_t sequence;
+ string data = msg.getData();
+ Buffer inBuffer(const_cast<char*>(data.c_str()), data.size());
+ uint8_t opcode;
+
+
+ if (checkHeader(inBuffer, &opcode, &sequence))
+ {
+ if (opcode == 'S') handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey);
+ else if (opcode == 'x') handleConsoleAddedIndication();
+ else
+ QPID_LOG(warning, "Ignoring old-format QMF Request! opcode=" << char(opcode));
+ }
+}
+
+
+void ManagementAgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq)
+{
+ buf.putOctet('A');
+ buf.putOctet('M');
+ buf.putOctet('2');
+ buf.putOctet(opcode);
+ buf.putLong (seq);
+}
+
+Variant::Map ManagementAgentImpl::mapEncodeSchemaId(const string& pname,
+ const string& cname,
+ const uint8_t *md5Sum,
+ uint8_t type)
+{
+ Variant::Map map_;
+
+ map_["_package_name"] = pname;
+ map_["_class_name"] = cname;
+ map_["_hash"] = types::Uuid(md5Sum);
+ if (type == ManagementItem::CLASS_KIND_EVENT)
+ map_["_type"] = "_event";
+ else
+ map_["_type"] = "_data";
+
+ return map_;
+}
+
+
+bool ManagementAgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq)
+{
+ if (buf.getSize() < 8)
+ return false;
+
+ uint8_t h1 = buf.getOctet();
+ uint8_t h2 = buf.getOctet();
+ uint8_t h3 = buf.getOctet();
+
+ *opcode = buf.getOctet();
+ *seq = buf.getLong();
+
+ return h1 == 'A' && h2 == 'M' && h3 == '2';
+}
+
+ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(const string& name)
+{
+ PackageMap::iterator pIter = packages.find(name);
+ if (pIter != packages.end())
+ return pIter;
+
+ // No such package found, create a new map entry.
+ pair<PackageMap::iterator, bool> result =
+ packages.insert(pair<string, ClassMap>(name, ClassMap()));
+
+ return result.first;
+}
+
+void ManagementAgentImpl::moveNewObjectsLH()
+{
+ sys::Mutex::ScopedLock lock(addLock);
+ for (ObjectMap::iterator iter = newManagementObjects.begin();
+ iter != newManagementObjects.end();
+ iter++)
+ managementObjects[iter->first] = iter->second;
+ newManagementObjects.clear();
+}
+
+void ManagementAgentImpl::addClassLocal(uint8_t classKind,
+ PackageMap::iterator pIter,
+ const string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ SchemaClassKey key;
+ ClassMap& cMap = pIter->second;
+
+ key.name = className;
+ memcpy(&key.hash, md5Sum, 16);
+
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end())
+ return;
+
+ // No such class found, create a new class with local information.
+ cMap.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(schemaCall, classKind)));
+ schemaTimestamp = Duration(EPOCH, now());
+ QPID_LOG(trace, "Updated schema timestamp, now=" << uint64_t(schemaTimestamp));
+}
+
+void ManagementAgentImpl::encodePackageIndication(Buffer& buf,
+ PackageMap::iterator pIter)
+{
+ buf.putShortString((*pIter).first);
+
+ QPID_LOG(trace, "SENT PackageInd: package=" << (*pIter).first);
+}
+
+void ManagementAgentImpl::encodeClassIndication(Buffer& buf,
+ PackageMap::iterator pIter,
+ ClassMap::iterator cIter)
+{
+ SchemaClassKey key = (*cIter).first;
+
+ buf.putOctet((*cIter).second.kind);
+ buf.putShortString((*pIter).first);
+ buf.putShortString(key.name);
+ buf.putBin128(key.hash);
+
+ QPID_LOG(trace, "SENT ClassInd: package=" << (*pIter).first << " class=" << key.name);
+}
+
+struct MessageItem {
+ string content;
+ Variant::Map headers;
+ string key;
+ MessageItem(const Variant::Map& h, const string& k) : headers(h), key(k) {}
+};
+
+void ManagementAgentImpl::periodicProcessing()
+{
+ string addr_key_base = "agent.ind.data.";
+ list<ObjectId> deleteList;
+ list<boost::shared_ptr<MessageItem> > message_list;
+
+ sendHeartbeat();
+
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+
+ if (!connected)
+ return;
+
+ moveNewObjectsLH();
+
+ //
+ // Clear the been-here flag on all objects in the map.
+ //
+ for (ObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second.get();
+ object->setFlags(0);
+ if (publishAllData) {
+ object->setForcePublish(true);
+ }
+ }
+
+ publishAllData = false;
+
+ //
+ // Process the entire object map.
+ //
+ uint32_t v2Objs = 0;
+
+ for (ObjectMap::iterator baseIter = managementObjects.begin();
+ baseIter != managementObjects.end();
+ baseIter++) {
+ ManagementObject* baseObject = baseIter->second.get();
+
+ //
+ // Skip until we find a base object requiring a sent message.
+ //
+ if (baseObject->getFlags() == 1 ||
+ (!baseObject->getConfigChanged() &&
+ !baseObject->getInstChanged() &&
+ !baseObject->getForcePublish() &&
+ !baseObject->isDeleted()))
+ continue;
+
+ std::string packageName = baseObject->getPackageName();
+ std::string className = baseObject->getClassName();
+
+ Variant::List list_;
+ std::stringstream addr_key;
+ Variant::Map headers;
+
+ addr_key << addr_key_base;
+ addr_key << keyifyNameStr(packageName)
+ << "." << keyifyNameStr(className)
+ << "." << vendorNameKey
+ << "." << productNameKey
+ << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ for (ObjectMap::iterator iter = baseIter;
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second.get();
+ bool send_stats, send_props;
+ if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
+ object->setFlags(1);
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ send_props = (object->getConfigChanged() || object->getForcePublish() || object->isDeleted());
+ send_stats = (object->hasInst() && (object->getInstChanged() || object->getForcePublish()));
+
+ if (send_stats || send_props) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oid;
+
+ object->getObjectId().mapEncode(oid);
+ map_["_object_id"] = oid;
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ object->getMd5Sum());
+ object->writeTimestamps(map_);
+ object->mapEncodeValues(values, send_props, send_stats);
+ map_["_values"] = values;
+ list_.push_back(map_);
+
+ if (++v2Objs >= maxV2ReplyObjs) {
+ v2Objs = 0;
+ boost::shared_ptr<MessageItem> item(new MessageItem(headers, addr_key.str()));
+ ListCodec::encode(list_, item->content);
+ message_list.push_back(item);
+ list_.clear();
+ }
+ }
+
+ if (object->isDeleted())
+ deleteList.push_back(iter->first);
+ object->setForcePublish(false);
+ }
+ }
+
+ if (!list_.empty()) {
+ boost::shared_ptr<MessageItem> item(new MessageItem(headers, addr_key.str()));
+ ListCodec::encode(list_, item->content);
+ message_list.push_back(item);
+ }
+ }
+
+ // Delete flagged objects
+ for (list<ObjectId>::reverse_iterator iter = deleteList.rbegin();
+ iter != deleteList.rend();
+ iter++)
+ managementObjects.erase(*iter);
+ }
+
+ while (!message_list.empty()) {
+ boost::shared_ptr<MessageItem> item(message_list.front());
+ message_list.pop_front();
+ connThreadBody.sendBuffer(item->content, "", item->headers, topicExchange, item->key, "amqp/list");
+ QPID_LOG(trace, "SENT DataIndication");
+ }
+}
+
+
+void ManagementAgentImpl::getHeartbeatContent(qpid::types::Variant::Map& map)
+{
+ map["_values"] = attrMap;
+ map["_values"].asMap()["_timestamp"] = uint64_t(Duration(EPOCH, now()));
+ map["_values"].asMap()["_heartbeat_interval"] = interval;
+ map["_values"].asMap()["_epoch"] = bootSequence;
+ map["_values"].asMap()["_schema_updated"] = uint64_t(schemaTimestamp);
+}
+
+void ManagementAgentImpl::ConnectionThread::run()
+{
+ static const int delayMin(1);
+ static const int delayMax(128);
+ static const int delayFactor(2);
+ int delay(delayMin);
+ string dest("qmfagent");
+ ConnectionThread::shared_ptr tmp;
+
+ sessionId.generate();
+ queueName << "qmfagent-" << sessionId;
+
+ while (true) {
+ try {
+ if (agent.initialized) {
+ QPID_LOG(debug, "QMF Agent attempting to connect to the broker...");
+ connection.open(agent.connectionSettings);
+ session = connection.newSession(queueName.str());
+ subscriptions.reset(new client::SubscriptionManager(session));
+
+ session.queueDeclare(arg::queue=queueName.str(), arg::autoDelete=true,
+ arg::exclusive=true);
+ session.exchangeBind(arg::exchange="amq.direct", arg::queue=queueName.str(),
+ arg::bindingKey=queueName.str());
+ session.exchangeBind(arg::exchange=agent.directExchange, arg::queue=queueName.str(),
+ arg::bindingKey=agent.name_address);
+ session.exchangeBind(arg::exchange=agent.topicExchange, arg::queue=queueName.str(),
+ arg::bindingKey="console.#");
+
+ subscriptions->subscribe(agent, queueName.str(), dest);
+ QPID_LOG(info, "Connection established with broker");
+ {
+ sys::Mutex::ScopedLock _lock(connLock);
+ if (shutdown)
+ return;
+ operational = true;
+ agent.connected = true;
+ agent.startProtocol();
+ try {
+ sys::Mutex::ScopedUnlock _unlock(connLock);
+ subscriptions->run();
+ } catch (exception) {}
+
+ QPID_LOG(warning, "Connection to the broker has been lost");
+
+ operational = false;
+ agent.connected = false;
+ tmp = subscriptions;
+ subscriptions.reset();
+ }
+ tmp.reset(); // frees the subscription outside the lock
+ delay = delayMin;
+ connection.close();
+ }
+ } catch (exception &e) {
+ if (delay < delayMax)
+ delay *= delayFactor;
+ QPID_LOG(debug, "Connection failed: exception=" << e.what());
+ }
+
+ {
+ // sleep for "delay" seconds, but peridically check if the
+ // agent is shutting down so we don't hang for up to delayMax
+ // seconds during agent shutdown
+ sys::Mutex::ScopedLock _lock(connLock);
+ if (shutdown)
+ return;
+ sleeping = true;
+ int totalSleep = 0;
+ do {
+ sys::Mutex::ScopedUnlock _unlock(connLock);
+ ::sleep(delayMin);
+ totalSleep += delayMin;
+ } while (totalSleep < delay && !shutdown);
+ sleeping = false;
+ if (shutdown)
+ return;
+ }
+ }
+}
+
+ManagementAgentImpl::ConnectionThread::~ConnectionThread()
+{
+}
+
+void ManagementAgentImpl::ConnectionThread::sendBuffer(Buffer& buf,
+ uint32_t length,
+ const string& exchange,
+ const string& routingKey)
+{
+ Message msg;
+ string data;
+
+ buf.getRawData(data, length);
+ msg.setData(data);
+ sendMessage(msg, exchange, routingKey);
+}
+
+
+
+void ManagementAgentImpl::ConnectionThread::sendBuffer(const string& data,
+ const string& cid,
+ const Variant::Map headers,
+ const string& exchange,
+ const string& routingKey,
+ const string& contentType,
+ uint64_t ttl_msec)
+{
+ Message msg;
+ Variant::Map::const_iterator i;
+
+ if (!cid.empty())
+ msg.getMessageProperties().setCorrelationId(cid);
+
+ if (!contentType.empty())
+ msg.getMessageProperties().setContentType(contentType);
+
+ if (ttl_msec)
+ msg.getDeliveryProperties().setTtl(ttl_msec);
+
+ for (i = headers.begin(); i != headers.end(); ++i) {
+ msg.getHeaders().setString(i->first, i->second.asString());
+ }
+
+ msg.setData(data);
+ sendMessage(msg, exchange, routingKey);
+}
+
+
+
+
+
+void ManagementAgentImpl::ConnectionThread::sendMessage(Message msg,
+ const string& exchange,
+ const string& routingKey)
+{
+ ConnectionThread::shared_ptr s;
+ {
+ sys::Mutex::ScopedLock _lock(connLock);
+ if (!operational)
+ return;
+ s = subscriptions;
+ }
+
+ msg.getDeliveryProperties().setRoutingKey(routingKey);
+ msg.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str()));
+ msg.getMessageProperties().getApplicationHeaders().setString("qmf.agent", agent.name_address);
+ msg.getMessageProperties().setAppId("qmf2");
+ try {
+ session.messageTransfer(arg::content=msg, arg::destination=exchange);
+ } catch(exception& e) {
+ QPID_LOG(error, "Exception caught in sendMessage: " << e.what());
+ // Bounce the connection
+ if (s)
+ s->stop();
+ }
+}
+
+
+
+void ManagementAgentImpl::ConnectionThread::bindToBank(uint32_t brokerBank, uint32_t agentBank)
+{
+ stringstream key;
+ key << "agent." << brokerBank << "." << agentBank;
+ session.exchangeBind(arg::exchange="qpid.management", arg::queue=queueName.str(),
+ arg::bindingKey=key.str());
+}
+
+void ManagementAgentImpl::ConnectionThread::close()
+{
+ ConnectionThread::shared_ptr s;
+ {
+ sys::Mutex::ScopedLock _lock(connLock);
+ shutdown = true;
+ s = subscriptions;
+ }
+ if (s)
+ s->stop();
+}
+
+bool ManagementAgentImpl::ConnectionThread::isSleeping() const
+{
+ sys::Mutex::ScopedLock _lock(connLock);
+ return sleeping;
+}
+
+
+void ManagementAgentImpl::PublishThread::run()
+{
+ uint16_t totalSleep;
+
+ while (!shutdown) {
+ agent.periodicProcessing();
+ totalSleep = 0;
+ while (totalSleep++ < agent.getInterval() && !shutdown) {
+ ::sleep(1);
+ }
+ }
+}
diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h
new file mode 100644
index 0000000000..bf340777d1
--- /dev/null
+++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.h
@@ -0,0 +1,298 @@
+#ifndef _qpid_agent_ManagementAgentImpl_
+#define _qpid_agent_ManagementAgentImpl_
+
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include "qpid/agent/ManagementAgent.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/PipeHandle.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/Uuid.h"
+#include <iostream>
+#include <sstream>
+#include <deque>
+
+namespace qpid {
+namespace management {
+
+class ManagementAgentImpl : public ManagementAgent, public client::MessageListener
+{
+ public:
+
+ ManagementAgentImpl();
+ virtual ~ManagementAgentImpl();
+
+ //
+ // Methods from ManagementAgent
+ //
+ int getMaxThreads() { return 1; }
+ void setName(const std::string& vendor,
+ const std::string& product,
+ const std::string& instance="");
+ void getName(std::string& vendor, std::string& product, std::string& instance);
+ const std::string& getAddress();
+ void init(const std::string& brokerHost = "localhost",
+ uint16_t brokerPort = 5672,
+ uint16_t intervalSeconds = 10,
+ bool useExternalThread = false,
+ const std::string& storeFile = "",
+ const std::string& uid = "guest",
+ const std::string& pwd = "guest",
+ const std::string& mech = "PLAIN",
+ const std::string& proto = "tcp");
+ void init(const management::ConnectionSettings& settings,
+ uint16_t intervalSeconds = 10,
+ bool useExternalThread = false,
+ const std::string& storeFile = "");
+ bool isConnected() { return connected; }
+ std::string& getLastFailure() { return lastFailure; }
+ void registerClass(const std::string& packageName,
+ const std::string& className,
+ uint8_t* md5Sum,
+ management::ManagementObject::writeSchemaCall_t schemaCall);
+ void registerEvent(const std::string& packageName,
+ const std::string& eventName,
+ uint8_t* md5Sum,
+ management::ManagementObject::writeSchemaCall_t schemaCall);
+ ObjectId addObject(management::ManagementObject* objectPtr, uint64_t persistId = 0);
+ ObjectId addObject(management::ManagementObject* objectPtr, const std::string& key,
+ bool persistent);
+ void raiseEvent(const management::ManagementEvent& event, severity_t severity = SEV_DEFAULT);
+ uint32_t pollCallbacks(uint32_t callLimit = 0);
+ int getSignalFd();
+ void setSignalCallback(cb_t callback, void* context);
+ void setSignalCallback(Notifyable& n);
+
+ uint16_t getInterval() { return interval; }
+ void periodicProcessing();
+
+ // these next are here to support the hot-wiring of state between clustered brokers
+ uint64_t getNextObjectId(void) { return nextObjectId; }
+ void setNextObjectId(uint64_t o) { nextObjectId = o; }
+
+ uint16_t getBootSequence(void) { return bootSequence; }
+ void setBootSequence(uint16_t b) { bootSequence = b; }
+
+ private:
+
+ struct SchemaClassKey {
+ std::string name;
+ uint8_t hash[16];
+ };
+
+ struct SchemaClassKeyComp {
+ bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const
+ {
+ if (lhs.name != rhs.name)
+ return lhs.name < rhs.name;
+ else
+ for (int i = 0; i < 16; i++)
+ if (lhs.hash[i] != rhs.hash[i])
+ return lhs.hash[i] < rhs.hash[i];
+ return false;
+ }
+ };
+
+ struct SchemaClass {
+ management::ManagementObject::writeSchemaCall_t writeSchemaCall;
+ uint8_t kind;
+
+ SchemaClass(const management::ManagementObject::writeSchemaCall_t call,
+ const uint8_t _kind) : writeSchemaCall(call), kind(_kind) {}
+ };
+
+ struct QueuedMethod {
+ QueuedMethod(const std::string& _cid, const std::string& _rte, const std::string& _rtk, const std::string& _body, const std::string& _uid) :
+ cid(_cid), replyToExchange(_rte), replyToKey(_rtk), body(_body), userId(_uid) {}
+
+ std::string cid;
+ std::string replyToExchange;
+ std::string replyToKey;
+ std::string body;
+ std::string userId;
+ };
+
+ typedef std::deque<QueuedMethod*> MethodQueue;
+ typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap;
+ typedef std::map<std::string, ClassMap> PackageMap;
+
+ PackageMap packages;
+ AgentAttachment attachment;
+
+ typedef std::map<ObjectId, boost::shared_ptr<ManagementObject> > ObjectMap;
+
+ ObjectMap managementObjects;
+ ObjectMap newManagementObjects;
+ MethodQueue methodQueue;
+
+ void received (client::Message& msg);
+
+ qpid::types::Variant::Map attrMap;
+ std::string name_address;
+ std::string vendorNameKey; // vendor name with "." --> "_"
+ std::string productNameKey; // product name with "." --> "_"
+ std::string instanceNameKey; // agent instance with "." --> "_"
+ uint16_t interval;
+ bool extThread;
+ sys::PipeHandle* pipeHandle;
+ uint64_t nextObjectId;
+ cb_t notifyCallback;
+ void* notifyContext;
+ Notifyable* notifyable;
+ bool inCallback;
+ std::string storeFile;
+ sys::Mutex agentLock;
+ sys::Mutex addLock;
+ framing::Uuid systemId;
+ client::ConnectionSettings connectionSettings;
+ bool initialized;
+ bool connected;
+ bool useMapMsg;
+ std::string lastFailure;
+ std::string topicExchange;
+ std::string directExchange;
+ qpid::sys::Duration schemaTimestamp;
+
+ bool publishAllData;
+ uint32_t requestedBrokerBank;
+ uint32_t requestedAgentBank;
+ uint32_t assignedBrokerBank;
+ uint32_t assignedAgentBank;
+ uint16_t bootSequence;
+
+ // Maximum # of objects allowed in a single V2 response
+ // message.
+ uint32_t maxV2ReplyObjs;
+
+ static const uint8_t DEBUG_OFF = 0;
+ static const uint8_t DEBUG_CONN = 1;
+ static const uint8_t DEBUG_PROTO = 2;
+ static const uint8_t DEBUG_PUBLISH = 3;
+
+# define MA_BUFFER_SIZE 65536
+ char outputBuffer[MA_BUFFER_SIZE];
+ char eventBuffer[MA_BUFFER_SIZE];
+
+ friend class ConnectionThread;
+ class ConnectionThread : public sys::Runnable
+ {
+ typedef boost::shared_ptr<client::SubscriptionManager> shared_ptr;
+
+ bool operational;
+ ManagementAgentImpl& agent;
+ framing::Uuid sessionId;
+ client::Connection connection;
+ client::Session session;
+ ConnectionThread::shared_ptr subscriptions;
+ std::stringstream queueName;
+ mutable sys::Mutex connLock;
+ bool shutdown;
+ bool sleeping;
+ void run();
+ public:
+ ConnectionThread(ManagementAgentImpl& _agent) :
+ operational(false), agent(_agent),
+ shutdown(false), sleeping(false) {}
+ ~ConnectionThread();
+ void sendBuffer(qpid::framing::Buffer& buf,
+ uint32_t length,
+ const std::string& exchange,
+ const std::string& routingKey);
+ void sendBuffer(const std::string& data,
+ const std::string& cid,
+ const qpid::types::Variant::Map headers,
+ const std::string& exchange,
+ const std::string& routingKey,
+ const std::string& contentType="amqp/map",
+ uint64_t ttl_msec=0);
+ void sendMessage(qpid::client::Message msg,
+ const std::string& exchange,
+ const std::string& routingKey);
+ void bindToBank(uint32_t brokerBank, uint32_t agentBank);
+ void close();
+ bool isSleeping() const;
+ };
+
+ class PublishThread : public sys::Runnable
+ {
+ ManagementAgentImpl& agent;
+ void run();
+ bool shutdown;
+ public:
+ PublishThread(ManagementAgentImpl& _agent) :
+ agent(_agent), shutdown(false) {}
+ void close() { shutdown = true; }
+ };
+
+ ConnectionThread connThreadBody;
+ sys::Thread connThread;
+ PublishThread pubThreadBody;
+ sys::Thread pubThread;
+
+ static const std::string storeMagicNumber;
+
+ void startProtocol();
+ void storeData(bool requested=false);
+ void retrieveData(std::string& vendor, std::string& product, std::string& inst);
+ PackageMap::iterator findOrAddPackage(const std::string& name);
+ void moveNewObjectsLH();
+ void addClassLocal (uint8_t classKind,
+ PackageMap::iterator pIter,
+ const std::string& className,
+ uint8_t* md5Sum,
+ management::ManagementObject::writeSchemaCall_t schemaCall);
+ void encodePackageIndication (framing::Buffer& buf,
+ PackageMap::iterator pIter);
+ void encodeClassIndication (framing::Buffer& buf,
+ PackageMap::iterator pIter,
+ ClassMap::iterator cIter);
+ void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
+ qpid::types::Variant::Map mapEncodeSchemaId(const std::string& pname,
+ const std::string& cname,
+ const uint8_t *md5Sum,
+ uint8_t type=ManagementItem::CLASS_KIND_TABLE);
+ bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
+ void sendHeartbeat();
+ void sendException(const std::string& replyToExchange, const std::string& replyToKey, const std::string& cid,
+ const std::string& text, uint32_t code=1);
+ void handlePackageRequest (qpid::framing::Buffer& inBuffer);
+ void handleClassQuery (qpid::framing::Buffer& inBuffer);
+ void handleSchemaRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence, const std::string& rte, const std::string& rtk);
+ void invokeMethodRequest (const std::string& body, const std::string& cid, const std::string& rte, const std::string& rtk, const std::string& userId);
+
+ void handleGetQuery (const std::string& body, const std::string& cid, const std::string& rte, const std::string& rtk);
+ void handleLocateRequest (const std::string& body, const std::string& sequence, const std::string& rte, const std::string& rtk);
+ void handleMethodRequest (const std::string& body, const std::string& sequence, const std::string& rte, const std::string& rtk, const std::string& userId);
+ void handleConsoleAddedIndication();
+ void getHeartbeatContent (qpid::types::Variant::Map& map);
+};
+
+}}
+
+#endif /*!_qpid_agent_ManagementAgentImpl_*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Array.cpp b/qpid/cpp/src/qpid/amqp_0_10/Array.cpp
new file mode 100644
index 0000000000..2ee47546f2
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Array.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Array.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+std::ostream& operator<<(std::ostream& o, const Array& a) {
+ std::ostream_iterator<UnknownType> i(o, " ");
+ o << "Array<" << typeName(a.getType()) << "[";
+ std::copy(a.begin(), a.end(), i);
+ o << "]";
+ return o;
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Array.h b/qpid/cpp/src/qpid/amqp_0_10/Array.h
new file mode 100644
index 0000000000..6e8a419df7
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Array.h
@@ -0,0 +1,124 @@
+#ifndef QPID_AMQP_0_10_ARRAY_H
+#define QPID_AMQP_0_10_ARRAY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/TypeForCode.h"
+#include "qpid/amqp_0_10/CodeForType.h"
+#include "qpid/amqp_0_10/UnknownType.h"
+#include "qpid/amqp_0_10/exceptions.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include <vector>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+template <class T> class ArrayDomain : public std::vector<T> {
+ public:
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ template <class S> void encode(S& s) const {
+ s(contentSize())(CodeForType<T>::value)(uint32_t(this->size()));
+ s(this->begin(), this->end());
+ }
+
+ void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); }
+
+ template <class S> void decode(S& s) {
+ uint32_t size; uint8_t type; uint32_t count;
+ s(size);
+ typename S::ScopedLimit l(s, size);
+ s(type);
+ if (type != CodeForType<T>::value)
+ throw InvalidArgumentException(QPID_MSG("Array domain expected type " << CodeForType<T>::value << " but found " << type));
+ s(count);
+ this->resize(count);
+ s(this->begin(), this->end());
+ }
+
+ private:
+ uint32_t contentSize() const {
+ return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/;
+ }
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream& o, const ArrayDomain<T>& ad) {
+ std::ostream_iterator<T> i(o, " ");
+ o << "Array<" << typeName(CodeForType<T>::value) << ">[";
+ std::copy(ad.begin(), ad.end(), i);
+ o << "]";
+ return o;
+}
+
+/** A non-domain array is represented as and array of UnknownType.
+ * Special case templat.
+ */
+template<> class ArrayDomain<UnknownType> : public std::vector<UnknownType> {
+ public:
+ ArrayDomain(uint8_t type_=0) : type(type_) {}
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ template <class S> void encode(S& s) const {
+ s(contentSize())(type)(uint32_t(this->size()));
+ s(this->begin(), this->end());
+ }
+
+ void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); }
+
+ template <class S> void decode(S& s) {
+ uint32_t size; uint32_t count;
+ s(size);
+ typename S::ScopedLimit l(s, size);
+ s(type)(count);
+ this->clear();
+ this->resize(count, UnknownType(type));
+ s(this->begin(), this->end());
+ }
+
+ uint8_t getType() const { return type; }
+
+ private:
+ uint32_t contentSize() const {
+ return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/;
+ }
+ uint8_t type;
+};
+
+std::ostream& operator<<(std::ostream& o, const Array& a);
+
+// FIXME aconway 2008-04-08: hack to supress encoding of
+// command-fragments and in-doubt as there is a problem with the spec
+// (command-fragments does not have a one byte type code.)
+namespace session { class CommandFragment; }
+namespace dtx { class Xid; }
+
+template <> struct ArrayDomain<session::CommandFragment> : public Void {};
+template <> struct ArrayDomain<dtx::Xid> : public Void {};
+inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<session::CommandFragment>&) { return o; }
+inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<dtx::Xid>&) { return o; }
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_ARRAY_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Body.h b/qpid/cpp/src/qpid/amqp_0_10/Body.h
new file mode 100644
index 0000000000..c96931551c
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Body.h
@@ -0,0 +1,55 @@
+#ifndef QPID_AMQP_0_10_BODY_H
+#define QPID_AMQP_0_10_BODY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Holds data from a body frame. */
+class Body {
+ public:
+ Body() {}
+ Body(size_t size_) : str(size_, '\0') {}
+ Body(const char* data_, size_t size_) : str(data_, size_) {}
+
+ size_t size() const { return str.size(); };
+ const char* data() const { return str.data(); }
+ char* data() { return const_cast<char*>(str.data()); }
+
+ template <class S> void serialize(S& s) { s.raw(data(), size()); }
+
+ private:
+ std::string str;
+
+ friend std::ostream& operator<<(std::ostream&, const Body&);
+};
+
+inline std::ostream& operator<<(std::ostream& o, const Body& b) {
+ return o << b.str.substr(0, 16) << "... (" << b.size() << ")";
+}
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_BODY_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codec.h b/qpid/cpp/src/qpid/amqp_0_10/Codec.h
new file mode 100644
index 0000000000..fd006a348e
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Codec.h
@@ -0,0 +1,213 @@
+#ifndef QPID_AMQP_0_10_CODEC_H
+#define QPID_AMQP_0_10_CODEC_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/built_in_types.h"
+#include "qpid/Serializer.h"
+#include <boost/type_traits/is_integral.hpp>
+#include <boost/type_traits/is_float.hpp>
+#include <boost/type_traits/is_arithmetic.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/static_assert.hpp>
+#include <iterator>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+template <class T> void reverse(T& t) {
+ char*p =reinterpret_cast<char*>(&t);
+ std::reverse(p, p+sizeof(T));
+}
+
+#ifdef BOOST_LITTLE_ENDIAN
+template <class T> void bigEndian(T& t) { reverse(t); }
+template <class T> void littleEndian(T&) {}
+#else
+template <class T> void littleEndian(T& t) { reverse(t); }
+template <class T> void bigEndian(T&) {}
+#endif
+
+/**
+ * AMQP 0-10 encoding and decoding.
+ */
+struct Codec {
+ /** Encode to an output byte iterator */
+ template <class OutIter>
+ class Encoder : public EncoderBase<Encoder<OutIter> >
+ {
+ public:
+ typedef EncoderBase<Encoder<OutIter> > Base;
+ typedef OutIter Iterator;
+
+ Encoder(OutIter o, size_t limit=Base::maxLimit()) : out(o) {
+ this->setLimit(limit);
+ }
+
+ using EncoderBase<Encoder<OutIter> >::operator();
+
+ Encoder& operator()(bool x) { raw(x); return *this;}
+ Encoder& operator()(char x) { raw(x); return *this; }
+ Encoder& operator()(int8_t x) { raw(x); return *this; }
+ Encoder& operator()(uint8_t x) { raw(x); return *this; }
+
+ Encoder& operator()(int16_t x) { return networkByteOrder(x); }
+ Encoder& operator()(int32_t x) { return networkByteOrder(x); }
+ Encoder& operator()(int64_t x) { return networkByteOrder(x); }
+
+ Encoder& operator()(uint16_t x) { return networkByteOrder(x); }
+ Encoder& operator()(uint32_t x) { return networkByteOrder(x); }
+ Encoder& operator()(uint64_t x) { return networkByteOrder(x); }
+
+ Encoder& operator()(float x) { return networkByteOrder(x); }
+ Encoder& operator()(double x) { return networkByteOrder(x); }
+
+ void raw(const void* p, size_t n) {
+ this->addBytes(n);
+ out = std::copy((const char*)p, (const char*)p+n, out);
+ }
+
+ void raw(char b) { this->addBytes(1); *out++=b; }
+
+ template <class T> Encoder& littleEnd(T x) {
+ littleEndian(x); raw(&x, sizeof(x)); return *this;
+ }
+
+ OutIter pos() const { return out; }
+
+ private:
+
+ template <class T> Encoder& networkByteOrder(T x) {
+ bigEndian(x); raw(&x, sizeof(x)); return *this;
+ }
+
+ OutIter out;
+ };
+
+ template <class InIter>
+ class Decoder : public DecoderBase<Decoder<InIter> > {
+ public:
+ typedef DecoderBase<Decoder<InIter> > Base;
+ typedef InIter Iterator;
+
+ Decoder(InIter i, size_t limit=Base::maxLimit()) : in(i) {
+ this->setLimit(limit);
+ }
+
+ using DecoderBase<Decoder<InIter> >::operator();
+
+ // FIXME aconway 2008-03-10: wrong encoding, need packing support
+ Decoder& operator()(bool& x) { raw((char&)x); return *this; }
+
+ Decoder& operator()(char& x) { raw((char&)x); return *this; }
+ Decoder& operator()(int8_t& x) { raw((char&)x); return *this; }
+ Decoder& operator()(uint8_t& x) { raw((char&)x); return *this; }
+
+ Decoder& operator()(int16_t& x) { return networkByteOrder(x); }
+ Decoder& operator()(int32_t& x) { return networkByteOrder(x); }
+ Decoder& operator()(int64_t& x) { return networkByteOrder(x); }
+
+ Decoder& operator()(uint16_t& x) { return networkByteOrder(x); }
+ Decoder& operator()(uint32_t& x) { return networkByteOrder(x); }
+ Decoder& operator()(uint64_t& x) { return networkByteOrder(x); }
+
+ Decoder& operator()(float& x) { return networkByteOrder(x); }
+ Decoder& operator()(double& x) { return networkByteOrder(x); }
+
+ void raw(void *p, size_t n) {
+ this->addBytes(n);
+ std::copy(in, in+n, (char*)p);
+ std::advance(in, n);
+ }
+
+ void raw(char &b) { this->addBytes(1); b=*in++; }
+
+ template <class T> Decoder& littleEnd(T& x) {
+ raw(&x, sizeof(x)); littleEndian(x); return *this;
+ }
+
+ InIter pos() const { return in; }
+
+ private:
+
+ template <class T> Decoder& networkByteOrder(T& x) {
+ raw(&x, sizeof(x)); bigEndian(x); return *this;
+ }
+
+ InIter in;
+ };
+
+
+ class Size : public EncoderBase<Size> {
+ public:
+ Size() : size(0) {}
+
+ operator size_t() const { return size; }
+
+ using EncoderBase<Size>::operator();
+
+ // FIXME aconway 2008-03-10: wrong encoding, need packing support
+ Size& operator()(bool x) { size += sizeof(x); return *this; }
+
+ Size& operator()(char x) { size += sizeof(x); return *this; }
+ Size& operator()(int8_t x) { size += sizeof(x); return *this; }
+ Size& operator()(uint8_t x) { size += sizeof(x); return *this; }
+
+ Size& operator()(int16_t x) { size += sizeof(x); return *this; }
+ Size& operator()(int32_t x) { size += sizeof(x); return *this; }
+ Size& operator()(int64_t x) { size += sizeof(x); return *this; }
+
+ Size& operator()(uint16_t x) { size += sizeof(x); return *this; }
+ Size& operator()(uint32_t x) { size += sizeof(x); return *this; }
+ Size& operator()(uint64_t x) { size += sizeof(x); return *this; }
+
+ Size& operator()(float x) { size += sizeof(x); return *this; }
+ Size& operator()(double x) { size += sizeof(x); return *this; }
+
+ // FIXME aconway 2008-04-03: optimize op()(Iter,Iter)
+ // for Iter with fixed-size value_type:
+ // distance(begin,end)*sizeof(value_type)
+
+ void raw(const void*, size_t n){ size += n; }
+
+ template <class T> Size& littleEnd(T) { size+= sizeof(T); return *this; }
+
+ private:
+ size_t size;
+ };
+
+ // FIXME aconway 2008-03-11: rename to encoder(), decoder()
+ template <class InIter> static Decoder<InIter> decode(const InIter &i) {
+ return Decoder<InIter>(i);
+ }
+
+ template <class OutIter> static Encoder<OutIter> encode(OutIter i) {
+ return Encoder<OutIter>(i);
+ }
+
+ template <class T> static size_t size(const T& x) { return Size()(x); }
+ template <class Iter> static size_t size(const Iter& a, const Iter& z) { return Size()(a,z); }
+};
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_CODEC_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp b/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp
new file mode 100644
index 0000000000..b976a5d09b
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Codecs.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/List.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+#include <functional>
+#include <limits>
+
+using namespace qpid::framing;
+using namespace qpid::types;
+
+namespace qpid {
+namespace amqp_0_10 {
+
+namespace {
+const std::string iso885915("iso-8859-15");
+const std::string utf8("utf8");
+const std::string utf16("utf16");
+const std::string binary("binary");
+const std::string amqp0_10_binary("amqp0-10:binary");
+const std::string amqp0_10_bit("amqp0-10:bit");
+const std::string amqp0_10_datetime("amqp0-10:datetime");
+const std::string amqp0_10_struct("amqp0-10:struct");
+}
+
+template <class T, class U, class F> void convert(const T& from, U& to, F f)
+{
+ std::transform(from.begin(), from.end(), std::inserter(to, to.begin()), f);
+}
+
+Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in);
+FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in);
+Variant toVariant(boost::shared_ptr<FieldValue> in);
+boost::shared_ptr<FieldValue> toFieldValue(const Variant& in);
+
+template <class T, class U, class F> void translate(boost::shared_ptr<FieldValue> in, U& u, F f)
+{
+ T t;
+ getEncodedValue<T>(in, t);
+ convert(t, u, f);
+}
+
+template <class T, class U, class F> T* toFieldValueCollection(const U& u, F f)
+{
+ typename T::ValueType t;
+ convert(u, t, f);
+ return new T(t);
+}
+
+FieldTableValue* toFieldTableValue(const Variant::Map& map)
+{
+ FieldTable ft;
+ convert(map, ft, &toFieldTableEntry);
+ return new FieldTableValue(ft);
+}
+
+ListValue* toListValue(const Variant::List& list)
+{
+ List l;
+ convert(list, l, &toFieldValue);
+ return new ListValue(l);
+}
+
+void setEncodingFor(Variant& out, uint8_t code)
+{
+ switch(code){
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ out.setEncoding(amqp0_10_binary);
+ break;
+ case 0x84:
+ case 0x94:
+ out.setEncoding(iso885915);
+ break;
+ case 0x85:
+ case 0x95:
+ out.setEncoding(utf8);
+ break;
+ case 0x86:
+ case 0x96:
+ out.setEncoding(utf16);
+ break;
+ case 0xab:
+ out.setEncoding(amqp0_10_struct);
+ break;
+ default:
+ //do nothing
+ break;
+ }
+}
+
+qpid::types::Uuid getUuid(FieldValue& value)
+{
+ unsigned char data[16];
+ value.getFixedWidthValue<16>(data);
+ return qpid::types::Uuid(data);
+}
+
+Variant toVariant(boost::shared_ptr<FieldValue> in)
+{
+ Variant out;
+ //based on AMQP 0-10 typecode, pick most appropriate variant type
+ switch (in->getType()) {
+ //Fixed Width types:
+ case 0x01: out.setEncoding(amqp0_10_binary);
+ case 0x02: out = in->getIntegerValue<int8_t>(); break;
+ case 0x03: out = in->getIntegerValue<uint8_t>(); break;
+ case 0x04: break; //TODO: iso-8859-15 char
+ case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t>()); break;
+ case 0x10: out.setEncoding(amqp0_10_binary);
+ case 0x11: out = in->getIntegerValue<int16_t, 2>(); break;
+ case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break;
+ case 0x20: out.setEncoding(amqp0_10_binary);
+ case 0x21: out = in->getIntegerValue<int32_t, 4>(); break;
+ case 0x22: out = in->getIntegerValue<uint32_t, 4>(); break;
+ case 0x23: out = in->get<float>(); break;
+ case 0x27: break; //TODO: utf-32 char
+ case 0x30: out.setEncoding(amqp0_10_binary);
+ case 0x31: out = in->getIntegerValue<int64_t, 8>(); break;
+ case 0x38: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding
+ case 0x32: out = in->getIntegerValue<uint64_t, 8>(); break;
+ case 0x33: out = in->get<double>(); break;
+
+ case 0x48: out = getUuid(*in); break;
+
+ //TODO: figure out whether and how to map values with codes 0x40-0xd8
+
+ case 0xf0: break;//void, which is the default value for Variant
+ case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant
+
+ //Variable Width types:
+ //strings:
+ case 0x80:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x90:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0xa0:
+ case 0xab:
+ out = in->get<std::string>();
+ setEncodingFor(out, in->getType());
+ break;
+
+ case 0xa8:
+ out = Variant::Map();
+ translate<FieldTable>(in, out.asMap(), &toVariantMapEntry);
+ break;
+
+ case 0xa9:
+ out = Variant::List();
+ translate<List>(in, out.asList(), &toVariant);
+ break;
+ case 0xaa: //convert amqp0-10 array into variant list
+ out = Variant::List();
+ translate<Array>(in, out.asList(), &toVariant);
+ break;
+
+ default:
+ //error?
+ break;
+ }
+ return out;
+}
+
+boost::shared_ptr<FieldValue> convertString(const std::string& value, const std::string& encoding)
+{
+ bool large = value.size() > std::numeric_limits<uint16_t>::max();
+ if (encoding.empty() || encoding == amqp0_10_binary || encoding == binary) {
+ if (large) {
+ return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0));
+ } else {
+ return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x90));
+ }
+ } else if (encoding == utf8) {
+ if (!large)
+ return boost::shared_ptr<FieldValue>(new Str16Value(value));
+ throw Exception(QPID_MSG("Could not encode utf8 character string - too long (" << value.size() << " bytes)"));
+ } else if (encoding == utf16) {
+ if (!large)
+ return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x96));
+ throw Exception(QPID_MSG("Could not encode utf16 character string - too long (" << value.size() << " bytes)"));
+ } else if (encoding == iso885915) {
+ if (!large)
+ return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x94));
+ throw Exception(QPID_MSG("Could not encode iso-8859-15 character string - too long (" << value.size() << " bytes)"));
+ } else {
+ // the encoding was not recognised
+ QPID_LOG(warning, "Unknown byte encoding: [" << encoding << "], encoding as vbin32.");
+ return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0));
+ }
+}
+
+boost::shared_ptr<FieldValue> toFieldValue(const Variant& in)
+{
+ boost::shared_ptr<FieldValue> out;
+ switch (in.getType()) {
+ case VAR_VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break;
+ case VAR_BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break;
+ case VAR_UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break;
+ case VAR_UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break;
+ case VAR_UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break;
+ case VAR_UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break;
+ case VAR_INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break;
+ case VAR_INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break;
+ case VAR_INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break;
+ case VAR_INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break;
+ case VAR_FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break;
+ case VAR_DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break;
+ case VAR_STRING: out = convertString(in.asString(), in.getEncoding()); break;
+ case VAR_UUID: out = boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data())); break;
+ case VAR_MAP:
+ out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap()));
+ break;
+ case VAR_LIST:
+ out = boost::shared_ptr<FieldValue>(toListValue(in.asList()));
+ break;
+ }
+ return out;
+}
+
+Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in)
+{
+ return Variant::Map::value_type(in.first, toVariant(in.second));
+}
+
+FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in)
+{
+ return FieldTable::value_type(in.first, toFieldValue(in.second));
+}
+
+struct EncodeBuffer
+{
+ char* data;
+ Buffer buffer;
+
+ EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {}
+ ~EncodeBuffer() { delete[] data; }
+
+ template <class T> void encode(T& t) { t.encode(buffer); }
+
+ void getData(std::string& s) {
+ s.assign(data, buffer.getSize());
+ }
+};
+
+struct DecodeBuffer
+{
+ Buffer buffer;
+
+ DecodeBuffer(const std::string& s) : buffer(const_cast<char*>(s.data()), s.size()) {}
+
+ template <class T> void decode(T& t) { t.decode(buffer); }
+
+};
+
+template <class T, class U, class F> void _encode(const U& value, std::string& data, F f)
+{
+ T t;
+ convert(value, t, f);
+ EncodeBuffer buffer(t.encodedSize());
+ buffer.encode(t);
+ buffer.getData(data);
+}
+
+template <class T, class U, class F> void _decode(const std::string& data, U& value, F f)
+{
+ T t;
+ DecodeBuffer buffer(data);
+ buffer.decode(t);
+ convert(t, value, f);
+}
+
+void MapCodec::encode(const Variant::Map& value, std::string& data)
+{
+ _encode<FieldTable>(value, data, &toFieldTableEntry);
+}
+
+void MapCodec::decode(const std::string& data, Variant::Map& value)
+{
+ _decode<FieldTable>(data, value, &toVariantMapEntry);
+}
+
+size_t MapCodec::encodedSize(const Variant::Map& value)
+{
+ std::string encoded;
+ encode(value, encoded);
+ return encoded.size();
+}
+
+void ListCodec::encode(const Variant::List& value, std::string& data)
+{
+ _encode<List>(value, data, &toFieldValue);
+}
+
+void ListCodec::decode(const std::string& data, Variant::List& value)
+{
+ _decode<List>(data, value, &toVariant);
+}
+
+size_t ListCodec::encodedSize(const Variant::List& value)
+{
+ std::string encoded;
+ encode(value, encoded);
+ return encoded.size();
+}
+
+void translate(const Variant::Map& from, FieldTable& to)
+{
+ convert(from, to, &toFieldTableEntry);
+}
+
+void translate(const FieldTable& from, Variant::Map& to)
+{
+ convert(from, to, &toVariantMapEntry);
+}
+
+const std::string ListCodec::contentType("amqp/list");
+const std::string MapCodec::contentType("amqp/map");
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Command.h b/qpid/cpp/src/qpid/amqp_0_10/Command.h
new file mode 100644
index 0000000000..b1d3607a84
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Command.h
@@ -0,0 +1,62 @@
+#ifndef QPID_AMQP_0_10_COMMAND_H
+#define QPID_AMQP_0_10_COMMAND_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/Control.h"
+#include "qpid/amqp_0_10/structs.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+struct CommandVisitor;
+struct ConstCommandVisitor;
+struct CommandHolder;
+struct Command
+ : public Action,
+ public Visitable<CommandVisitor, ConstCommandVisitor, CommandHolder>
+{
+ using Action::getCommand;
+ Command* getCommand() { return this; }
+ uint8_t getCode() const;
+ uint8_t getClassCode() const;
+ const char* getName() const;
+ const char* getClassName() const;
+
+ session::Header sessionHeader;
+};
+
+std::ostream& operator<<(std::ostream&, const Command&);
+
+template <class T>
+struct CommandPacker : Packer<T> {
+ CommandPacker(T& t) : Packer<T>(t) {}
+
+ template <class S> void serialize(S& s) {
+ s(this->data.sessionHeader);
+ Packer<T>::serialize(s);
+ }
+};
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_COMMAND_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h b/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h
new file mode 100644
index 0000000000..51ebfe8186
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/CommmandPacker.h
@@ -0,0 +1,60 @@
+#ifndef QPID_AMQP_0_10_COMMMANDPACKER_H
+#define QPID_AMQP_0_10_COMMMANDPACKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/structs.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/**
+ * Packer for commands - serialize session.header before pack bits.
+ */
+template <class T>
+class CommmandPacker : public Packer<T>
+{
+ public:
+ CommmandPacker(T& t) : Packer<T>(t) {}
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ template <class S> void encode(S& s) const {
+ s.sessionHeader(
+ Packer<T>::encode(s);
+ }
+
+ template <class S> void decode(S& s) {
+ Bits bits;
+ s.littleEnd(bits);
+ PackedDecoder<S, Bits> decode(s, bits);
+ data.serialize(decode);
+ }
+
+
+ protected:
+ T& data;
+
+
+};
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_COMMMANDPACKER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp
new file mode 100644
index 0000000000..bf2e7d5713
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/amqp_0_10/exceptions.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/ProtocolInitiation.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+using sys::Mutex;
+
+Connection::Connection(sys::OutputControl& o, const std::string& id, bool _isClient)
+ : pushClosed(false), popClosed(false), output(o), identifier(id), initialized(false),
+ isClient(_isClient), buffered(0), version(0,10)
+{}
+
+void Connection::setInputHandler(std::auto_ptr<sys::ConnectionInputHandler> c) {
+ connection = c;
+}
+
+size_t Connection::decode(const char* buffer, size_t size) {
+ framing::Buffer in(const_cast<char*>(buffer), size);
+ if (isClient && !initialized) {
+ //read in protocol header
+ framing::ProtocolInitiation pi;
+ if (pi.decode(in)) {
+ if(!(pi==version))
+ throw Exception(QPID_MSG("Unsupported version: " << pi
+ << " supported version " << version));
+ QPID_LOG(trace, "RECV " << identifier << " INIT(" << pi << ")");
+ }
+ initialized = true;
+ }
+ framing::AMQFrame frame;
+ while(frame.decode(in)) {
+ QPID_LOG(trace, "RECV [" << identifier << "]: " << frame);
+ connection->received(frame);
+ }
+ return in.getPosition();
+}
+
+bool Connection::canEncode() {
+ Mutex::ScopedLock l(frameQueueLock);
+ if (!popClosed) {
+ Mutex::ScopedUnlock u(frameQueueLock);
+ connection->doOutput();
+ }
+ return !popClosed && ((!isClient && !initialized) || !frameQueue.empty());
+}
+
+bool Connection::isClosed() const {
+ Mutex::ScopedLock l(frameQueueLock);
+ return pushClosed && popClosed;
+}
+
+size_t Connection::encode(const char* buffer, size_t size) {
+ { // Swap frameQueue data into workQueue to avoid holding lock while we encode.
+ Mutex::ScopedLock l(frameQueueLock);
+ if (popClosed) return 0; // Can't pop any more frames.
+ assert(workQueue.empty());
+ workQueue.swap(frameQueue);
+ }
+ framing::Buffer out(const_cast<char*>(buffer), size);
+ if (!isClient && !initialized) {
+ framing::ProtocolInitiation pi(getVersion());
+ pi.encode(out);
+ initialized = true;
+ QPID_LOG(trace, "SENT " << identifier << " INIT(" << pi << ")");
+ }
+ size_t frameSize=0;
+ size_t encoded=0;
+ while (!workQueue.empty() && ((frameSize=workQueue.front().encodedSize()) <= out.available())) {
+ workQueue.front().encode(out);
+ QPID_LOG(trace, "SENT [" << identifier << "]: " << workQueue.front());
+ workQueue.pop_front();
+ encoded += frameSize;
+ if (workQueue.empty() && out.available() > 0) connection->doOutput();
+ }
+ assert(workQueue.empty() || workQueue.front().encodedSize() <= size);
+ if (!workQueue.empty() && workQueue.front().encodedSize() > size)
+ throw InternalErrorException(QPID_MSG("Frame too large for buffer."));
+ {
+ Mutex::ScopedLock l(frameQueueLock);
+ buffered -= encoded;
+ // Put back any frames we did not encode.
+ frameQueue.insert(frameQueue.begin(), workQueue.begin(), workQueue.end());
+ workQueue.clear();
+ if (frameQueue.empty() && pushClosed)
+ popClosed = true;
+ }
+ return out.getPosition();
+}
+
+void Connection::abort() { output.abort(); }
+void Connection::activateOutput() { output.activateOutput(); }
+void Connection::giveReadCredit(int32_t credit) { output.giveReadCredit(credit); }
+
+void Connection::close() {
+ // No more frames can be pushed onto the queue.
+ // Frames aleady on the queue can be popped.
+ Mutex::ScopedLock l(frameQueueLock);
+ pushClosed = true;
+}
+
+void Connection::closed() {
+ connection->closed();
+}
+
+void Connection::send(framing::AMQFrame& f) {
+ {
+ Mutex::ScopedLock l(frameQueueLock);
+ if (!pushClosed)
+ frameQueue.push_back(f);
+ buffered += f.encodedSize();
+ }
+ activateOutput();
+}
+
+framing::ProtocolVersion Connection::getVersion() const {
+ return version;
+}
+
+void Connection::setVersion(const framing::ProtocolVersion& v) {
+ version = v;
+}
+
+size_t Connection::getBuffered() const {
+ Mutex::ScopedLock l(frameQueueLock);
+ return buffered;
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.h b/qpid/cpp/src/qpid/amqp_0_10/Connection.h
new file mode 100644
index 0000000000..995d824796
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.h
@@ -0,0 +1,83 @@
+#ifndef QPID_AMQP_0_10_CONNECTION_H
+#define QPID_AMQP_0_10_CONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/ConnectionInputHandler.h"
+#include "qpid/sys/ConnectionOutputHandler.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include <boost/intrusive_ptr.hpp>
+#include <memory>
+#include <deque>
+
+namespace qpid {
+
+namespace sys {
+class ConnectionInputHandlerFactory;
+}
+
+namespace amqp_0_10 {
+
+class Connection : public sys::ConnectionCodec,
+ public sys::ConnectionOutputHandler
+{
+ typedef std::deque<framing::AMQFrame> FrameQueue;
+
+ FrameQueue frameQueue;
+ FrameQueue workQueue;
+ bool pushClosed, popClosed;
+ mutable sys::Mutex frameQueueLock;
+ sys::OutputControl& output;
+ std::auto_ptr<sys::ConnectionInputHandler> connection;
+ std::string identifier;
+ bool initialized;
+ bool isClient;
+ size_t buffered;
+ framing::ProtocolVersion version;
+
+ public:
+ QPID_BROKER_EXTERN Connection(sys::OutputControl&, const std::string& id, bool isClient);
+ QPID_BROKER_EXTERN void setInputHandler(std::auto_ptr<sys::ConnectionInputHandler> c);
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool isClosed() const;
+ bool canEncode();
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t);
+ void closed(); // connection closed by peer.
+ void close(); // closing from this end.
+ void send(framing::AMQFrame&);
+ framing::ProtocolVersion getVersion() const;
+ size_t getBuffered() const;
+
+ /** Used by cluster code to set a special version on "update" connections. */
+ // FIXME aconway 2009-07-30: find a cleaner mechanism for this.
+ void setVersion(const framing::ProtocolVersion&);
+};
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_CONNECTION_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Control.h b/qpid/cpp/src/qpid/amqp_0_10/Control.h
new file mode 100644
index 0000000000..ce188ae6d8
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Control.h
@@ -0,0 +1,70 @@
+#ifndef QPID_AMQP_0_10_CONTROL_H
+#define QPID_AMQP_0_10_CONTROL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/Struct.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+struct Command;
+struct Control;
+
+struct Action { // Base for commands & controls
+ virtual ~Action() {}
+ virtual Command* getCommand() { return 0; }
+ virtual Control* getControl() { return 0; }
+
+ virtual const Command* getCommand() const {
+ return const_cast<Action*>(this)->getCommand();
+ }
+ virtual const Control* getControl() const {
+ return const_cast<Action*>(this)->getControl();
+ }
+ static const uint8_t SIZE=0;
+ static const uint8_t PACK=2;
+};
+
+struct ControlVisitor;
+struct ConstControlVisitor;
+struct ControlHolder;
+struct Control
+ : public Action,
+ public Visitable<ControlVisitor, ConstControlVisitor, ControlHolder>
+{
+ using Action::getControl;
+ Control* getControl() { return this; }
+ uint8_t getCode() const;
+ uint8_t getClassCode() const;
+ const char* getName() const;
+ const char* getClassName() const;
+};
+std::ostream& operator<<(std::ostream&, const Control&);
+
+template <SegmentType E> struct ActionType;
+template <> struct ActionType<CONTROL> { typedef Control type; };
+template <> struct ActionType<COMMAND> { typedef Command type; };
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_CONTROL_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Decimal.h b/qpid/cpp/src/qpid/amqp_0_10/Decimal.h
new file mode 100644
index 0000000000..50fc457c76
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Decimal.h
@@ -0,0 +1,51 @@
+#ifndef TESTS_DECIMAL_H
+#define TESTS_DECIMAL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+template <class E, class M> struct Decimal {
+ E exponent;
+ M mantissa;
+
+ Decimal(E exp=0, M man=0) : exponent(exp), mantissa(man) {}
+
+ bool operator==(const Decimal& d) const {
+ return exponent == d.exponent && mantissa == d.mantissa;
+ }
+
+ // TODO aconway 2008-02-20: We could provide arithmetic operators
+ // if anybody really cares about this type.
+
+ template <class S> void serialize(S& s) { s(exponent)(mantissa); }
+};
+
+template<class E, class M>
+inline std::ostream& operator<<(std::ostream& o, const Decimal<E,M>& d) {
+ return o << "Decimal{" << d.mantissa << "/10^" << (int)d.exponent << "}";
+}
+}}
+
+#endif /*!TESTS_DECIMAL_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Exception.h b/qpid/cpp/src/qpid/amqp_0_10/Exception.h
new file mode 100644
index 0000000000..6d526c1706
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Exception.h
@@ -0,0 +1,96 @@
+#ifndef QPID_AMQP_0_10_EXCEPTION_H
+#define QPID_AMQP_0_10_EXCEPTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/amqp_0_10/specification_fwd.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/**
+ * Raised when the connection is unexpectedly closed. Sessions with
+ * non-0 timeout may be available for re-attachment on another connection.
+ */
+struct ConnectionException : public qpid::Exception {
+ // FIXME aconway 2008-04-04: Merge qpid::ConnectionException
+ // into this when the old code is removed.
+ typedef connection::CloseCode Code;
+ ConnectionException(Code c, const std::string m)
+ : qpid::Exception(m), code(c) {}
+ Code code;
+};
+
+/**
+ * Raised when a session is unexpectedly detached for any reason, or
+ * if an attempt is made to use a session that is not attached.
+ */
+struct SessionException : public qpid::Exception {
+ // FIXME aconway 2008-04-04: should not have a code at this level.
+ // Leave in place till old preview code is gone.
+ SessionException(int /*code*/, const std::string& msg) : qpid::Exception(msg) {}
+};
+
+/** Raised when the state of a session has been destroyed */
+struct SessionDestroyedException : public SessionException {
+ // FIXME aconway 2008-04-04: should not have a code at this level.
+ // Leave in place till old preview code is gone.
+ SessionDestroyedException(int code, const std::string& msg) : SessionException(code, msg){}
+};
+
+/** Raised when a session is destroyed due to an execution.exception */
+struct SessionAbortedException : public SessionDestroyedException {
+ typedef execution::ErrorCode Code;
+ SessionAbortedException(Code c, const std::string m)
+ : SessionDestroyedException(c, m), code(c) {}
+ Code code;
+};
+
+/**
+ * Raised when a session with 0 timeout is unexpectedly detached
+ * and therefore expires and is destroyed.
+ */
+struct SessionExpiredException : public SessionDestroyedException {
+ typedef session::DetachCode Code;
+ SessionExpiredException(Code c, const std::string m)
+ : SessionDestroyedException(c, m), code(c) {}
+ Code code;
+};
+
+/**
+ * Raised when a session with non-0 timeout is unexpectedly detached
+ * or if an attempt is made to use a session that is not attached.
+ *
+ * The session is not necessarily destroyed, it may be possible to
+ * re-attach.
+ */
+struct SessionDetachedException : public SessionException {
+ typedef session::DetachCode Code;
+ SessionDetachedException(Code c, const std::string m)
+ : SessionException(c, m), code(c) {}
+ Code code;
+};
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp
new file mode 100644
index 0000000000..371e3c1bcb
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/FrameHeader.h"
+#include <ios>
+#include <iomanip>
+#include <ostream>
+
+using namespace std;
+
+namespace qpid {
+namespace amqp_0_10 {
+
+bool FrameHeader::operator==(const FrameHeader& x) const {
+ return flags == x.flags &&
+ type == x.type &&
+ size == x.size &&
+ track == x.track &&
+ channel == x.channel;
+}
+
+std::ostream& operator<<(std::ostream& o, const FrameHeader& f) {
+ std::ios::fmtflags saveFlags = o.flags();
+ return o << "Frame["
+ << "flags=" << std::hex << std::showbase << int(f.getFlags()) << std::setiosflags(saveFlags)
+ << " type=" << f.getType()
+ << " size=" << f.getSize()
+ << " track=" << int(f.getTrack())
+ << " channel=" << f.getChannel()
+ << "]";
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h
new file mode 100644
index 0000000000..b2f0619f9b
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h
@@ -0,0 +1,90 @@
+#ifndef QPID_AMQP_0_10_FRAMEHEADER_H
+#define QPID_AMQP_0_10_FRAMEHEADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/built_in_types.h"
+#include <boost/shared_array.hpp>
+#include <string.h>
+#include <assert.h>
+#include <iosfwd>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+enum FrameFlags { FIRST_SEGMENT=8, LAST_SEGMENT=4, FIRST_FRAME=2, LAST_FRAME=1 };
+
+class FrameHeader {
+ public:
+ static const size_t SIZE=12;
+ static uint8_t trackFor(SegmentType type) { return type == 0 ? 0 : 1; }
+
+ FrameHeader(uint8_t flags_=0, SegmentType type_=SegmentType(), uint16_t size_=0, uint8_t track_=0, uint16_t channel_=0)
+ : flags(flags_), type(type_), size(size_), track(track_), channel(channel_)
+ {}
+
+ uint8_t getFlags() const { return flags; }
+ SegmentType getType() const { return type; }
+ /** @return size total size of of frame, including frame header. */
+ uint16_t getSize() const { return size; }
+ /** @return size of frame data, excluding frame header. */
+ uint16_t getDataSize() const { return size - SIZE; }
+ uint8_t getTrack() const { return track; }
+ uint16_t getChannel() const { return channel; }
+
+ void setFlags(uint8_t flags_) { flags=flags_; }
+ /** Also sets the track. There is no setTrack() */
+ void setType(SegmentType type_) { type=type_; track=trackFor(type); }
+ /** @param size total size of of frame, including frame header. */
+ void setSize(uint16_t size_) { size = size_; }
+ /** @param size size of frame data, excluding frame header. */
+ void setDataSize(uint16_t size_) { size = size_+SIZE; }
+ void setChannel(uint8_t channel_) { channel=channel_; }
+
+ bool allFlags(uint8_t f) const { return (flags & f) == f; }
+ bool anyFlags(uint8_t f) const { return (flags & f); }
+
+ void raiseFlags(uint8_t f) { flags |= f; }
+ void clearFlags(uint8_t f) { flags &= ~f; }
+
+ bool isComplete() const { return allFlags(FIRST_FRAME | LAST_FRAME); }
+
+ bool operator==(const FrameHeader&) const;
+
+ template <class S> void serialize(S& s) {
+ uint8_t pad8=0; uint32_t pad32=0;
+ s(flags)(type)(size)(pad8)(track)(channel)(pad32);
+ }
+
+ private:
+ uint8_t flags;
+ SegmentType type;
+ uint16_t size;
+ uint8_t track;
+ uint16_t channel;
+};
+
+std::ostream& operator<<(std::ostream&, const FrameHeader&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_FRAMEHEADER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Header.cpp b/qpid/cpp/src/qpid/amqp_0_10/Header.cpp
new file mode 100644
index 0000000000..d83814e969
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Header.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Header.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+std::ostream& operator<<(std::ostream& o, const Header& h) {
+ o << "Header[";
+ std::ostream_iterator<Struct32> i(o, " ");
+ std::copy(h.begin(), h.end(), i);
+ o << "]";
+ return o;
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Header.h b/qpid/cpp/src/qpid/amqp_0_10/Header.h
new file mode 100644
index 0000000000..0ce6ad9135
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Header.h
@@ -0,0 +1,53 @@
+#ifndef QPID_AMQP_0_10_HEADER_H
+#define QPID_AMQP_0_10_HEADER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp_0_10/built_in_types.h"
+#include "qpid/amqp_0_10/Struct32.h"
+#include <vector>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class Header : public std::vector<Struct32> {
+ public:
+ Header() {}
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+ template <class S> void encode(S& s) const { s(this->begin(), this->end()); }
+ template <class S> void decode(S& s);
+};
+
+template <class S> void Header::decode(S& s) {
+ this->clear();
+ while (s.bytesRemaining() > 0) {
+ this->push_back(Struct32());
+ s(this->back());
+ }
+}
+
+std::ostream& operator<<(std::ostream& o, const Header&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_HEADER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Holder.h b/qpid/cpp/src/qpid/amqp_0_10/Holder.h
new file mode 100644
index 0000000000..605d2e0ed5
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Holder.h
@@ -0,0 +1,103 @@
+#ifndef QPID_AMQP_0_10_HOLDER_H
+#define QPID_AMQP_0_10_HOLDER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/Blob.h"
+#include "qpid/amqp_0_10/apply.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+using framing::in_place;
+
+template <class Invokable> struct InvokeVisitor {
+ typedef void result_type;
+ Invokable& target;
+ InvokeVisitor(Invokable& i) : target(i) {}
+
+ template <class Action>
+ void operator()(const Action& action) { action.invoke(target); }
+};
+
+template <class DerivedHolder, class BaseHeld, size_t Size>
+class Holder : public framing::Blob<Size, BaseHeld> {
+ typedef framing::Blob<Size, BaseHeld> Base;
+
+ public:
+
+ Holder() {}
+ template <class T> explicit Holder(const T& value) : Base(value) {}
+
+ using Base::operator=;
+ Holder& operator=(const BaseHeld& rhs);
+
+ uint8_t getCode() const { return this->get()->getCode(); }
+ uint8_t getClassCode() const { return this->get()->getClassCode(); }
+
+ template <class Invokable> void invoke(Invokable& i) const {
+ InvokeVisitor<Invokable> v(i);
+ apply(v, *this->get());
+ }
+
+ template <class S> void encode(S& s) const {
+ s(getClassCode())(getCode());
+ }
+
+ template <class S> void decode(S& s) {
+ uint8_t code, classCode;
+ s(classCode)(code);
+ static_cast<DerivedHolder*>(this)->set(classCode, code);
+ }
+
+ template <class S> void serialize(S& s) {
+ s.split(*this);
+ qpid::amqp_0_10::apply(s, *this->get());
+ }
+
+ template <class T> T* getIf() {
+ return (getClassCode()==T::CLASS_CODE && getCode()==T::CODE) ? static_cast<T*>(this->get()) : 0;
+ }
+
+ template <class T> const T* getIf() const {
+ return (getClassCode()==T::CLASS_CODE && getCode()==T::CODE) ? static_cast<T*>(this->get()) : 0;
+ }
+
+ private:
+ struct Assign : public ApplyFunctor<void> {
+ Holder& holder;
+ Assign(Holder& x) : holder(x) {}
+ template <class T> void operator()(const T& rhs) { holder=rhs; }
+ };
+};
+
+template <class D, class B, size_t S>
+Holder<D,B,S>& Holder<D,B,S>::operator=(const B& rhs) {
+ Assign assign(*this);
+ qpid::amqp_0_10::apply(assign, rhs);
+ return *this;
+}
+
+
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_HOLDER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.cpp b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp
new file mode 100644
index 0000000000..af3b302d25
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Map.h"
+#include "qpid/amqp_0_10/Struct32.h"
+#include "qpid/amqp_0_10/Array.h"
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place<uint8_t>(0)) {}
+
+MapValue::MapValue(const MapValue& x) : code(x.code), blob(x.blob) {}
+
+bool MapValue::operator==(const MapValue& x) const {
+ return code == x.code; // FIXME aconway 2008-04-01: incomplete
+}
+
+struct OstreamVisitor : public MapValue::Visitor<std::ostream&> {
+ std::ostream& out;
+ OstreamVisitor(std::ostream& o) : out(o) {}
+ template <class T> std::ostream& operator()(const T& t) {
+ return out << t;
+ }
+};
+
+std::ostream& operator<<(std::ostream& o, const MapValue& m) {
+ o << typeName(m.getCode()) << ":";
+ const_cast<MapValue&>(m).apply_visitor(OstreamVisitor(o));
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const Map::value_type& v) {
+ return o << v.first << "=" << v.second;
+}
+std::ostream& operator<<(std::ostream& o, const Map& map) {
+ o << "map[";
+ std::ostream_iterator<Map::value_type> i(o, " ");
+ std::copy(map.begin(), map.end(), i);
+ return o << "]";
+}
+
+uint32_t Map::contentSize() const {
+ // FIXME aconway 2008-04-03: preview to 0-10 mapping: +4 for count.
+ return /*4 +*/ Codec::Size()(begin(), end());
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.h b/qpid/cpp/src/qpid/amqp_0_10/Map.h
new file mode 100644
index 0000000000..4093b1a0aa
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Map.h
@@ -0,0 +1,188 @@
+#ifndef QPID_AMQP_0_10_MAP_H
+#define QPID_AMQP_0_10_MAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on ang
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/amqp_0_10/built_in_types.h"
+#include "qpid/amqp_0_10/UnknownType.h"
+#include "qpid/amqp_0_10/CodeForType.h"
+#include "qpid/amqp_0_10/TypeForCode.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include "qpid/framing/Blob.h"
+#include <map>
+#include <string>
+#include <iosfwd>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class Map;
+
+class MapValue {
+ public:
+ struct BadTypeException : public Exception {};
+
+ template <class R> struct Visitor { typedef R result_type; };
+
+ MapValue();
+ MapValue(const MapValue& x);
+ template <class T> explicit MapValue(const T& t);
+ template <class T> MapValue& operator=(const T& t);
+
+ template <class T> T* get();
+ template <class T> const T* get() const;
+
+ template <class V> typename V::result_type apply_visitor(V&);
+ template <class V> typename V::result_type apply_visitor(const V&);
+
+ uint8_t getCode() const { return code; }
+
+ bool operator==(const MapValue&) const;
+
+ template <class S> void serialize(S& s) { s(code); s.split(*this); }
+ template <class S> void encode(S& s) const {
+ const_cast<MapValue*>(this)->apply_visitor(s);
+ }
+ template <class S> void decode(S& s) {
+ DecodeVisitor<S> dv(blob, s);
+ qpid::amqp_0_10::apply_visitor(dv, code);
+ }
+
+
+ private:
+ // TODO aconway 2008-04-15: Estimate required size, we will get a
+ // compile error from static_assert in Blob.h if the estimate is too
+ // low. We can't use sizeof() directly because #include Struct32.h
+ // creates a circular dependency. Needs a better solution.
+ static const size_t SIZE=256;
+ typedef framing::Blob<SIZE> Blob;
+
+ template <class V> struct VisitVisitor;
+ template <class T> struct GetVisitor;
+ template <class D> struct DecodeVisitor;
+
+ uint8_t code;
+ Blob blob;
+};
+
+class Map : public std::map<Str8, MapValue> {
+ public:
+ template <class S> void serialize(S& s) { s.split(*this); }
+ template <class S> void encode(S& s) const;
+ // Shortcut calculation for size.
+ void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); }
+
+ template <class S> void decode(S& s);
+
+ private:
+ uint32_t contentSize() const;
+};
+
+std::ostream& operator<<(std::ostream&, const MapValue&);
+std::ostream& operator<<(std::ostream&, const Map::value_type&);
+std::ostream& operator<<(std::ostream&, const Map&);
+
+using framing::in_place;
+
+template <class T> MapValue::MapValue(const T& t) : code(codeFor(t)), blob(in_place<t>()) {}
+
+template <class T> MapValue& MapValue::operator=(const T& t) {
+ code=codeFor(t);
+ blob=t;
+ return *this;
+}
+
+template <class V> struct MapValue::VisitVisitor {
+ typedef typename V::result_type result_type;
+ V& visitor;
+ Blob& blob;
+ VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {}
+
+ template <class T> result_type operator()(T*) {
+ return visitor(*reinterpret_cast<T*>(blob.get()));
+ }
+};
+
+template <class V> typename V::result_type MapValue::apply_visitor(V& v) {
+ VisitVisitor<V> visitor(v, blob);
+ return qpid::amqp_0_10::apply_visitor(visitor, code);
+}
+
+template <class R> struct MapValue::GetVisitor {
+ typedef R* result_type;
+ const MapValue::Blob& blob;
+
+ GetVisitor(const MapValue::Blob& b) : blob(b) {}
+
+ R* operator()(R& r) { return &r; }
+ template <class T> R* operator()(T&) { return 0; }
+};
+
+template <class D> struct MapValue::DecodeVisitor {
+ typedef void result_type;
+ MapValue::Blob& blob;
+ D& decoder;
+ DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {}
+
+ template <class T> void operator()(T*) {
+ T t;
+ decoder(t);
+ blob = t;
+ }
+};
+
+template <class T> T* MapValue::get() { return apply_visitor(GetVisitor<T>(blob)); }
+template <class T> const T* MapValue::get() const { return apply_visitor(GetVisitor<const T>()); }
+
+template <class V> typename V::result_type MapValue::apply_visitor(const V& v) {
+ return apply_visitor(const_cast<V&>(v));
+}
+
+template <class S> void Map::encode(S& s) const {
+ // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping:
+ // s(contentSize())(uint32_t(size())); // size, count
+ s(contentSize());
+ for (const_iterator i = begin(); i != end(); ++i)
+ s(i->first)(i->second); // key (type value)
+}
+
+template <class S> void Map::decode(S& s) {
+ uint32_t decodedSize /*, count*/;
+ // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping:
+ // s(contentSize())(uint32_t(size())); // size, count
+ // s(decodedSize)(count);
+ s(decodedSize);
+ typename S::ScopedLimit l(s, decodedSize); // Make sure we don't overrun.
+ // FIXME aconway 2008-04-03: replace preview with 0-10:
+ // for ( ; count > 0; --count) {
+ while (s.bytesRemaining() > 0) {
+ key_type k; MapValue v;
+ s(k)(v);
+ insert(value_type(k,v));
+ }
+}
+
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_MAP_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Packer.h b/qpid/cpp/src/qpid/amqp_0_10/Packer.h
new file mode 100644
index 0000000000..c38e3a7efa
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Packer.h
@@ -0,0 +1,195 @@
+#ifndef QPID_PACKER_H
+#define QPID_PACKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/optional.hpp>
+#include <boost/none.hpp>
+#include "qpid/amqp_0_10/built_in_types.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Serialization for optional values */
+template <class T> struct SerializableOptional {
+ boost::optional<T>& optional;
+ SerializableOptional(boost::optional<T>& x) : optional(x) {}
+ template <class S> void serialize(S& s) {
+ if (optional)
+ s(*optional);
+ }
+};
+
+}}
+
+
+namespace boost { // For argument dependent lookup.
+
+template <class T>
+qpid::amqp_0_10::SerializableOptional<T> serializable(boost::optional<T>& x) {
+ return qpid::amqp_0_10::SerializableOptional<T>(x);
+}
+
+} // namespace boost
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/** "Encoder" that encodes a struct as a set of bit flags
+ * for all non-empty members.
+ */
+class PackBits {
+ public:
+ PackBits() : bit(1), bits(0) {}
+
+ void setBit(bool b) { if (b) bits |= bit; bit <<= 1; }
+ uint32_t getBits() { return bits; }
+
+ /** The bit is always set for non-optional values. */
+ template <class T>
+ PackBits& operator()(const T&) { setBit(1); return *this; }
+
+ /** For optional values the bit is set if the value is present. */
+ template <class T> PackBits& operator()(const boost::optional<T>& opt) {
+ setBit(opt); return *this;
+ }
+
+ /** Bits are special optional values */
+ PackBits& operator()(Bit b) { setBit(b); return *this; }
+
+ private:
+ uint32_t bit;
+ uint32_t bits;
+};
+
+/** Bit mask to encode a packable struct */
+template<class T> uint32_t packBits(const T& t) {
+ PackBits pack;
+ const_cast<T&>(t).serialize(pack);
+ return pack.getBits();
+}
+
+/** Decode members enabled by Bits */
+template <class Decoder, class Bits>
+class PackedDecoder {
+ public:
+ PackedDecoder(Decoder& d, Bits b) : decode(d), bits(b) {}
+
+ template <class T> PackedDecoder& operator()(T& t) {
+ if (bits & 1)
+ decode(t);
+ else
+ t = T();
+ // FIXME aconway 2008-04-10: When we have all optionals
+ // represented by boost::optional the line above should be:
+ // throw CommandInvalidException("A required value was omitted.");
+ bits >>= 1;
+ return *this;
+ }
+
+ template <class T> PackedDecoder& operator()(boost::optional<T>& opt) {
+ if (bits & 1) {
+ opt = T();
+ decode(*opt);
+ }
+ else
+ opt = boost::none;
+ bits >>= 1;
+ return *this;
+ }
+
+ private:
+ Decoder& decode;
+ Bits bits;
+};
+
+/** Metafunction to compute type to contain pack bits. */
+template <int Bytes> struct UintOfSize;
+template <> struct UintOfSize<1> { typedef uint8_t type; };
+template <> struct UintOfSize<2> { typedef uint16_t type; };
+template <> struct UintOfSize<4> { typedef uint32_t type; };
+
+/**
+ * Helper to serialize packed structs.
+ */
+template <class T> class Packer
+{
+ public:
+ typedef typename UintOfSize<T::PACK>::type Bits;
+
+ Packer(T& t) : data(t) {}
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ template <class S> void encode(S& s) const {
+ Bits bits = packBits(data);
+ s.littleEnd(bits);
+ data.serialize(s);
+ }
+
+ template <class S> void decode(S& s) {
+ Bits bits;
+ s.littleEnd(bits);
+ PackedDecoder<S, Bits> decode(s, bits);
+ data.serialize(decode);
+ }
+
+
+ protected:
+ T& data;
+};
+
+template <class T, uint8_t=T::SIZE> struct SizedPacker : public Packer<T> {
+ typedef typename UintOfSize<T::SIZE>::type Size;
+
+ SizedPacker(T& t) : Packer<T>(t) {}
+
+ template <class S> void serialize(S& s) {
+ s.split(*this);
+ }
+
+ template <class S> void encode(S& s) const {
+ Codec::Size sizer;
+ this->data.serialize(sizer);
+ Size size=size_t(sizer)+T::PACK; // Size with pack bits.
+ s(size);
+ Packer<T>::encode(s);
+ }
+
+ template <class S> void decode(S& s) {
+ Size size;
+ s(size);
+ typename S::ScopedLimit l(s, size);
+ Packer<T>::decode(s);
+ }
+
+};
+
+template <class T> struct SizedPacker<T,0> : public Packer<T> {
+ SizedPacker(T& t) : Packer<T>(t) {}
+};
+
+}} // namespace qpid::amqp_0_10
+
+
+
+#endif /*!QPID_PACKER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h
new file mode 100644
index 0000000000..485b7ca6a8
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h
@@ -0,0 +1,62 @@
+#ifndef QPID_AMQP_0_10_SERIALIZABLESTRING_H
+#define QPID_AMQP_0_10_SERIALIZABLESTRING_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Template for length-prefixed strings/arrays.
+ * Unique parameter allows creation of distinct SerializableString
+ * types with the smae T/SizeType
+ */
+template <class T, class SizeType, int Unique=0>
+struct SerializableString : public std::basic_string<T> {
+ SerializableString() {}
+ template <class U> SerializableString(const U& u) : std::basic_string<T>(u) {}
+ template <class I> SerializableString(const I& i, const I& j) : std::basic_string<T>(i,j) {}
+
+ using std::basic_string<T>::operator=;
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ template <class S> void encode(S& s) const {
+ s(SizeType(this->size()))(this->begin(), this->end());
+ }
+
+ template <class S> void decode(S& s) {
+ SizeType newSize;
+ s(newSize);
+ this->resize(newSize);
+ s(this->begin(), this->end());
+ }
+};
+
+// TODO aconway 2008-02-29: separate ostream ops
+template <class T, class SizeType>
+std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) {
+ const std::basic_string<T> str(s);
+ return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work?
+}
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_SERIALIZABLESTRING_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp b/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
new file mode 100644
index 0000000000..97281a8d8c
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include "qpid/amqp_0_10/SessionHandler.h"
+#include "qpid/SessionState.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/AllInvoker.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Statement.h"
+
+
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace amqp_0_10 {
+using namespace framing;
+using namespace std;
+
+void SessionHandler::checkAttached() {
+ if (!getState())
+ throw NotAttachedException(QPID_MSG("Channel " << channel.get() << " is not attached"));
+}
+
+SessionHandler::SessionHandler(FrameHandler* out, ChannelId ch)
+ : channel(ch, out), peer(channel),
+ awaitingDetached(false),
+ sendReady(), receiveReady() {}
+
+SessionHandler::~SessionHandler() {}
+
+namespace {
+bool isSessionControl(AMQMethodBody* m) {
+ return m && m->amqpClassId() == SESSION_CLASS_ID;
+}
+
+session::DetachCode convert(uint8_t code) {
+ switch(code) {
+ case 0: return session::DETACH_CODE_NORMAL;
+ case 1: return session::DETACH_CODE_SESSION_BUSY;
+ case 2: return session::DETACH_CODE_TRANSPORT_BUSY;
+ case 3: return session::DETACH_CODE_NOT_ATTACHED;
+ case 4: default: return session::DETACH_CODE_UNKNOWN_IDS;
+ }
+}
+
+} // namespace
+
+void SessionHandler::invoke(const AMQMethodBody& m) {
+ framing::invoke(*this, m);
+}
+
+void SessionHandler::handleIn(AMQFrame& f) {
+ // Note on channel states: a channel is attached if session != 0
+ AMQMethodBody* m = f.getBody()->getMethod();
+ try {
+ // Ignore all but detach controls while awaiting detach
+ if (awaitingDetached) {
+ if (!isSessionControl(m)) return;
+ if (m->amqpMethodId() != SESSION_DETACH_METHOD_ID &&
+ m->amqpMethodId() != SESSION_DETACHED_METHOD_ID)
+ return;
+ }
+ if (isSessionControl(m)) {
+ invoke(*m);
+ }
+ else {
+ // Drop frames if we are detached.
+ if (!getState()) return;
+ if (!receiveReady)
+ throw IllegalStateException(QPID_MSG(getState()->getId() << ": Not ready to receive data"));
+ if (!getState()->receiverRecord(f))
+ return; // Ignore duplicates.
+ if (getState()->receiverNeedKnownCompleted())
+ sendCompletion();
+ getInHandler()->handle(f);
+ }
+ }
+ catch(const SessionException& e) {
+ QPID_LOG(error, "Execution exception: " << e.what());
+ executionException(e.code, e.what()); // Let subclass handle this first.
+ framing::AMQP_AllProxy::Execution execution(channel);
+ AMQMethodBody* m = f.getMethod();
+ SequenceNumber commandId;
+ if (getState()) commandId = getState()->receiverGetCurrent();
+ execution.exception(e.code, commandId, m ? m->amqpClassId() : 0, m ? m->amqpMethodId() : 0, 0, e.what(), FieldTable());
+ detaching();
+ sendDetach();
+ }
+ catch(const ChannelException& e){
+ QPID_LOG(error, "Channel exception: " << e.what());
+ channelException(e.code, e.what()); // Let subclass handle this first.
+ peer.detached(name, e.code);
+ }
+ catch(const ConnectionException& e) {
+ QPID_LOG(error, "Connection exception: " << e.what());
+ connectionException(e.code, e.getMessage());
+ }
+ catch(const std::exception& e) {
+ QPID_LOG(error, "Unexpected exception: " << e.what());
+ connectionException(connection::CLOSE_CODE_FRAMING_ERROR, e.what());
+ }
+}
+
+void SessionHandler::handleException(const qpid::SessionException& e)
+{
+ QPID_LOG(error, "Execution exception (during output): " << e.what());
+ executionException(e.code, e.what()); // Let subclass handle this first.
+ framing::AMQP_AllProxy::Execution execution(channel);
+ execution.exception(e.code, 0, 0, 0, 0, e.what(), FieldTable());
+ detaching();
+ sendDetach();
+}
+
+namespace {
+bool isCommand(const AMQFrame& f) {
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND;
+}
+} // namespace
+
+void SessionHandler::handleOut(AMQFrame& f) {
+ checkAttached();
+ if (!sendReady)
+ throw IllegalStateException(QPID_MSG(getState()->getId() << ": Not ready to send data"));
+ getState()->senderRecord(f);
+ if (isCommand(f) && getState()->senderNeedFlush()) {
+ peer.flush(false, false, true);
+ getState()->senderRecordFlush();
+ }
+ channel.handle(f);
+}
+
+void SessionHandler::attach(const std::string& name_, bool force) {
+ // Save the name for possible session-busy exception. Session-busy
+ // can be thrown before we have attached the handler to a valid
+ // SessionState, and in that case we need the name to send peer.detached
+ name = name_;
+ if (getState() && name == getState()->getId().getName())
+ return; // Idempotent
+ if (getState())
+ throw TransportBusyException(
+ QPID_MSG("Channel " << channel.get() << " already attached to " << getState()->getId()));
+ setState(name, force);
+ QPID_LOG(debug, "Attached channel " << channel.get() << " to " << getState()->getId());
+ peer.attached(name);
+ if (getState()->hasState())
+ peer.flush(true, true, true);
+ else
+ sendCommandPoint(getState()->senderGetCommandPoint());
+}
+
+#define CHECK_NAME(NAME, MSG) do { \
+ checkAttached(); \
+ if (NAME != getState()->getId().getName()) \
+ throw InvalidArgumentException( \
+ QPID_MSG(MSG << ": incorrect session name: " << NAME \
+ << ", expecting: " << getState()->getId().getName())); \
+ } while(0)
+
+
+void SessionHandler::attached(const std::string& name) {
+ CHECK_NAME(name, "session.attached");
+}
+
+void SessionHandler::detach(const std::string& name) {
+ CHECK_NAME(name, "session.detach");
+ peer.detached(name, session::DETACH_CODE_NORMAL);
+ handleDetach();
+}
+
+void SessionHandler::detached(const std::string& name, uint8_t code) {
+ CHECK_NAME(name, "session.detached");
+ awaitingDetached = false;
+ if (code != session::DETACH_CODE_NORMAL)
+ channelException(convert(code), "session.detached from peer.");
+ else {
+ handleDetach();
+ }
+}
+
+void SessionHandler::handleDetach() {
+ sendReady = receiveReady = false;
+}
+
+void SessionHandler::requestTimeout(uint32_t t) {
+ checkAttached();
+ getState()->setTimeout(t);
+ peer.timeout(getState()->getTimeout());
+}
+
+void SessionHandler::timeout(uint32_t t) {
+ checkAttached();
+ getState()->setTimeout(t);
+}
+
+void SessionHandler::commandPoint(const SequenceNumber& id, uint64_t offset) {
+ checkAttached();
+ getState()->receiverSetCommandPoint(SessionPoint(id, offset));
+ if (!receiveReady) {
+ receiveReady = true;
+ readyToReceive();
+ }
+}
+
+void SessionHandler::expected(const SequenceSet& commands, const Array& /*fragments*/) {
+ checkAttached();
+ if (getState()->hasState()) { // Replay
+ if (commands.empty()) throw IllegalStateException(
+ QPID_MSG(getState()->getId() << ": has state but client is attaching as new session."));
+ // TODO aconway 2008-05-12: support replay of partial commands.
+ // Here we always round down to the last command boundary.
+ SessionPoint expectedPoint = commands.empty() ? SequenceNumber(0) : SessionPoint(commands.front(),0);
+ SessionState::ReplayRange replay = getState()->senderExpected(expectedPoint);
+ sendCommandPoint(expectedPoint);
+ std::for_each(replay.begin(), replay.end(), out); // replay
+ }
+ else
+ sendCommandPoint(getState()->senderGetCommandPoint());
+}
+
+void SessionHandler::confirmed(const SequenceSet& commands, const Array& /*fragments*/) {
+ checkAttached();
+ // Ignore non-contiguous confirmations.
+ if (!commands.empty() && commands.front() >= getState()->senderGetReplayPoint())
+ getState()->senderConfirmed(commands.rangesBegin()->last());
+}
+
+void SessionHandler::completed(const SequenceSet& commands, bool timelyReply) {
+ checkAttached();
+ getState()->senderCompleted(commands);
+ if (getState()->senderNeedKnownCompleted() || timelyReply) {
+ peer.knownCompleted(commands);
+ getState()->senderRecordKnownCompleted();
+ }
+}
+
+void SessionHandler::knownCompleted(const SequenceSet& commands) {
+ checkAttached();
+ getState()->receiverKnownCompleted(commands);
+}
+
+void SessionHandler::flush(bool expected, bool confirmed, bool completed) {
+ checkAttached();
+ if (expected) {
+ SequenceSet expectSet;
+ if (getState()->hasState())
+ expectSet.add(getState()->receiverGetExpected().command);
+ peer.expected(expectSet, Array());
+ }
+ if (confirmed) {
+ SequenceSet confirmSet;
+ if (!getState()->receiverGetUnknownComplete().empty())
+ confirmSet.add(getState()->receiverGetUnknownComplete().front(),
+ getState()->receiverGetReceived().command);
+ peer.confirmed(confirmSet, Array());
+ }
+ if (completed)
+ peer.completed(getState()->receiverGetUnknownComplete(), true);
+}
+
+void SessionHandler::gap(const SequenceSet& /*commands*/) {
+ throw NotImplementedException("session.gap not supported");
+}
+
+void SessionHandler::sendDetach()
+{
+ checkAttached();
+ awaitingDetached = true;
+ peer.detach(getState()->getId().getName());
+}
+
+void SessionHandler::sendCompletion() {
+ checkAttached();
+ const SequenceSet& c = getState()->receiverGetUnknownComplete();
+ peer.completed(c, getState()->receiverNeedKnownCompleted());
+}
+
+void SessionHandler::sendAttach(bool force) {
+ QPID_LOG(debug, "SessionHandler::sendAttach attach id=" << getState()->getId());
+ peer.attach(getState()->getId().getName(), force);
+ if (getState()->hasState())
+ peer.flush(true, true, true);
+ else
+ sendCommandPoint(getState()->senderGetCommandPoint());
+}
+
+void SessionHandler::sendCommandPoint(const SessionPoint& point) {
+ peer.commandPoint(point.command, point.offset);
+ if (!sendReady) {
+ sendReady = true;
+ readyToSend();
+ }
+}
+
+void SessionHandler::markReadyToSend() {
+ if (!sendReady) {
+ sendReady = true;
+ }
+}
+
+void SessionHandler::sendTimeout(uint32_t t) {
+ checkAttached();
+ peer.requestTimeout(t);
+}
+
+void SessionHandler::sendFlush() {
+ peer.flush(false, true, true);
+}
+
+bool SessionHandler::ready() const {
+ return sendReady && receiveReady;
+}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h b/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h
new file mode 100644
index 0000000000..b5b0fe5ee0
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/SessionHandler.h
@@ -0,0 +1,118 @@
+#ifndef QPID_AMQP_0_10_SESSIONHANDLER_H
+#define QPID_AMQP_0_10_SESSIONHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/ChannelHandler.h"
+#include "qpid/framing/AMQP_AllProxy.h"
+#include "qpid/framing/AMQP_AllOperations.h"
+#include "qpid/SessionState.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+
+struct SessionException;
+
+namespace amqp_0_10 {
+
+/**
+ * Base SessionHandler with logic common to both client and broker.
+ *
+ * A SessionHandler is associated with a channel and can be attached
+ * to a session state.
+ */
+
+class QPID_COMMON_CLASS_EXTERN SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
+ public framing::FrameHandler::InOutHandler
+{
+ public:
+ QPID_COMMON_EXTERN SessionHandler(framing::FrameHandler* out=0, uint16_t channel=0);
+ QPID_COMMON_EXTERN ~SessionHandler();
+
+ void setChannel(uint16_t ch) { channel = ch; }
+ uint16_t getChannel() const { return channel.get(); }
+
+ void setOutHandler(framing::FrameHandler& h) { channel.next = &h; }
+
+ virtual SessionState* getState() = 0;
+ virtual framing::FrameHandler* getInHandler() = 0;
+
+ // Non-protocol methods, called locally to initiate some action.
+ QPID_COMMON_EXTERN void sendDetach();
+ QPID_COMMON_EXTERN void sendCompletion();
+ QPID_COMMON_EXTERN void sendAttach(bool force);
+ QPID_COMMON_EXTERN void sendTimeout(uint32_t t);
+ QPID_COMMON_EXTERN void sendFlush();
+ QPID_COMMON_EXTERN void markReadyToSend();//TODO: only needed for inter-broker bridge; cleanup
+ QPID_COMMON_EXTERN void handleException(const qpid::SessionException& e);
+
+ /** True if the handler is ready to send and receive */
+ bool ready() const;
+
+ // Protocol methods
+ QPID_COMMON_EXTERN void attach(const std::string& name, bool force);
+ QPID_COMMON_EXTERN void attached(const std::string& name);
+ QPID_COMMON_EXTERN void detach(const std::string& name);
+ QPID_COMMON_EXTERN void detached(const std::string& name, uint8_t code);
+
+ QPID_COMMON_EXTERN void requestTimeout(uint32_t t);
+ QPID_COMMON_EXTERN void timeout(uint32_t t);
+
+ QPID_COMMON_EXTERN void commandPoint(const framing::SequenceNumber& id, uint64_t offset);
+ QPID_COMMON_EXTERN void expected(const framing::SequenceSet& commands, const framing::Array& fragments);
+ QPID_COMMON_EXTERN void confirmed(const framing::SequenceSet& commands,const framing::Array& fragments);
+ QPID_COMMON_EXTERN void completed(const framing::SequenceSet& commands, bool timelyReply);
+ QPID_COMMON_EXTERN void knownCompleted(const framing::SequenceSet& commands);
+ QPID_COMMON_EXTERN void flush(bool expected, bool confirmed, bool completed);
+ QPID_COMMON_EXTERN void gap(const framing::SequenceSet& commands);
+
+ protected:
+ QPID_COMMON_EXTERN virtual void invoke(const framing::AMQMethodBody& m);
+
+ virtual void setState(const std::string& sessionName, bool force) = 0;
+ virtual void connectionException(framing::connection::CloseCode code, const std::string& msg) = 0;
+ virtual void channelException(framing::session::DetachCode, const std::string& msg) = 0;
+ virtual void executionException(framing::execution::ErrorCode, const std::string& msg) = 0;
+ virtual void detaching() = 0;
+
+ // Notification of events
+ virtual void readyToSend() {}
+ virtual void readyToReceive() {}
+
+ QPID_COMMON_EXTERN virtual void handleDetach();
+ QPID_COMMON_EXTERN virtual void handleIn(framing::AMQFrame&);
+ QPID_COMMON_EXTERN virtual void handleOut(framing::AMQFrame&);
+
+ framing::ChannelHandler channel;
+
+ private:
+ void checkAttached();
+ void sendCommandPoint(const SessionPoint&);
+
+ framing::AMQP_AllProxy::Session peer;
+ std::string name;
+ bool awaitingDetached;
+ bool sendReady, receiveReady;
+};
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_SESSIONHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Struct.h b/qpid/cpp/src/qpid/amqp_0_10/Struct.h
new file mode 100644
index 0000000000..29ece84f6e
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Struct.h
@@ -0,0 +1,60 @@
+#ifndef QPID_AMQP_0_10_STRUCT_H
+#define QPID_AMQP_0_10_STRUCT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/built_in_types.h"
+#include <iosfwd>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+// Base classes for complex types.
+
+template <class V, class CV, class H> struct Visitable {
+ typedef V Visitor;
+ typedef CV ConstVisitor;
+ typedef H Holder;
+
+ virtual ~Visitable() {}
+ virtual void accept(Visitor&) = 0;
+ virtual void accept(ConstVisitor&) const = 0;
+};
+
+
+// Note: only coded structs inherit from Struct.
+struct StructVisitor;
+struct ConstStructVisitor;
+struct StructHolder;
+struct Struct
+ : public Visitable<StructVisitor, ConstStructVisitor, StructHolder>
+{
+ uint8_t getCode() const;
+ uint8_t getPack() const;
+ uint8_t getSize() const;
+ uint8_t getClassCode() const;
+};
+std::ostream& operator<<(std::ostream&, const Struct&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_STRUCT_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp b/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp
new file mode 100644
index 0000000000..2d38c09c21
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Struct32.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/amqp_0_10/Struct32.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+Struct32::Struct32() {
+ // FIXME aconway 2008-04-16: this is only here to force a valid
+ // default-constructed Struct32 for serialize tests, clean up.
+ *this = in_place<message::MessageResumeResult>();
+}
+
+std::ostream& operator<<(std::ostream& o, const Struct32& s) {
+ return o << static_cast<const StructHolder&>(s);
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Struct32.h b/qpid/cpp/src/qpid/amqp_0_10/Struct32.h
new file mode 100644
index 0000000000..2ed73e0b4c
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Struct32.h
@@ -0,0 +1,64 @@
+#ifndef QPID_AMQP_0_10_STRUCT32_H
+#define QPID_AMQP_0_10_STRUCT32_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/StructHolder.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class Struct32 : public StructHolder
+{
+ public:
+ Struct32();
+
+ template <class T> explicit Struct32(const T& t) : StructHolder(t) {}
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+
+ using StructHolder::operator=;
+
+ template <class S> void encode(S& s) const {
+ s(contentSize());
+ const_cast<Struct32*>(this)->StructHolder::serialize(s);
+ }
+
+ template <class S> void decode(S& s) {
+ uint32_t contentSz;
+ s(contentSz);
+ typename S::ScopedLimit l(s, contentSz);
+ StructHolder::serialize(s);
+ }
+
+ private:
+ uint32_t contentSize() const {
+ return Codec::size(static_cast<const StructHolder&>(*this));
+ }
+
+};
+
+std::ostream& operator<<(std::ostream&, const Struct32&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_STRUCT32_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp b/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp
new file mode 100644
index 0000000000..381de76dcc
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Unit.h"
+#include "qpid/amqp_0_10/Codec.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+void Unit::updateVariant() {
+ switch (header.getType()) {
+ case CONTROL: variant=ControlHolder(); break;
+ case COMMAND: variant=CommandHolder(); break;
+ case HEADER: variant=Header(); break;
+ case BODY: variant=Body(header.getDataSize()); break;
+ default: assert(0); // FIXME aconway 2008-04-14: exception?
+ }
+}
+
+struct GetTypeVisitor : public boost::static_visitor<SegmentType> {
+ SegmentType operator()(const CommandHolder& ) const { return COMMAND; }
+ SegmentType operator()(const ControlHolder& ) const { return CONTROL; }
+ SegmentType operator()(const Header& ) const { return HEADER; }
+ SegmentType operator()(const Body&) const { return BODY; }
+};
+
+struct GetFlagsVisitor : public boost::static_visitor<uint8_t> {
+ uint8_t operator()(const CommandHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; }
+ uint8_t operator()(const ControlHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; }
+ uint8_t operator()(const Header& ) const { return FIRST_FRAME|LAST_FRAME; }
+ uint8_t operator()(const Body&) const { return 0; }
+};
+
+void Unit::updateHeader(uint8_t flags) {
+ GetFlagsVisitor flagger;
+ header.setFlags(flags | variant.apply_visitor(flagger));
+ GetTypeVisitor getter;
+ header.setType(variant.apply_visitor(getter));
+ header.setDataSize(Codec::size(*this));
+ // track automatically set from type.
+ // no channel specified at this point.
+}
+
+std::ostream& operator<<(std::ostream& o, const Unit& u) {
+ return o << u.getHeader() << " " << u.variant.type().name() << "[" << u.variant << "]";
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/Unit.h b/qpid/cpp/src/qpid/amqp_0_10/Unit.h
new file mode 100644
index 0000000000..0229e07419
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/Unit.h
@@ -0,0 +1,82 @@
+#ifndef QPID_AMQP_0_10_UNIT_H
+#define QPID_AMQP_0_10_UNIT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/ControlHolder.h"
+#include "qpid/amqp_0_10/CommandHolder.h"
+#include "qpid/amqp_0_10/Header.h"
+#include "qpid/amqp_0_10/Body.h"
+#include "qpid/amqp_0_10/FrameHeader.h"
+
+#include <boost/variant.hpp>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/**
+ * A Unit contains a frame header and associated value.
+ * For all types except BODY the frame header is for a complete segment.
+ */
+class Unit {
+ public:
+ explicit Unit(const FrameHeader& h=FrameHeader()) : header(h) { updateVariant(); }
+
+ /**
+ *@param flags: is ORed with the required flags for type T.
+ */
+ template <class T>
+ explicit Unit(const T& t, uint8_t flags=0) : variant(t) { updateHeader(flags); }
+
+ void setHeader(FrameHeader& h) { header = h; updateVariant(); }
+ const FrameHeader& getHeader() const { return header; }
+
+ template<class T> const T* get() const { return boost::get<T>(&variant); }
+ template<class T> T* get() { return boost::get<T>(&variant); }
+ template<class T> Unit& operator=(const T& t) { variant=t; return *this; }
+
+ template <class V> typename V::result_type applyVisitor(V& v) const {
+ variant.apply_visitor(v);
+ }
+
+ template <class S> void serialize(S& s) { variant.apply_visitor(s); s.split(*this); }
+ template <class S> void encode(S&) const {}
+ template <class S> void decode(S&) { updateHeader(header.getFlags()); }
+
+ private:
+ typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant;
+
+ void updateHeader(uint8_t flags);
+ void updateVariant();
+
+ Variant variant;
+ FrameHeader header;
+
+ friend std::ostream& operator<<(std::ostream& o, const Unit& u);
+};
+
+std::ostream& operator<<(std::ostream& o, const Unit& u);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_UNIT_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h b/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h
new file mode 100644
index 0000000000..93a8ce573a
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h
@@ -0,0 +1,35 @@
+#ifndef QPID_AMQP_0_10_UNITHANDLER_H
+#define QPID_AMQP_0_10_UNITHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/Handler.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class Unit;
+typedef framing::Handler<const Unit&> UnitHandler;
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_UNITHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp b/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp
new file mode 100644
index 0000000000..35445054c9
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/amqp_0_10/StructVisitor.h"
+#include "qpid/amqp_0_10/UnknownStruct.h"
+
+namespace qpid {
+namespace amqp_0_10 {
+
+void UnknownStruct::accept(Visitor& v) { v.visit(*this); }
+void UnknownStruct::accept(ConstVisitor& v) const { v.visit(*this); }
+std::ostream& operator<<(std::ostream& o, const UnknownStruct& u) {
+ return o << "UnknownStruct[class=" << u.getClassCode() << " code=" << u.getCode() << "]";
+}
+
+}} // namespace qpid::amqp_0_10
diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h b/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h
new file mode 100644
index 0000000000..1c66d8e6af
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownStruct.h
@@ -0,0 +1,55 @@
+#ifndef QPID_AMQP_0_10_UNKNOWNSTRUCT_H
+#define QPID_AMQP_0_10_UNKNOWNSTRUCT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp_0_10/Struct.h"
+#include <string>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class UnknownStruct : public Struct {
+ public:
+ static const uint8_t SIZE=4;
+ static const uint8_t PACK=2;
+
+ template <class S> void serialize(S& s) { s.split(*this); s(data.begin(), data.end()); }
+ template <class S> void encode(S&) const { }
+ template <class S> void decode(S& s) { data.resize(s.bytesRemaining()); }
+
+ UnknownStruct(uint8_t cc=0, uint8_t c=0) : classCode(cc), code(c) {}
+ void accept(Visitor&);
+ void accept(ConstVisitor&) const;
+
+ uint8_t getClassCode() const { return classCode; }
+ uint8_t getCode() const { return code; }
+
+ private:
+ uint8_t classCode, code;
+ std::string data;
+};
+
+std::ostream& operator<<(std::ostream&, const UnknownStruct&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_UNKNOWNSTRUCT_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp
new file mode 100644
index 0000000000..cd45dd76db
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/UnknownType.h"
+#include <boost/range/iterator_range.hpp>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+UnknownType::Width UnknownType::WidthTable[16] = {
+ { 1, 0 },
+ { 2, 0 },
+ { 4, 0 },
+ { 8, 0 },
+ { 16, 0 },
+ { 32, 0 },
+ { 64, 0 },
+ { 128, 0 },
+ { 0, 1 },
+ { 0, 2 },
+ { 0, 4 },
+ { -1, -1 }, // Invalid
+ { 5, 0 },
+ { 9, 0 },
+ { -1, -1 }, // Invalid
+ { 0, 0 }
+};
+
+int UnknownType::fixed() const { return WidthTable[code>>4].fixed; }
+int UnknownType::variable() const { return WidthTable[code>>4].variable; }
+UnknownType::UnknownType(uint8_t c) : code(c) { data.resize(fixed()); }
+
+std::ostream& operator<<(std::ostream& o, const UnknownType& u) {
+ return o << boost::make_iterator_range(u.begin(), u.end()) << std::endl;
+}
+
+}} // namespace qpid::amqp_0_10
+
diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h
new file mode 100644
index 0000000000..77498871b3
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h
@@ -0,0 +1,87 @@
+#ifndef QPID_AMQP_0_10_UNKNOWNTYPE_H
+#define QPID_AMQP_0_10_UNKNOWNTYPE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/IntegerTypes.h"
+#include <vector>
+#include <iosfwd>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Encode/decode an unknown type based on typecode. */
+class UnknownType {
+ public:
+ UnknownType(uint8_t code=0);
+ uint8_t getCode() const { return code; }
+ /** Size of fixed type or 0 if not fixed/0-length. -1 invalid */
+ int fixed() const;
+ /** Bytes in size type for variable width. -1 invalid */
+ int variable() const;
+
+ typedef std::vector<char>::const_iterator const_iterator;
+ const_iterator begin() const { return data.begin(); }
+ const_iterator end() const { return data.end(); }
+ size_t size() const { return data.size(); }
+
+ template <class S> void serialize(S& s) { s.split(*this); }
+ template <class S> void encode(S& s) const;
+ template <class S> void decode(S& s);
+
+ private:
+ uint8_t code;
+ struct Width { int fixed; int variable; };
+ static Width WidthTable[16];
+
+ std::vector<char> data;
+};
+
+template <class S> void UnknownType::encode(S& s) const {
+ switch (variable()) {
+ case 0: break;
+ case 1: s(uint8_t(data.size())); break;
+ case 2: s(uint16_t(data.size())); break;
+ case 4: s(uint32_t(data.size())); break;
+ }
+ s(data.begin(), data.end());
+}
+
+template <class S> void UnknownType::decode(S& s) {
+ uint32_t s8;
+ uint32_t s16;
+ uint32_t s32;
+ switch (variable()) {
+ case 0: break;
+ case 1: s(s8); data.resize(s8); break;
+ case 2: s(s16); data.resize(s16); break;
+ case 4: s(s32); data.resize(s32); break;
+ }
+ s(data.begin(), data.end());
+}
+
+inline uint8_t codeFor(const UnknownType& u) { return u.getCode(); }
+
+std::ostream& operator<<(std::ostream&, const UnknownType&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_UNKNOWNTYPE_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/apply.h b/qpid/cpp/src/qpid/amqp_0_10/apply.h
new file mode 100644
index 0000000000..f32b3482ef
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/apply.h
@@ -0,0 +1,86 @@
+#ifndef QPID_AMQP_0_10_APPLY_H
+#define QPID_AMQP_0_10_APPLY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/optional.hpp>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+template <class F, class R=typename F::result_type> struct FunctionAndResult {
+ F* functor;
+ boost::optional<R> result;
+
+ FunctionAndResult() : functor(0) {}
+ template <class T> void invoke(T& t) { result=(*functor)(t); }
+ template <class T> void invoke(const T& t) { result=(*functor)(t); }
+ R getResult() { return *result; }
+};
+
+// void result is special case.
+template <class F> struct FunctionAndResult<F, void> {
+ F* functor;
+
+ FunctionAndResult() : functor(0) {}
+ template <class T> void invoke(T& t) { (*functor)(t); }
+ void getResult() {}
+};
+
+// Metafunction returning correct abstract visitor for Visitable type.
+template <class Visitable> struct VisitorType {
+ typedef typename Visitable::Visitor type;
+};
+template <class Visitable> struct VisitorType<const Visitable> {
+ typedef typename Visitable::ConstVisitor type;
+};
+
+template <class Visitor, class F>
+struct ApplyVisitorBase : public Visitor, public FunctionAndResult<F> {};
+
+// Specialize for each visitor type
+template <class Visitable, class F> struct ApplyVisitor;
+
+/** Apply a functor to a visitable object.
+ * The functor can have operator() overloads for each visitable type
+ * and/or templated operator().
+ */
+template <class F, class Visitable>
+typename F::result_type apply(F& functor, Visitable& visitable) {
+ ApplyVisitor<typename VisitorType<Visitable>::type, F> visitor;
+ visitor.functor=&functor;
+ visitable.accept(visitor);
+ return visitor.getResult();
+}
+
+template <class F, class Visitable>
+typename F::result_type apply(const F& functor, Visitable& visitable) {
+ ApplyVisitor<typename VisitorType<Visitable>::type, const F> visitor;
+ visitor.functor=&functor;
+ visitable.accept(visitor);
+ return visitor.getResult();
+}
+
+template <class R> struct ApplyFunctor { typedef R result_type; };
+
+}} // namespace qpid::amqp_0_10
+
+#endif /*!QPID_AMQP_0_10_APPLY_H*/
diff --git a/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h
new file mode 100644
index 0000000000..e95d1cf3e9
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h
@@ -0,0 +1,171 @@
+#ifndef QPID_AMQP_0_10_BUILT_IN_TYPES_H
+#define QPID_AMQP_0_10_BUILT_IN_TYPES_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Serializer.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/Time.h"
+#include "qpid/amqp_0_10/Decimal.h"
+#include "qpid/amqp_0_10/SerializableString.h"
+#include <boost/array.hpp>
+#include <boost/range/iterator_range.hpp>
+#include <string>
+#include <ostream>
+#include <vector>
+
+/**@file Mapping from built-in AMQP types to C++ types */
+
+namespace qpid {
+
+namespace framing {
+class SequenceNumber;
+class SequenceSet;
+}
+
+namespace amqp_0_10 {
+
+/** Wrapper that behaves like type T but is a distinct type for
+ * overloading purposes. Unique allows multiple distinc wrappers.
+ */
+template <class T, int Unique=0> struct Wrapper {
+ T value;
+ Wrapper() {}
+ Wrapper(const T& x) : value(x) {}
+ Wrapper& operator=(const T& x) { value=x; return *this; }
+ operator T&() { return value; }
+ operator const T&() const { return value; }
+ template <class S> void serialize(S& s) { s(value); }
+};
+
+template<class T>
+inline std::ostream& operator<<(std::ostream& o, const Wrapper<T>& w) {
+ return o << w.value;
+}
+
+/** Void type */
+struct Void { template <class S> void serialize(S&) {} };
+inline std::ostream& operator<<(std::ostream& o, const Void&) { return o; }
+
+/** Bit is a presence indicator - an optional value with no encoding. */
+struct Bit : public Wrapper<bool> {
+ Bit(bool b=false) : Wrapper<bool>(b) {}
+ using Wrapper<bool>::operator=;
+ template <class S> void serialize(S& s) { s.split(*this); }
+ template <class S> void encode(S&) const { }
+ template <class S> void decode(S&) { *this = true; }
+};
+
+inline std::ostream& operator<<(std::ostream& o, const Bit& b) {
+ return o << bool(b);
+}
+
+// Fixed size types
+typedef bool Boolean;
+typedef char Char;
+typedef int8_t Int8;
+typedef int16_t Int16;
+typedef int32_t Int32;
+typedef int64_t Int64;
+typedef uint8_t Uint8;
+typedef uint16_t Uint16;
+typedef uint32_t Uint32;
+typedef uint64_t Uint64;
+typedef Wrapper<uint32_t> CharUtf32;
+
+template <size_t N> struct Bin : public boost::array<char, N> {
+ template <class S> void serialize(S& s) { s.raw(this->begin(), this->size()); }
+};
+
+template <size_t N> std::ostream& operator<<(std::ostream& o, const Bin<N>& b) {
+ return o << boost::make_iterator_range(b.begin(), b.end());
+}
+
+template <> struct Bin<1> : public boost::array<char, 1> {
+ Bin(char c=0) { this->front() = c; }
+ operator char() { return this->front(); }
+ template <class S> void serialize(S& s) { s(front()); }
+};
+
+typedef Bin<1> Bin8;
+typedef Bin<128> Bin1024;
+typedef Bin<16> Bin128;
+typedef Bin<2> Bin16;
+typedef Bin<32> Bin256;
+typedef Bin<4> Bin32;
+typedef Bin<5> Bin40;
+typedef Bin<64> Bin512;
+typedef Bin<8> Bin64;
+typedef Bin<9> Bin72;
+
+typedef double Double;
+typedef float Float;
+typedef framing::SequenceNumber SequenceNo;
+using framing::Uuid;
+typedef sys::AbsTime Datetime;
+
+typedef Decimal<Uint8, Int32> Dec32;
+typedef Decimal<Uint8, Int64> Dec64;
+
+// Variable width types
+
+typedef SerializableString<Uint8, Uint8> Vbin8;
+typedef SerializableString<char, Uint8, 1> Str8Latin;
+typedef SerializableString<char, Uint8> Str8;
+typedef SerializableString<Uint16, Uint8> Str8Utf16;
+
+typedef SerializableString<Uint8, Uint16> Vbin16;
+typedef SerializableString<char, Uint16, 1> Str16Latin;
+typedef SerializableString<char, Uint16> Str16;
+typedef SerializableString<Uint16, Uint16> Str16Utf16;
+
+typedef SerializableString<Uint8, Uint32> Vbin32;
+
+typedef framing::SequenceSet SequenceSet;
+
+// Forward declare class types.
+class Map;
+class Struct32;
+class UnknownType;
+
+template <class T> struct ArrayDomain;
+typedef ArrayDomain<UnknownType> Array;
+
+// FIXME aconway 2008-04-08: TODO
+struct ByteRanges { template <class S> void serialize(S&) {} };
+struct List { template <class S> void serialize(S&) {} };
+
+// FIXME aconway 2008-03-10: dummy ostream operators
+inline std::ostream& operator<<(std::ostream& o, const ByteRanges&) { return o; }
+inline std::ostream& operator<<(std::ostream& o, const SequenceSet&) { return o; }
+inline std::ostream& operator<<(std::ostream& o, const List&) { return o; }
+
+enum SegmentType { CONTROL, COMMAND, HEADER, BODY };
+
+inline SerializeAs<SegmentType, uint8_t> serializable(SegmentType& st) {
+ return SerializeAs<SegmentType, uint8_t>(st);
+}
+
+
+}} // namespace qpid::amqp_0_10
+
+#endif
diff --git a/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp b/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp
new file mode 100644
index 0000000000..656d363ba6
--- /dev/null
+++ b/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/amqp_0_10/UnknownStruct.h"
+#include "qpid/amqp_0_10/ApplyCommand.h"
+#include "qpid/amqp_0_10/ApplyControl.h"
+#include "qpid/amqp_0_10/ApplyStruct.h"
+#include "qpid/amqp_0_10/apply.h"
+#include <iostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+// Functors for getting static values from a visitable base type.
+
+#define QPID_STATIC_VALUE_GETTER(NAME, TYPE, VALUE) \
+ struct NAME : public ApplyFunctor<TYPE> { \
+ template <class T> TYPE operator()(const T&) const { return T::VALUE; }\
+ }
+
+QPID_STATIC_VALUE_GETTER(GetCode, uint8_t, CODE);
+QPID_STATIC_VALUE_GETTER(GetSize, uint8_t, SIZE);
+QPID_STATIC_VALUE_GETTER(GetPack, uint8_t, PACK);
+QPID_STATIC_VALUE_GETTER(GetClassCode, uint8_t, CLASS_CODE);
+QPID_STATIC_VALUE_GETTER(GetName, const char*, NAME);
+QPID_STATIC_VALUE_GETTER(GetClassName, const char*, CLASS_NAME);
+
+
+uint8_t Command::getCode() const { return apply(GetCode(), *this); }
+uint8_t Command::getClassCode() const { return apply(GetClassCode(), *this); }
+const char* Command::getName() const { return apply(GetName(), *this); }
+const char* Command::getClassName() const { return apply(GetClassName(), *this); }
+
+uint8_t Control::getCode() const { return apply(GetCode(), *this); }
+uint8_t Control::getClassCode() const { return apply(GetClassCode(), *this); }
+const char* Control::getName() const { return apply(GetName(), *this); }
+const char* Control::getClassName() const { return apply(GetClassName(), *this); }
+
+// Special cases for UnknownStruct
+struct GetStructCode : public GetCode {
+ using GetCode::operator();
+ uint8_t operator()(const UnknownStruct& u) const { return u.getCode(); }
+};
+
+struct GetStructClassCode : public GetClassCode {
+ using GetClassCode::operator();
+ uint8_t operator()(const UnknownStruct& u) const { return u.getClassCode(); }
+};
+
+uint8_t Struct::getCode() const { return apply(GetStructCode(), *this); }
+uint8_t Struct::getClassCode() const { return apply(GetStructClassCode(), *this); }
+uint8_t Struct::getPack() const { return apply(GetPack(), *this); }
+uint8_t Struct::getSize() const { return apply(GetSize(), *this); }
+
+struct PrintVisitor {
+ typedef std::ostream& result_type;
+ std::ostream& out;
+ PrintVisitor(std::ostream& o) : out(o) {}
+ template <class T> result_type operator()(const T& t) const { return out << t; }
+};
+
+std::ostream& operator<<(std::ostream& o, const Command& x) { return apply(PrintVisitor(o), x); }
+std::ostream& operator<<(std::ostream& o, const Control& x) { return apply(PrintVisitor(o), x); }
+std::ostream& operator<<(std::ostream& o, const Struct& x) { return apply(PrintVisitor(o), x); }
+
+}} // namespace qpid::amqp_0_10
+
diff --git a/qpid/cpp/src/qpid/assert.cpp b/qpid/cpp/src/qpid/assert.cpp
new file mode 100644
index 0000000000..801bfa6ae5
--- /dev/null
+++ b/qpid/cpp/src/qpid/assert.cpp
@@ -0,0 +1,47 @@
+#ifndef QPID_ASSERT_CPP
+#define QPID_ASSERT_CPP
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <sstream>
+#include <iostream>
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include <stdlib.h>
+
+namespace qpid {
+
+void assert_fail(char const * expr, char const * function, char const * file, long line) {
+ std::ostringstream msg;
+ msg << "Assertion failed: " << expr << " in function " << function
+ << "(" << file << ":" << line << ")";
+ QPID_LOG(critical, msg.str());
+#ifdef NDEBUG
+ throw framing::InternalErrorException(msg.str());
+#else
+ std::cerr << msg.str() << std::endl;
+ abort();
+#endif
+}
+
+} // namespace qpid
+
+#endif /*!QPID_ASSERT_CPP*/
diff --git a/qpid/cpp/src/qpid/assert.h b/qpid/cpp/src/qpid/assert.h
new file mode 100644
index 0000000000..49e7c5355d
--- /dev/null
+++ b/qpid/cpp/src/qpid/assert.h
@@ -0,0 +1,38 @@
+#ifndef QPID_ASSERT_H
+#define QPID_ASSERT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/current_function.hpp>
+
+/**
+ * Abort if !expr in debug mode, throw an exception if NDEBUG is set.
+ */
+#define QPID_ASSERT(expr) ((expr) ? static_cast<void>(0) : ::qpid::assert_fail(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))
+
+namespace qpid {
+
+void assert_fail(char const * expr, char const * function, char const * file, long line);
+
+} // namespace qpid
+
+#endif /*!QPID_ASSERT_H*/
diff --git a/qpid/cpp/src/qpid/broker/AclModule.h b/qpid/cpp/src/qpid/broker/AclModule.h
new file mode 100644
index 0000000000..2f4f7eaacc
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/AclModule.h
@@ -0,0 +1,281 @@
+#ifndef QPID_ACLMODULE_ACL_H
+#define QPID_ACLMODULE_ACL_H
+
+
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "qpid/RefCounted.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+
+namespace qpid {
+
+namespace acl {
+
+enum ObjectType {OBJ_QUEUE, OBJ_EXCHANGE, OBJ_BROKER, OBJ_LINK,
+ OBJ_METHOD, OBJECTSIZE}; // OBJECTSIZE must be last in list
+enum Action {ACT_CONSUME, ACT_PUBLISH, ACT_CREATE, ACT_ACCESS, ACT_BIND,
+ ACT_UNBIND, ACT_DELETE, ACT_PURGE, ACT_UPDATE,
+ ACTIONSIZE}; // ACTIONSIZE must be last in list
+enum Property {PROP_NAME, PROP_DURABLE, PROP_OWNER, PROP_ROUTINGKEY,
+ PROP_PASSIVE, PROP_AUTODELETE, PROP_EXCLUSIVE, PROP_TYPE,
+ PROP_ALTERNATE, PROP_QUEUENAME, PROP_SCHEMAPACKAGE,
+ PROP_SCHEMACLASS, PROP_POLICYTYPE, PROP_MAXQUEUESIZE,
+ PROP_MAXQUEUECOUNT};
+enum AclResult {ALLOW, ALLOWLOG, DENY, DENYLOG};
+
+} // namespace acl
+
+namespace broker {
+
+
+class AclModule
+{
+
+public:
+
+ // effienty turn off ACL on message transfer.
+ virtual bool doTransferAcl()=0;
+
+ virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& name,
+ std::map<acl::Property, std::string>* params=0)=0;
+ virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& ExchangeName,
+ const std::string& RoutingKey)=0;
+ // create specilied authorise methods for cases that need faster matching as needed.
+
+ virtual ~AclModule() {};
+};
+
+} // namespace broker
+
+namespace acl {
+
+class AclHelper {
+ private:
+ AclHelper(){}
+ public:
+ static inline ObjectType getObjectType(const std::string& str) {
+ if (str.compare("queue") == 0) return OBJ_QUEUE;
+ if (str.compare("exchange") == 0) return OBJ_EXCHANGE;
+ if (str.compare("broker") == 0) return OBJ_BROKER;
+ if (str.compare("link") == 0) return OBJ_LINK;
+ if (str.compare("method") == 0) return OBJ_METHOD;
+ throw str;
+ }
+ static inline std::string getObjectTypeStr(const ObjectType o) {
+ switch (o) {
+ case OBJ_QUEUE: return "queue";
+ case OBJ_EXCHANGE: return "exchange";
+ case OBJ_BROKER: return "broker";
+ case OBJ_LINK: return "link";
+ case OBJ_METHOD: return "method";
+ default: assert(false); // should never get here
+ }
+ return "";
+ }
+ static inline Action getAction(const std::string& str) {
+ if (str.compare("consume") == 0) return ACT_CONSUME;
+ if (str.compare("publish") == 0) return ACT_PUBLISH;
+ if (str.compare("create") == 0) return ACT_CREATE;
+ if (str.compare("access") == 0) return ACT_ACCESS;
+ if (str.compare("bind") == 0) return ACT_BIND;
+ if (str.compare("unbind") == 0) return ACT_UNBIND;
+ if (str.compare("delete") == 0) return ACT_DELETE;
+ if (str.compare("purge") == 0) return ACT_PURGE;
+ if (str.compare("update") == 0) return ACT_UPDATE;
+ throw str;
+ }
+ static inline std::string getActionStr(const Action a) {
+ switch (a) {
+ case ACT_CONSUME: return "consume";
+ case ACT_PUBLISH: return "publish";
+ case ACT_CREATE: return "create";
+ case ACT_ACCESS: return "access";
+ case ACT_BIND: return "bind";
+ case ACT_UNBIND: return "unbind";
+ case ACT_DELETE: return "delete";
+ case ACT_PURGE: return "purge";
+ case ACT_UPDATE: return "update";
+ default: assert(false); // should never get here
+ }
+ return "";
+ }
+ static inline Property getProperty(const std::string& str) {
+ if (str.compare("name") == 0) return PROP_NAME;
+ if (str.compare("durable") == 0) return PROP_DURABLE;
+ if (str.compare("owner") == 0) return PROP_OWNER;
+ if (str.compare("routingkey") == 0) return PROP_ROUTINGKEY;
+ if (str.compare("passive") == 0) return PROP_PASSIVE;
+ if (str.compare("autodelete") == 0) return PROP_AUTODELETE;
+ if (str.compare("exclusive") == 0) return PROP_EXCLUSIVE;
+ if (str.compare("type") == 0) return PROP_TYPE;
+ if (str.compare("alternate") == 0) return PROP_ALTERNATE;
+ if (str.compare("queuename") == 0) return PROP_QUEUENAME;
+ if (str.compare("schemapackage") == 0) return PROP_SCHEMAPACKAGE;
+ if (str.compare("schemaclass") == 0) return PROP_SCHEMACLASS;
+ if (str.compare("policytype") == 0) return PROP_POLICYTYPE;
+ if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE;
+ if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT;
+ throw str;
+ }
+ static inline std::string getPropertyStr(const Property p) {
+ switch (p) {
+ case PROP_NAME: return "name";
+ case PROP_DURABLE: return "durable";
+ case PROP_OWNER: return "owner";
+ case PROP_ROUTINGKEY: return "routingkey";
+ case PROP_PASSIVE: return "passive";
+ case PROP_AUTODELETE: return "autodelete";
+ case PROP_EXCLUSIVE: return "exclusive";
+ case PROP_TYPE: return "type";
+ case PROP_ALTERNATE: return "alternate";
+ case PROP_QUEUENAME: return "queuename";
+ case PROP_SCHEMAPACKAGE: return "schemapackage";
+ case PROP_SCHEMACLASS: return "schemaclass";
+ case PROP_POLICYTYPE: return "policytype";
+ case PROP_MAXQUEUESIZE: return "maxqueuesize";
+ case PROP_MAXQUEUECOUNT: return "maxqueuecount";
+ default: assert(false); // should never get here
+ }
+ return "";
+ }
+ static inline AclResult getAclResult(const std::string& str) {
+ if (str.compare("allow") == 0) return ALLOW;
+ if (str.compare("allow-log") == 0) return ALLOWLOG;
+ if (str.compare("deny") == 0) return DENY;
+ if (str.compare("deny-log") == 0) return DENYLOG;
+ throw str;
+ }
+ static inline std::string getAclResultStr(const AclResult r) {
+ switch (r) {
+ case ALLOW: return "allow";
+ case ALLOWLOG: return "allow-log";
+ case DENY: return "deny";
+ case DENYLOG: return "deny-log";
+ default: assert(false); // should never get here
+ }
+ return "";
+ }
+
+ typedef std::set<Property> propSet;
+ typedef boost::shared_ptr<propSet> propSetPtr;
+ typedef std::pair<Action, propSetPtr> actionPair;
+ typedef std::map<Action, propSetPtr> actionMap;
+ typedef boost::shared_ptr<actionMap> actionMapPtr;
+ typedef std::pair<ObjectType, actionMapPtr> objectPair;
+ typedef std::map<ObjectType, actionMapPtr> objectMap;
+ typedef objectMap::const_iterator omCitr;
+ typedef boost::shared_ptr<objectMap> objectMapPtr;
+ typedef std::map<Property, std::string> propMap;
+ typedef propMap::const_iterator propMapItr;
+
+ // This map contains the legal combinations of object/action/properties found in an ACL file
+ static void loadValidationMap(objectMapPtr& map) {
+ if (!map.get()) return;
+ map->clear();
+ propSetPtr p0; // empty ptr, used for no properties
+
+ // == Exchanges ==
+
+ propSetPtr p1(new propSet);
+ p1->insert(PROP_TYPE);
+ p1->insert(PROP_ALTERNATE);
+ p1->insert(PROP_PASSIVE);
+ p1->insert(PROP_DURABLE);
+
+ propSetPtr p2(new propSet);
+ p2->insert(PROP_ROUTINGKEY);
+
+ propSetPtr p3(new propSet);
+ p3->insert(PROP_QUEUENAME);
+ p3->insert(PROP_ROUTINGKEY);
+
+ actionMapPtr a0(new actionMap);
+ a0->insert(actionPair(ACT_CREATE, p1));
+ a0->insert(actionPair(ACT_DELETE, p0));
+ a0->insert(actionPair(ACT_ACCESS, p0));
+ a0->insert(actionPair(ACT_BIND, p2));
+ a0->insert(actionPair(ACT_UNBIND, p2));
+ a0->insert(actionPair(ACT_ACCESS, p3));
+ a0->insert(actionPair(ACT_PUBLISH, p0));
+
+ map->insert(objectPair(OBJ_EXCHANGE, a0));
+
+ // == Queues ==
+
+ propSetPtr p4(new propSet);
+ p4->insert(PROP_ALTERNATE);
+ p4->insert(PROP_PASSIVE);
+ p4->insert(PROP_DURABLE);
+ p4->insert(PROP_EXCLUSIVE);
+ p4->insert(PROP_AUTODELETE);
+ p4->insert(PROP_POLICYTYPE);
+ p4->insert(PROP_MAXQUEUESIZE);
+ p4->insert(PROP_MAXQUEUECOUNT);
+
+ actionMapPtr a1(new actionMap);
+ a1->insert(actionPair(ACT_ACCESS, p0));
+ a1->insert(actionPair(ACT_CREATE, p4));
+ a1->insert(actionPair(ACT_PURGE, p0));
+ a1->insert(actionPair(ACT_DELETE, p0));
+ a1->insert(actionPair(ACT_CONSUME, p0));
+
+ map->insert(objectPair(OBJ_QUEUE, a1));
+
+ // == Links ==
+
+ actionMapPtr a2(new actionMap);
+ a2->insert(actionPair(ACT_CREATE, p0));
+
+ map->insert(objectPair(OBJ_LINK, a2));
+
+ // == Method ==
+
+ propSetPtr p5(new propSet);
+ p5->insert(PROP_SCHEMAPACKAGE);
+ p5->insert(PROP_SCHEMACLASS);
+
+ actionMapPtr a4(new actionMap);
+ a4->insert(actionPair(ACT_ACCESS, p5));
+
+ map->insert(objectPair(OBJ_METHOD, a4));
+ }
+
+ static std::string propertyMapToString(const std::map<Property, std::string>* params) {
+ std::ostringstream ss;
+ ss << "{";
+ if (params)
+ {
+ for (propMapItr pMItr = params->begin(); pMItr != params->end(); pMItr++) {
+ ss << " " << getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second;
+ }
+ }
+ ss << " }";
+ return ss.str();
+ }
+};
+
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACLMODULE_ACL_H
diff --git a/qpid/cpp/src/qpid/broker/AsyncCompletion.h b/qpid/cpp/src/qpid/broker/AsyncCompletion.h
new file mode 100644
index 0000000000..fef994438f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/AsyncCompletion.h
@@ -0,0 +1,201 @@
+#ifndef _AsyncCompletion_
+#define _AsyncCompletion_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/intrusive_ptr.hpp>
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Monitor.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Class to implement asynchronous notification of completion.
+ *
+ * Use-case: An "initiator" needs to wait for a set of "completers" to
+ * finish a unit of work before an action can occur. This object
+ * tracks the progress of the set of completers, and allows the action
+ * to occur once all completers have signalled that they are done.
+ *
+ * The initiator and completers may be running in separate threads.
+ *
+ * The initiating thread is the thread that initiates the action,
+ * i.e. the connection read thread.
+ *
+ * A completing thread is any thread that contributes to completion,
+ * e.g. a store thread that does an async write.
+ * There may be zero or more completers.
+ *
+ * When the work is complete, a callback is invoked. The callback
+ * may be invoked in the Initiator thread, or one of the Completer
+ * threads. The callback is passed a flag indicating whether or not
+ * the callback is running under the context of the Initiator thread.
+ *
+ * Use model:
+ * 1) Initiator thread invokes begin()
+ * 2) After begin() has been invoked, zero or more Completers invoke
+ * startCompleter(). Completers may be running in the same or
+ * different thread as the Initiator, as long as they guarantee that
+ * startCompleter() is invoked at least once before the Initiator invokes end().
+ * 3) Completers may invoke finishCompleter() at any time, even after the
+ * initiator has invoked end(). finishCompleter() may be called from any
+ * thread.
+ * 4) startCompleter()/finishCompleter() calls "nest": for each call to
+ * startCompleter(), a corresponding call to finishCompleter() must be made.
+ * Once the last finishCompleter() is called, the Completer must no longer
+ * reference the completion object.
+ * 5) The Initiator invokes end() at the point where it has finished
+ * dispatching work to the Completers, and is prepared for the callback
+ * handler to be invoked. Note: if there are no outstanding Completers
+ * pending when the Initiator invokes end(), the callback will be invoked
+ * directly, and the sync parameter will be set true. This indicates to the
+ * Initiator that the callback is executing in the context of the end() call,
+ * and the Initiator is free to optimize the handling of the completion,
+ * assuming no need for synchronization with Completer threads.
+ */
+
+class AsyncCompletion
+{
+ public:
+
+ /** Supplied by the Initiator to the end() method, allows for a callback
+ * when all outstanding completers are done. If the callback cannot be
+ * made during the end() call, the clone() method must supply a copy of
+ * this callback object that persists after end() returns. The cloned
+ * callback object will be used by the last completer thread, and
+ * released when the callback returns.
+ */
+ class Callback : public RefCounted
+ {
+ public:
+ virtual void completed(bool) = 0;
+ virtual boost::intrusive_ptr<Callback> clone() = 0;
+ };
+
+ private:
+ mutable qpid::sys::AtomicValue<uint32_t> completionsNeeded;
+ mutable qpid::sys::Monitor callbackLock;
+ bool inCallback, active;
+
+ void invokeCallback(bool sync) {
+ qpid::sys::Mutex::ScopedLock l(callbackLock);
+ if (active) {
+ if (callback.get()) {
+ inCallback = true;
+ {
+ qpid::sys::Mutex::ScopedUnlock ul(callbackLock);
+ callback->completed(sync);
+ }
+ inCallback = false;
+ callback = boost::intrusive_ptr<Callback>();
+ callbackLock.notifyAll();
+ }
+ active = false;
+ }
+ }
+
+ protected:
+ /** Invoked when all completers have signalled that they have completed
+ * (via calls to finishCompleter()). bool == true if called via end()
+ */
+ boost::intrusive_ptr<Callback> callback;
+
+ public:
+ AsyncCompletion() : completionsNeeded(0), inCallback(false), active(true) {};
+ virtual ~AsyncCompletion() { cancel(); }
+
+
+ /** True when all outstanding operations have compeleted
+ */
+ bool isDone()
+ {
+ return !active;
+ }
+
+ /** Called to signal the start of an asynchronous operation. The operation
+ * is considered pending until finishCompleter() is called.
+ * E.g. called when initiating an async store operation.
+ */
+ void startCompleter() { ++completionsNeeded; }
+
+ /** Called by completer to signal that it has finished the operation started
+ * when startCompleter() was invoked.
+ * e.g. called when async write complete.
+ */
+ void finishCompleter()
+ {
+ if (--completionsNeeded == 0) {
+ invokeCallback(false);
+ }
+ }
+
+ /** called by initiator before any calls to startCompleter can be done.
+ */
+ void begin()
+ {
+ ++completionsNeeded;
+ }
+
+ /** called by initiator after all potential completers have called
+ * startCompleter().
+ */
+ void end(Callback& cb)
+ {
+ assert(completionsNeeded.get() > 0); // ensure begin() has been called!
+ // the following only "decrements" the count if it is 1. This means
+ // there are no more outstanding completers and we are done.
+ if (completionsNeeded.boolCompareAndSwap(1, 0)) {
+ // done! Complete immediately
+ cb.completed(true);
+ return;
+ }
+
+ // the compare-and-swap did not succeed. This means there are
+ // outstanding completers pending (count > 1). Get a persistent
+ // Callback object to use when the last completer is done.
+ // Decrement after setting up the callback ensures that pending
+ // completers cannot touch the callback until it is ready.
+ callback = cb.clone();
+ if (--completionsNeeded == 0) {
+ // note that a completer may have completed during the
+ // callback setup or decrement:
+ invokeCallback(true);
+ }
+ }
+
+ /** may be called by Initiator to cancel the callback. Will wait for
+ * callback to complete if in progress.
+ */
+ virtual void cancel() {
+ qpid::sys::Mutex::ScopedLock l(callbackLock);
+ while (inCallback) callbackLock.wait();
+ callback = boost::intrusive_ptr<Callback>();
+ active = false;
+ }
+};
+
+}} // qpid::broker::
+#endif /*!_AsyncCompletion_*/
diff --git a/qpid/cpp/src/qpid/broker/Bridge.cpp b/qpid/cpp/src/qpid/broker/Bridge.cpp
new file mode 100644
index 0000000000..7fbbf4e2c4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Bridge.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/FedOps.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/SessionState.h"
+
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include <iostream>
+
+using qpid::framing::FieldTable;
+using qpid::framing::Uuid;
+using qpid::framing::Buffer;
+using qpid::management::ManagementAgent;
+using std::string;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+void Bridge::PushHandler::handle(framing::AMQFrame& frame)
+{
+ conn->received(frame);
+}
+
+Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l,
+ const _qmf::ArgsLinkBridge& _args) :
+ link(_link), id(_id), args(_args), mgmtObject(0),
+ listener(l), name(Uuid(true).str()), queueName("bridge_queue_"), persistenceId(0)
+{
+ std::stringstream title;
+ title << id << "_" << link->getBroker()->getFederationTag();
+ queueName += title.str();
+ ManagementAgent* agent = link->getBroker()->getManagementAgent();
+ if (agent != 0) {
+ mgmtObject = new _qmf::Bridge
+ (agent, this, link, id, args.i_durable, args.i_src, args.i_dest,
+ args.i_key, args.i_srcIsQueue, args.i_srcIsLocal,
+ args.i_tag, args.i_excludes, args.i_dynamic, args.i_sync);
+ agent->addObject(mgmtObject);
+ }
+ QPID_LOG(debug, "Bridge created from " << args.i_src << " to " << args.i_dest);
+}
+
+Bridge::~Bridge()
+{
+ mgmtObject->resourceDestroy();
+}
+
+void Bridge::create(Connection& c)
+{
+ connState = &c;
+ conn = &c;
+ FieldTable options;
+ if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync);
+ SessionHandler& sessionHandler = c.getChannel(id);
+ if (args.i_srcIsLocal) {
+ if (args.i_dynamic)
+ throw Exception("Dynamic routing not supported for push routes");
+ // Point the bridging commands at the local connection handler
+ pushHandler.reset(new PushHandler(&c));
+ channelHandler.reset(new framing::ChannelHandler(id, pushHandler.get()));
+
+ session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler));
+ peer.reset(new framing::AMQP_ServerProxy(*channelHandler));
+
+ session->attach(name, false);
+ session->commandPoint(0,0);
+ } else {
+ sessionHandler.attachAs(name);
+ // Point the bridging commands at the remote peer broker
+ peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out));
+ }
+
+ if (args.i_srcIsLocal) sessionHandler.getSession()->disableReceiverTracking();
+ if (args.i_srcIsQueue) {
+ peer->getMessage().subscribe(args.i_src, args.i_dest, args.i_sync ? 0 : 1, 0, false, "", 0, options);
+ peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
+ peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
+ QPID_LOG(debug, "Activated route from queue " << args.i_src << " to " << args.i_dest);
+ } else {
+ FieldTable queueSettings;
+
+ if (args.i_tag.size()) {
+ queueSettings.setString("qpid.trace.id", args.i_tag);
+ } else {
+ const string& peerTag = c.getFederationPeerTag();
+ if (peerTag.size())
+ queueSettings.setString("qpid.trace.id", peerTag);
+ }
+
+ if (args.i_excludes.size()) {
+ queueSettings.setString("qpid.trace.exclude", args.i_excludes);
+ } else {
+ const string& localTag = link->getBroker()->getFederationTag();
+ if (localTag.size())
+ queueSettings.setString("qpid.trace.exclude", localTag);
+ }
+
+ bool durable = false;//should this be an arg, or would we use srcIsQueue for durable queues?
+ bool autoDelete = !durable;//auto delete transient queues?
+ peer->getQueue().declare(queueName, "", false, durable, true, autoDelete, queueSettings);
+ if (!args.i_dynamic)
+ peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable());
+ peer->getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable());
+ peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
+ peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
+
+ if (args.i_dynamic) {
+ Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src);
+ if (exchange.get() == 0)
+ throw Exception("Exchange not found for dynamic route");
+ exchange->registerDynamicBridge(this);
+ QPID_LOG(debug, "Activated dynamic route for exchange " << args.i_src);
+ } else {
+ QPID_LOG(debug, "Activated static route from exchange " << args.i_src << " to " << args.i_dest);
+ }
+ }
+ if (args.i_srcIsLocal) sessionHandler.getSession()->enableReceiverTracking();
+}
+
+void Bridge::cancel(Connection&)
+{
+ if (resetProxy()) {
+ peer->getMessage().cancel(args.i_dest);
+ peer->getSession().detach(name);
+ }
+}
+
+void Bridge::closed()
+{
+ if (args.i_dynamic) {
+ Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src);
+ if (exchange.get() != 0)
+ exchange->removeDynamicBridge(this);
+ }
+}
+
+void Bridge::destroy()
+{
+ listener(this);
+}
+
+void Bridge::setPersistenceId(uint64_t pId) const
+{
+ persistenceId = pId;
+}
+
+const string& Bridge::getName() const
+{
+ return name;
+}
+
+Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer)
+{
+ string host;
+ uint16_t port;
+ string src;
+ string dest;
+ string key;
+ string id;
+ string excludes;
+
+ buffer.getShortString(host);
+ port = buffer.getShort();
+ bool durable(buffer.getOctet());
+ buffer.getShortString(src);
+ buffer.getShortString(dest);
+ buffer.getShortString(key);
+ bool is_queue(buffer.getOctet());
+ bool is_local(buffer.getOctet());
+ buffer.getShortString(id);
+ buffer.getShortString(excludes);
+ bool dynamic(buffer.getOctet());
+ uint16_t sync = buffer.getShort();
+
+ return links.declare(host, port, durable, src, dest, key,
+ is_queue, is_local, id, excludes, dynamic, sync).first;
+}
+
+void Bridge::encode(Buffer& buffer) const
+{
+ buffer.putShortString(string("bridge"));
+ buffer.putShortString(link->getHost());
+ buffer.putShort(link->getPort());
+ buffer.putOctet(args.i_durable ? 1 : 0);
+ buffer.putShortString(args.i_src);
+ buffer.putShortString(args.i_dest);
+ buffer.putShortString(args.i_key);
+ buffer.putOctet(args.i_srcIsQueue ? 1 : 0);
+ buffer.putOctet(args.i_srcIsLocal ? 1 : 0);
+ buffer.putShortString(args.i_tag);
+ buffer.putShortString(args.i_excludes);
+ buffer.putOctet(args.i_dynamic ? 1 : 0);
+ buffer.putShort(args.i_sync);
+}
+
+uint32_t Bridge::encodedSize() const
+{
+ return link->getHost().size() + 1 // short-string (host)
+ + 7 // short-string ("bridge")
+ + 2 // port
+ + 1 // durable
+ + args.i_src.size() + 1
+ + args.i_dest.size() + 1
+ + args.i_key.size() + 1
+ + 1 // srcIsQueue
+ + 1 // srcIsLocal
+ + args.i_tag.size() + 1
+ + args.i_excludes.size() + 1
+ + 1 // dynamic
+ + 2; // sync
+}
+
+management::ManagementObject* Bridge::GetManagementObject (void) const
+{
+ return (management::ManagementObject*) mgmtObject;
+}
+
+management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId,
+ management::Args& /*args*/,
+ string&)
+{
+ if (methodId == _qmf::Bridge::METHOD_CLOSE) {
+ //notify that we are closed
+ destroy();
+ return management::Manageable::STATUS_OK;
+ } else {
+ return management::Manageable::STATUS_UNKNOWN_METHOD;
+ }
+}
+
+void Bridge::propagateBinding(const string& key, const string& tagList,
+ const string& op, const string& origin,
+ qpid::framing::FieldTable* extra_args)
+{
+ const string& localTag = link->getBroker()->getFederationTag();
+ const string& peerTag = connState->getFederationPeerTag();
+
+ if (tagList.find(peerTag) == tagList.npos) {
+ FieldTable bindArgs;
+ if (extra_args) {
+ for (qpid::framing::FieldTable::ValueMap::iterator i=extra_args->begin(); i != extra_args->end(); ++i) {
+ bindArgs.insert((*i));
+ }
+ }
+ string newTagList(tagList + string(tagList.empty() ? "" : ",") + localTag);
+
+ bindArgs.setString(qpidFedOp, op);
+ bindArgs.setString(qpidFedTags, newTagList);
+ if (origin.empty())
+ bindArgs.setString(qpidFedOrigin, localTag);
+ else
+ bindArgs.setString(qpidFedOrigin, origin);
+
+ conn->requestIOProcessing(boost::bind(&Bridge::ioThreadPropagateBinding, this,
+ queueName, args.i_src, key, bindArgs));
+ }
+}
+
+void Bridge::sendReorigin()
+{
+ FieldTable bindArgs;
+
+ bindArgs.setString(qpidFedOp, fedOpReorigin);
+ bindArgs.setString(qpidFedTags, link->getBroker()->getFederationTag());
+
+ conn->requestIOProcessing(boost::bind(&Bridge::ioThreadPropagateBinding, this,
+ queueName, args.i_src, args.i_key, bindArgs));
+}
+bool Bridge::resetProxy()
+{
+ SessionHandler& sessionHandler = conn->getChannel(id);
+ if (!sessionHandler.getSession()) peer.reset();
+ else peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out));
+ return peer.get();
+}
+
+void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchange, const string& key, FieldTable args)
+{
+ if (resetProxy()) {
+ peer->getExchange().bind(queue, exchange, key, args);
+ } else {
+ QPID_LOG(error, "Cannot propagate binding for dynamic bridge as session has been detached, deleting dynamic bridge");
+ destroy();
+ }
+}
+
+bool Bridge::containsLocalTag(const string& tagList) const
+{
+ const string& localTag = link->getBroker()->getFederationTag();
+ return (tagList.find(localTag) != tagList.npos);
+}
+
+const string& Bridge::getLocalTag() const
+{
+ return link->getBroker()->getFederationTag();
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/Bridge.h b/qpid/cpp/src/qpid/broker/Bridge.h
new file mode 100644
index 0000000000..a846254c57
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Bridge.h
@@ -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.
+ *
+ */
+#ifndef _Bridge_
+#define _Bridge_
+
+#include "qpid/broker/PersistableConfig.h"
+#include "qpid/framing/AMQP_ServerProxy.h"
+#include "qpid/framing/ChannelHandler.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/broker/Exchange.h"
+#include "qmf/org/apache/qpid/broker/ArgsLinkBridge.h"
+#include "qmf/org/apache/qpid/broker/Bridge.h"
+
+#include <boost/function.hpp>
+#include <memory>
+
+namespace qpid {
+namespace broker {
+
+class Connection;
+class ConnectionState;
+class Link;
+class LinkRegistry;
+
+class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge
+{
+public:
+ typedef boost::shared_ptr<Bridge> shared_ptr;
+ typedef boost::function<void(Bridge*)> CancellationListener;
+
+ Bridge(Link* link, framing::ChannelId id, CancellationListener l,
+ const qmf::org::apache::qpid::broker::ArgsLinkBridge& args);
+ ~Bridge();
+
+ void create(Connection& c);
+ void cancel(Connection& c);
+ void closed();
+ void destroy();
+ bool isDurable() { return args.i_durable; }
+
+ management::ManagementObject* GetManagementObject() const;
+ management::Manageable::status_t ManagementMethod(uint32_t methodId,
+ management::Args& args,
+ std::string& text);
+
+ // PersistableConfig:
+ void setPersistenceId(uint64_t id) const;
+ uint64_t getPersistenceId() const { return persistenceId; }
+ uint32_t encodedSize() const;
+ void encode(framing::Buffer& buffer) const;
+ const std::string& getName() const;
+ static Bridge::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer);
+
+ // Exchange::DynamicBridge methods
+ void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin, qpid::framing::FieldTable* extra_args=0);
+ void sendReorigin();
+ void ioThreadPropagateBinding(const std::string& queue, const std::string& exchange, const std::string& key, framing::FieldTable args);
+ bool containsLocalTag(const std::string& tagList) const;
+ const std::string& getLocalTag() const;
+
+private:
+ struct PushHandler : framing::FrameHandler {
+ PushHandler(Connection* c) { conn = c; }
+ void handle(framing::AMQFrame& frame);
+ Connection* conn;
+ };
+
+ std::auto_ptr<PushHandler> pushHandler;
+ std::auto_ptr<framing::ChannelHandler> channelHandler;
+ std::auto_ptr<framing::AMQP_ServerProxy::Session> session;
+ std::auto_ptr<framing::AMQP_ServerProxy> peer;
+
+ Link* link;
+ framing::ChannelId id;
+ qmf::org::apache::qpid::broker::ArgsLinkBridge args;
+ qmf::org::apache::qpid::broker::Bridge* mgmtObject;
+ CancellationListener listener;
+ std::string name;
+ std::string queueName;
+ mutable uint64_t persistenceId;
+ ConnectionState* connState;
+ Connection* conn;
+
+ bool resetProxy();
+};
+
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp
new file mode 100644
index 0000000000..ca3be5b567
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Broker.cpp
@@ -0,0 +1,967 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/HeadersExchange.h"
+#include "qpid/broker/MessageStoreModule.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/RecoveryManagerImpl.h"
+#include "qpid/broker/SaslAuthenticator.h"
+#include "qpid/broker/SecureConnectionFactory.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+
+#include "qmf/org/apache/qpid/broker/Package.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerCreate.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerDelete.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogLevel.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogLevel.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
+#include "qmf/org/apache/qpid/broker/EventBind.h"
+#include "qmf/org/apache/qpid/broker/EventUnbind.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/management/ManagementDirectExchange.h"
+#include "qpid/management/ManagementTopicExchange.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/log/Statement.h"
+#include "qpid/log/posix/SinkOptions.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/ProtocolFactory.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/ConnectionInputHandler.h"
+#include "qpid/sys/ConnectionInputHandlerFactory.h"
+#include "qpid/sys/TimeoutHandler.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/Address.h"
+#include "qpid/StringUtils.h"
+#include "qpid/Url.h"
+#include "qpid/Version.h"
+
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include <iostream>
+#include <memory>
+
+using qpid::sys::ProtocolFactory;
+using qpid::sys::Poller;
+using qpid::sys::Dispatcher;
+using qpid::sys::Thread;
+using qpid::framing::FrameHandler;
+using qpid::framing::ChannelId;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+using qpid::management::getManagementExecutionContext;
+using qpid::types::Variant;
+using std::string;
+using std::make_pair;
+
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+Broker::Options::Options(const std::string& name) :
+ qpid::Options(name),
+ noDataDir(0),
+ port(DEFAULT_PORT),
+ workerThreads(5),
+ maxConnections(500),
+ connectionBacklog(10),
+ enableMgmt(1),
+ mgmtPubInterval(10),
+ queueCleanInterval(60*10),//10 minutes
+ auth(SaslAuthenticator::available()),
+ realm("QPID"),
+ replayFlushLimit(0),
+ replayHardLimit(0),
+ queueLimit(100*1048576/*100M default limit*/),
+ tcpNoDelay(false),
+ requireEncrypted(false),
+ maxSessionRate(0),
+ asyncQueueEvents(false), // Must be false in a cluster.
+ qmf2Support(true),
+ qmf1Support(true),
+ queueFlowStopRatio(80),
+ queueFlowResumeRatio(70),
+ queueThresholdEventRatio(80)
+{
+ int c = sys::SystemInfo::concurrency();
+ workerThreads=c+1;
+ std::string home = getHome();
+
+ if (home.length() == 0)
+ dataDir += DEFAULT_DATA_DIR_LOCATION;
+ else
+ dataDir += home;
+ dataDir += DEFAULT_DATA_DIR_NAME;
+
+ addOptions()
+ ("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker")
+ ("no-data-dir", optValue(noDataDir), "Don't use a data directory. No persistent configuration will be loaded or stored")
+ ("port,p", optValue(port,"PORT"), "Tells the broker to listen on PORT")
+ ("worker-threads", optValue(workerThreads, "N"), "Sets the broker thread pool size")
+ ("max-connections", optValue(maxConnections, "N"), "Sets the maximum allowed connections")
+ ("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket")
+ ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management")
+ ("mgmt-qmf2", optValue(qmf2Support,"yes|no"), "Enable broadcast of management information over QMF v2")
+ ("mgmt-qmf1", optValue(qmf1Support,"yes|no"), "Enable broadcast of management information over QMF v1")
+ ("mgmt-pub-interval", optValue(mgmtPubInterval, "SECONDS"), "Management Publish Interval")
+ ("queue-purge-interval", optValue(queueCleanInterval, "SECONDS"),
+ "Interval between attempts to purge any expired messages from queues")
+ ("auth", optValue(auth, "yes|no"), "Enable authentication, if disabled all incoming connections will be trusted")
+ ("realm", optValue(realm, "REALM"), "Use the given realm when performing authentication")
+ ("default-queue-limit", optValue(queueLimit, "BYTES"), "Default maximum size for queues (in bytes)")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections")
+ ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted")
+ ("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)")
+ ("sasl-config", optValue(saslConfigPath, "DIR"), "gets sasl config info from nonstandard location")
+ ("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)")
+ ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication")
+ ("default-flow-stop-threshold", optValue(queueFlowStopRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is activated.")
+ ("default-flow-resume-threshold", optValue(queueFlowResumeRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is de-activated.")
+ ("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised");
+}
+
+const std::string empty;
+const std::string amq_direct("amq.direct");
+const std::string amq_topic("amq.topic");
+const std::string amq_fanout("amq.fanout");
+const std::string amq_match("amq.match");
+const std::string qpid_management("qpid.management");
+const std::string knownHostsNone("none");
+
+Broker::Broker(const Broker::Options& conf) :
+ poller(new Poller),
+ config(conf),
+ managementAgent(conf.enableMgmt ? new ManagementAgent(conf.qmf1Support,
+ conf.qmf2Support)
+ : 0),
+ store(new NullMessageStore),
+ acl(0),
+ dataDir(conf.noDataDir ? std::string() : conf.dataDir),
+ queues(this),
+ exchanges(this),
+ links(this),
+ factory(new SecureConnectionFactory(*this)),
+ dtxManager(timer),
+ sessionManager(
+ qpid::SessionState::Configuration(
+ conf.replayFlushLimit*1024, // convert kb to bytes.
+ conf.replayHardLimit*1024),
+ *this),
+ queueCleaner(queues, timer),
+ queueEvents(poller,!conf.asyncQueueEvents),
+ recovery(true),
+ inCluster(false),
+ clusterUpdatee(false),
+ expiryPolicy(new ExpiryPolicy),
+ connectionCounter(conf.maxConnections),
+ getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)),
+ deferDelivery(boost::bind(&Broker::deferDeliveryImpl, this, _1, _2))
+{
+ try {
+ if (conf.enableMgmt) {
+ QPID_LOG(info, "Management enabled");
+ managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(),
+ conf.mgmtPubInterval, this, conf.workerThreads + 3);
+ managementAgent->setName("apache.org", "qpidd");
+ _qmf::Package packageInitializer(managementAgent.get());
+
+ System* system = new System (dataDir.isEnabled() ? dataDir.getPath() : string(), this);
+ systemObject = System::shared_ptr(system);
+
+ mgmtObject = new _qmf::Broker(managementAgent.get(), this, system, "amqp-broker");
+ mgmtObject->set_systemRef(system->GetManagementObject()->getObjectId());
+ mgmtObject->set_port(conf.port);
+ mgmtObject->set_workerThreads(conf.workerThreads);
+ mgmtObject->set_maxConns(conf.maxConnections);
+ mgmtObject->set_connBacklog(conf.connectionBacklog);
+ mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval);
+ mgmtObject->set_version(qpid::version);
+ if (dataDir.isEnabled())
+ mgmtObject->set_dataDir(dataDir.getPath());
+ else
+ mgmtObject->clr_dataDir();
+
+ managementAgent->addObject(mgmtObject, 0, true);
+
+ // Since there is currently no support for virtual hosts, a placeholder object
+ // representing the implied single virtual host is added here to keep the
+ // management schema correct.
+ Vhost* vhost = new Vhost(this, this);
+ vhostObject = Vhost::shared_ptr(vhost);
+ framing::Uuid uuid(managementAgent->getUuid());
+ federationTag = uuid.str();
+ vhostObject->setFederationTag(federationTag);
+
+ queues.setParent(vhost);
+ exchanges.setParent(vhost);
+ links.setParent(vhost);
+ } else {
+ // Management is disabled so there is no broker management ID.
+ // Create a unique uuid to use as the federation tag.
+ framing::Uuid uuid(true);
+ federationTag = uuid.str();
+ }
+
+ QueuePolicy::setDefaultMaxSize(conf.queueLimit);
+
+ // Early-Initialize plugins
+ Plugin::earlyInitAll(*this);
+
+ QueueFlowLimit::setDefaults(conf.queueLimit, conf.queueFlowStopRatio, conf.queueFlowResumeRatio);
+
+ // If no plugin store module registered itself, set up the null store.
+ if (NullMessageStore::isNullStore(store.get()))
+ setStore();
+
+ exchanges.declare(empty, DirectExchange::typeName); // Default exchange.
+
+ if (store.get() != 0) {
+ // The cluster plug-in will setRecovery(false) on all but the first
+ // broker to join a cluster.
+ if (getRecovery()) {
+ RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager);
+ store->recover(recoverer);
+ }
+ else {
+ QPID_LOG(notice, "Cluster recovery: recovered journal data discarded and journal files pushed down");
+ store->truncateInit(true); // save old files in subdir
+ }
+ }
+
+ //ensure standard exchanges exist (done after recovery from store)
+ declareStandardExchange(amq_direct, DirectExchange::typeName);
+ declareStandardExchange(amq_topic, TopicExchange::typeName);
+ declareStandardExchange(amq_fanout, FanOutExchange::typeName);
+ declareStandardExchange(amq_match, HeadersExchange::typeName);
+
+ if(conf.enableMgmt) {
+ exchanges.declare(qpid_management, ManagementTopicExchange::typeName);
+ Exchange::shared_ptr mExchange = exchanges.get(qpid_management);
+ Exchange::shared_ptr dExchange = exchanges.get(amq_direct);
+ managementAgent->setExchange(mExchange, dExchange);
+ boost::dynamic_pointer_cast<ManagementTopicExchange>(mExchange)->setManagmentAgent(managementAgent.get(), 1);
+
+ std::string qmfTopic("qmf.default.topic");
+ std::string qmfDirect("qmf.default.direct");
+
+ std::pair<Exchange::shared_ptr, bool> topicPair(exchanges.declare(qmfTopic, ManagementTopicExchange::typeName));
+ std::pair<Exchange::shared_ptr, bool> directPair(exchanges.declare(qmfDirect, ManagementDirectExchange::typeName));
+
+ boost::dynamic_pointer_cast<ManagementDirectExchange>(directPair.first)->setManagmentAgent(managementAgent.get(), 2);
+ boost::dynamic_pointer_cast<ManagementTopicExchange>(topicPair.first)->setManagmentAgent(managementAgent.get(), 2);
+
+ managementAgent->setExchangeV2(topicPair.first, directPair.first);
+ }
+ else
+ QPID_LOG(info, "Management not enabled");
+
+ /**
+ * SASL setup, can fail and terminate startup
+ */
+ if (conf.auth) {
+ SaslAuthenticator::init(qpid::saslName, conf.saslConfigPath);
+ QPID_LOG(info, "SASL enabled");
+ } else {
+ QPID_LOG(notice, "SASL disabled: No Authentication Performed");
+ }
+
+ // Initialize plugins
+ Plugin::initializeAll(*this);
+
+ if (managementAgent.get()) managementAgent->pluginsInitialized();
+
+ if (conf.queueCleanInterval) {
+ queueCleaner.start(conf.queueCleanInterval * qpid::sys::TIME_SEC);
+ }
+
+ //initialize known broker urls (TODO: add support for urls for other transports (SSL, RDMA)):
+ if (conf.knownHosts.empty()) {
+ boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(TCP_TRANSPORT);
+ if (factory) {
+ knownBrokers.push_back ( qpid::Url::getIpAddressesUrl ( factory->getPort() ) );
+ }
+ } else if (conf.knownHosts != knownHostsNone) {
+ knownBrokers.push_back(Url(conf.knownHosts));
+ }
+ } catch (const std::exception& /*e*/) {
+ finalize();
+ throw;
+ }
+}
+
+void Broker::declareStandardExchange(const std::string& name, const std::string& type)
+{
+ bool storeEnabled = store.get() != NULL;
+ std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled);
+ if (status.second && storeEnabled) {
+ store->create(*status.first, framing::FieldTable ());
+ }
+}
+
+
+boost::intrusive_ptr<Broker> Broker::create(int16_t port)
+{
+ Options config;
+ config.port=port;
+ return create(config);
+}
+
+boost::intrusive_ptr<Broker> Broker::create(const Options& opts)
+{
+ return boost::intrusive_ptr<Broker>(new Broker(opts));
+}
+
+void Broker::setStore (boost::shared_ptr<MessageStore>& _store)
+{
+ store.reset(new MessageStoreModule (_store));
+ setStore();
+}
+
+void Broker::setStore () {
+ queues.setStore (store.get());
+ dtxManager.setStore (store.get());
+ links.setStore (store.get());
+}
+
+void Broker::run() {
+ if (config.workerThreads > 0) {
+ QPID_LOG(notice, "Broker running");
+ Dispatcher d(poller);
+ int numIOThreads = config.workerThreads;
+ std::vector<Thread> t(numIOThreads-1);
+
+ // Run n-1 io threads
+ for (int i=0; i<numIOThreads-1; ++i)
+ t[i] = Thread(d);
+
+ // Run final thread
+ d.run();
+
+ // Now wait for n-1 io threads to exit
+ for (int i=0; i<numIOThreads-1; ++i) {
+ t[i].join();
+ }
+ } else {
+ throw Exception((boost::format("Invalid value for worker-threads: %1%") % config.workerThreads).str());
+ }
+}
+
+void Broker::shutdown() {
+ // NB: this function must be async-signal safe, it must not
+ // call any function that is not async-signal safe.
+ // Any unsafe shutdown actions should be done in the destructor.
+ poller->shutdown();
+}
+
+Broker::~Broker() {
+ shutdown();
+ queueEvents.shutdown();
+ finalize(); // Finalize any plugins.
+ if (config.auth)
+ SaslAuthenticator::fini();
+ timer.stop();
+ QPID_LOG(notice, "Shut down");
+}
+
+ManagementObject* Broker::GetManagementObject(void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable* Broker::GetVhostObject(void) const
+{
+ return vhostObject.get();
+}
+
+Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
+ Args& args,
+ string&)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ switch (methodId)
+ {
+ case _qmf::Broker::METHOD_ECHO :
+ QPID_LOG (debug, "Broker::echo("
+ << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_sequence
+ << ", "
+ << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_body
+ << ")");
+ status = Manageable::STATUS_OK;
+ break;
+ case _qmf::Broker::METHOD_CONNECT : {
+ _qmf::ArgsBrokerConnect& hp=
+ dynamic_cast<_qmf::ArgsBrokerConnect&>(args);
+
+ QPID_LOG (debug, "Broker::connect()");
+ string transport = hp.i_transport.empty() ? TCP_TRANSPORT : hp.i_transport;
+ if (!getProtocolFactory(transport)) {
+ QPID_LOG(error, "Transport '" << transport << "' not supported");
+ return Manageable::STATUS_NOT_IMPLEMENTED;
+ }
+ std::pair<Link::shared_ptr, bool> response =
+ links.declare (hp.i_host, hp.i_port, transport, hp.i_durable,
+ hp.i_authMechanism, hp.i_username, hp.i_password);
+ if (hp.i_durable && response.second)
+ store->create(*response.first);
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ case _qmf::Broker::METHOD_QUEUEMOVEMESSAGES : {
+ _qmf::ArgsBrokerQueueMoveMessages& moveArgs=
+ dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args);
+ QPID_LOG (debug, "Broker::queueMoveMessages()");
+ if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty))
+ status = Manageable::STATUS_OK;
+ else
+ return Manageable::STATUS_PARAMETER_INVALID;
+ break;
+ }
+ case _qmf::Broker::METHOD_SETLOGLEVEL :
+ setLogLevel(dynamic_cast<_qmf::ArgsBrokerSetLogLevel&>(args).i_level);
+ QPID_LOG (debug, "Broker::setLogLevel()");
+ status = Manageable::STATUS_OK;
+ break;
+ case _qmf::Broker::METHOD_GETLOGLEVEL :
+ dynamic_cast<_qmf::ArgsBrokerGetLogLevel&>(args).o_level = getLogLevel();
+ QPID_LOG (debug, "Broker::getLogLevel()");
+ status = Manageable::STATUS_OK;
+ break;
+ case _qmf::Broker::METHOD_CREATE :
+ {
+ _qmf::ArgsBrokerCreate& a = dynamic_cast<_qmf::ArgsBrokerCreate&>(args);
+ createObject(a.i_type, a.i_name, a.i_properties, a.i_strict, getManagementExecutionContext());
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ case _qmf::Broker::METHOD_DELETE :
+ {
+ _qmf::ArgsBrokerDelete& a = dynamic_cast<_qmf::ArgsBrokerDelete&>(args);
+ deleteObject(a.i_type, a.i_name, a.i_options, getManagementExecutionContext());
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ default:
+ QPID_LOG (debug, "Broker ManagementMethod not implemented: id=" << methodId << "]");
+ status = Manageable::STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return status;
+}
+
+namespace
+{
+const std::string TYPE_QUEUE("queue");
+const std::string TYPE_EXCHANGE("exchange");
+const std::string TYPE_TOPIC("topic");
+const std::string TYPE_BINDING("binding");
+const std::string DURABLE("durable");
+const std::string AUTO_DELETE("auto-delete");
+const std::string ALTERNATE_EXCHANGE("alternate-exchange");
+const std::string EXCHANGE_TYPE("exchange-type");
+const std::string QUEUE_NAME("queue");
+const std::string EXCHANGE_NAME("exchange");
+
+const std::string _TRUE("true");
+const std::string _FALSE("false");
+}
+
+struct InvalidBindingIdentifier : public qpid::Exception
+{
+ InvalidBindingIdentifier(const std::string& name) : qpid::Exception(name) {}
+ std::string getPrefix() const { return "invalid binding"; }
+};
+
+struct BindingIdentifier
+{
+ std::string exchange;
+ std::string queue;
+ std::string key;
+
+ BindingIdentifier(const std::string& name)
+ {
+ std::vector<std::string> path;
+ split(path, name, "/");
+ switch (path.size()) {
+ case 1:
+ queue = path[0];
+ break;
+ case 2:
+ exchange = path[0];
+ queue = path[1];
+ break;
+ case 3:
+ exchange = path[0];
+ queue = path[1];
+ key = path[2];
+ break;
+ default:
+ throw InvalidBindingIdentifier(name);
+ }
+ }
+};
+
+struct ObjectAlreadyExists : public qpid::Exception
+{
+ ObjectAlreadyExists(const std::string& name) : qpid::Exception(name) {}
+ std::string getPrefix() const { return "object already exists"; }
+};
+
+struct UnknownObjectType : public qpid::Exception
+{
+ UnknownObjectType(const std::string& type) : qpid::Exception(type) {}
+ std::string getPrefix() const { return "unknown object type"; }
+};
+
+void Broker::createObject(const std::string& type, const std::string& name,
+ const Variant::Map& properties, bool /*strict*/, const ConnectionState* context)
+{
+ std::string userId;
+ std::string connectionId;
+ if (context) {
+ userId = context->getUserId();
+ connectionId = context->getUrl();
+ }
+ //TODO: implement 'strict' option (check there are no unrecognised properties)
+ QPID_LOG (debug, "Broker::create(" << type << ", " << name << "," << properties << ")");
+ if (type == TYPE_QUEUE) {
+ bool durable(false);
+ bool autodelete(false);
+ std::string alternateExchange;
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == DURABLE) durable = i->second;
+ else if (i->first == AUTO_DELETE) autodelete = i->second;
+ else if (i->first == ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ std::pair<boost::shared_ptr<Queue>, bool> result =
+ createQueue(name, durable, autodelete, 0, alternateExchange, arguments, userId, connectionId);
+ if (!result.second) {
+ throw ObjectAlreadyExists(name);
+ }
+ } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) {
+ bool durable(false);
+ std::string exchangeType("topic");
+ std::string alternateExchange;
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == DURABLE) durable = i->second;
+ else if (i->first == EXCHANGE_TYPE) exchangeType = i->second.asString();
+ else if (i->first == ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ try {
+ std::pair<boost::shared_ptr<Exchange>, bool> result =
+ createExchange(name, exchangeType, durable, alternateExchange, arguments, userId, connectionId);
+ if (!result.second) {
+ throw ObjectAlreadyExists(name);
+ }
+ } catch (const UnknownExchangeTypeException&) {
+ throw Exception(QPID_MSG("Invalid exchange type: " << exchangeType));
+ }
+ } else if (type == TYPE_BINDING) {
+ BindingIdentifier binding(name);
+ std::string exchangeType("topic");
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == EXCHANGE_TYPE) exchangeType = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ bind(binding.queue, binding.exchange, binding.key, arguments, userId, connectionId);
+ } else {
+ throw UnknownObjectType(type);
+ }
+}
+
+void Broker::deleteObject(const std::string& type, const std::string& name,
+ const Variant::Map& options, const ConnectionState* context)
+{
+ std::string userId;
+ std::string connectionId;
+ if (context) {
+ userId = context->getUserId();
+ connectionId = context->getUrl();
+ }
+ QPID_LOG (debug, "Broker::delete(" << type << ", " << name << "," << options << ")");
+ if (type == TYPE_QUEUE) {
+ deleteQueue(name, userId, connectionId);
+ } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) {
+ deleteExchange(name, userId, connectionId);
+ } else if (type == TYPE_BINDING) {
+ BindingIdentifier binding(name);
+ unbind(binding.queue, binding.exchange, binding.key, userId, connectionId);
+ } else {
+ throw UnknownObjectType(type);
+ }
+
+}
+
+void Broker::setLogLevel(const std::string& level)
+{
+ QPID_LOG(notice, "Changing log level to " << level);
+ std::vector<std::string> selectors;
+ split(selectors, level, ", ");
+ qpid::log::Logger::instance().reconfigure(selectors);
+}
+
+std::string Broker::getLogLevel()
+{
+ std::string level;
+ const std::vector<std::string>& selectors = qpid::log::Logger::instance().getOptions().selectors;
+ for (std::vector<std::string>::const_iterator i = selectors.begin(); i != selectors.end(); ++i) {
+ if (i != selectors.begin()) level += std::string(",");
+ level += *i;
+ }
+ return level;
+}
+
+boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory(const std::string& name) const {
+ ProtocolFactoryMap::const_iterator i
+ = name.empty() ? protocolFactories.begin() : protocolFactories.find(name);
+ if (i == protocolFactories.end()) return boost::shared_ptr<ProtocolFactory>();
+ else return i->second;
+}
+
+uint16_t Broker::getPort(const std::string& name) const {
+ boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(name);
+ if (factory) {
+ return factory->getPort();
+ } else {
+ throw NoSuchTransportException(QPID_MSG("No such transport: '" << name << "'"));
+ }
+}
+
+void Broker::registerProtocolFactory(const std::string& name, ProtocolFactory::shared_ptr protocolFactory) {
+ protocolFactories[name] = protocolFactory;
+ Url::addProtocol(name);
+}
+
+void Broker::accept() {
+ for (ProtocolFactoryMap::const_iterator i = protocolFactories.begin(); i != protocolFactories.end(); i++) {
+ i->second->accept(poller, factory.get());
+ }
+}
+
+void Broker::connect(
+ const std::string& host, const std::string& port, const std::string& transport,
+ boost::function2<void, int, std::string> failed,
+ sys::ConnectionCodec::Factory* f)
+{
+ boost::shared_ptr<ProtocolFactory> pf = getProtocolFactory(transport);
+ if (pf) pf->connect(poller, host, port, f ? f : factory.get(), failed);
+ else throw NoSuchTransportException(QPID_MSG("Unsupported transport type: " << transport));
+}
+
+void Broker::connect(
+ const Url& url,
+ boost::function2<void, int, std::string> failed,
+ sys::ConnectionCodec::Factory* f)
+{
+ url.throwIfEmpty();
+ const Address& addr=url[0];
+ connect(addr.host, boost::lexical_cast<std::string>(addr.port), addr.protocol, failed, f);
+}
+
+uint32_t Broker::queueMoveMessages(
+ const std::string& srcQueue,
+ const std::string& destQueue,
+ uint32_t qty)
+{
+ Queue::shared_ptr src_queue = queues.find(srcQueue);
+ if (!src_queue)
+ return 0;
+ Queue::shared_ptr dest_queue = queues.find(destQueue);
+ if (!dest_queue)
+ return 0;
+
+ return src_queue->move(dest_queue, qty);
+}
+
+
+boost::shared_ptr<sys::Poller> Broker::getPoller() { return poller; }
+
+std::vector<Url>
+Broker::getKnownBrokersImpl()
+{
+ return knownBrokers;
+}
+
+bool Broker::deferDeliveryImpl(const std::string& ,
+ const boost::intrusive_ptr<Message>& )
+{ return false; }
+
+void Broker::setClusterTimer(std::auto_ptr<sys::Timer> t) {
+ clusterTimer = t;
+}
+
+const std::string Broker::TCP_TRANSPORT("tcp");
+
+
+std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const OwnershipToken* owner,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
+ params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
+ params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
+
+ if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId));
+ }
+
+ Exchange::shared_ptr alternate;
+ if (!alternateExchange.empty()) {
+ alternate = exchanges.get(alternateExchange);
+ if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+ }
+
+ std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner, alternate, arguments);
+ if (result.second) {
+ //add default binding:
+ result.first->bind(exchanges.getDefault(), name);
+
+ if (managementAgent.get()) {
+ //TODO: debatable whether we should raise an event here for
+ //create when this is a 'declare' event; ideally add a create
+ //event instead?
+ managementAgent->raiseEvent(
+ _qmf::EventQueueDeclare(connectionId, userId, name,
+ durable, owner, autodelete,
+ ManagementAgent::toMap(arguments),
+ "created"));
+ }
+ }
+ return result;
+}
+
+void Broker::deleteQueue(const std::string& name, const std::string& userId,
+ const std::string& connectionId, QueueFunctor check)
+{
+ if (acl && !acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_QUEUE,name,NULL)) {
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue delete request from " << userId));
+ }
+
+ Queue::shared_ptr queue = queues.find(name);
+ if (queue) {
+ if (check) check(queue);
+ queues.destroy(name);
+ queue->destroyed();
+ } else {
+ throw framing::NotFoundException(QPID_MSG("Delete failed. No such queue: " << name));
+ }
+
+ if (managementAgent.get())
+ managementAgent->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, name));
+
+}
+
+std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_TYPE, type));
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_EXCHANGE,name,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << userId));
+ }
+
+ Exchange::shared_ptr alternate;
+ if (!alternateExchange.empty()) {
+ alternate = exchanges.get(alternateExchange);
+ if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+ }
+
+ std::pair<Exchange::shared_ptr, bool> result;
+ result = exchanges.declare(name, type, durable, arguments);
+ if (result.second) {
+ if (alternate) {
+ result.first->setAlternate(alternate);
+ alternate->incAlternateUsers();
+ }
+ if (durable) {
+ store->create(*result.first, arguments);
+ }
+ if (managementAgent.get()) {
+ //TODO: debatable whether we should raise an event here for
+ //create when this is a 'declare' event; ideally add a create
+ //event instead?
+ managementAgent->raiseEvent(_qmf::EventExchangeDeclare(connectionId,
+ userId,
+ name,
+ type,
+ alternateExchange,
+ durable,
+ false,
+ ManagementAgent::toMap(arguments),
+ "created"));
+ }
+ }
+ return result;
+}
+
+void Broker::deleteExchange(const std::string& name, const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ if (!acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange delete request from " << userId));
+ }
+
+ Exchange::shared_ptr exchange(exchanges.get(name));
+ if (!exchange) throw framing::NotFoundException(QPID_MSG("Delete failed. No such exchange: " << name));
+ if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange."));
+ if (exchange->isDurable()) store->destroy(*exchange);
+ if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers();
+ exchanges.destroy(name);
+
+ if (managementAgent.get())
+ managementAgent->raiseEvent(_qmf::EventExchangeDelete(connectionId, userId, name));
+
+}
+
+void Broker::bind(const std::string& queueName,
+ const std::string& exchangeName,
+ const std::string& key,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
+ params.insert(make_pair(acl::PROP_ROUTINGKEY, key));
+
+ if (!acl->authorise(userId,acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,&params))
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange bind request from " << userId));
+ }
+
+ Queue::shared_ptr queue = queues.find(queueName);
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ if (!queue) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such queue: " << queueName));
+ } else if (!exchange) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such exchange: " << exchangeName));
+ } else {
+ if (queue->bind(exchange, key, arguments)) {
+ if (managementAgent.get()) {
+ managementAgent->raiseEvent(_qmf::EventBind(connectionId, userId, exchangeName,
+ queueName, key, ManagementAgent::toMap(arguments)));
+ }
+ }
+ }
+}
+
+void Broker::unbind(const std::string& queueName,
+ const std::string& exchangeName,
+ const std::string& key,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
+ params.insert(make_pair(acl::PROP_ROUTINGKEY, key));
+ if (!acl->authorise(userId,acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange unbind request from " << userId));
+ }
+
+ Queue::shared_ptr queue = queues.find(queueName);
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ if (!queue) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such queue: " << queueName));
+ } else if (!exchange) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such exchange: " << exchangeName));
+ } else {
+ if (exchange->unbind(queue, key, 0)) {
+ if (exchange->isDurable() && queue->isDurable()) {
+ store->unbind(*exchange, *queue, key, qpid::framing::FieldTable());
+ }
+ if (managementAgent.get()) {
+ managementAgent->raiseEvent(_qmf::EventUnbind(connectionId, userId, exchangeName, queueName, key));
+ }
+ }
+ }
+}
+
+}} // namespace qpid::broker
+
diff --git a/qpid/cpp/src/qpid/broker/Broker.h b/qpid/cpp/src/qpid/broker/Broker.h
new file mode 100644
index 0000000000..40f7b6273f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Broker.h
@@ -0,0 +1,351 @@
+#ifndef _Broker_
+#define _Broker_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/ConnectionFactory.h"
+#include "qpid/broker/ConnectionToken.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/DtxManager.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/SessionManager.h"
+#include "qpid/broker/QueueCleaner.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Vhost.h"
+#include "qpid/broker/System.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/broker/Broker.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerConnect.h"
+#include "qpid/Options.h"
+#include "qpid/Plugin.h"
+#include "qpid/DataDir.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/OutputHandler.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/types/Variant.h"
+#include "qpid/RefCounted.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/intrusive_ptr.hpp>
+#include <string>
+#include <vector>
+
+namespace qpid {
+
+namespace sys {
+ class ProtocolFactory;
+ class Poller;
+}
+
+struct Url;
+
+namespace broker {
+
+class ConnectionState;
+class ExpiryPolicy;
+class Message;
+
+static const uint16_t DEFAULT_PORT=5672;
+
+struct NoSuchTransportException : qpid::Exception
+{
+ NoSuchTransportException(const std::string& s) : Exception(s) {}
+ virtual ~NoSuchTransportException() throw() {}
+};
+
+/**
+ * A broker instance.
+ */
+class Broker : public sys::Runnable, public Plugin::Target,
+ public management::Manageable,
+ public RefCounted
+{
+public:
+
+ struct Options : public qpid::Options {
+ static const std::string DEFAULT_DATA_DIR_LOCATION;
+ static const std::string DEFAULT_DATA_DIR_NAME;
+
+ QPID_BROKER_EXTERN Options(const std::string& name="Broker Options");
+
+ bool noDataDir;
+ std::string dataDir;
+ uint16_t port;
+ int workerThreads;
+ int maxConnections;
+ int connectionBacklog;
+ bool enableMgmt;
+ uint16_t mgmtPubInterval;
+ uint16_t queueCleanInterval;
+ bool auth;
+ std::string realm;
+ size_t replayFlushLimit;
+ size_t replayHardLimit;
+ uint queueLimit;
+ bool tcpNoDelay;
+ bool requireEncrypted;
+ std::string knownHosts;
+ std::string saslConfigPath;
+ uint32_t maxSessionRate;
+ bool asyncQueueEvents;
+ bool qmf2Support;
+ bool qmf1Support;
+ uint queueFlowStopRatio; // producer flow control: on
+ uint queueFlowResumeRatio; // producer flow control: off
+ uint16_t queueThresholdEventRatio;
+
+ private:
+ std::string getHome();
+ };
+
+ class ConnectionCounter {
+ int maxConnections;
+ int connectionCount;
+ sys::Mutex connectionCountLock;
+ public:
+ ConnectionCounter(int mc): maxConnections(mc),connectionCount(0) {};
+ void inc_connectionCount() {
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
+ connectionCount++;
+ }
+ void dec_connectionCount() {
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
+ connectionCount--;
+ }
+ bool allowConnection() {
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
+ return (maxConnections <= connectionCount);
+ }
+ };
+
+ private:
+ typedef std::map<std::string, boost::shared_ptr<sys::ProtocolFactory> > ProtocolFactoryMap;
+
+ void declareStandardExchange(const std::string& name, const std::string& type);
+ void setStore ();
+ void setLogLevel(const std::string& level);
+ std::string getLogLevel();
+ void createObject(const std::string& type, const std::string& name,
+ const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context);
+ void deleteObject(const std::string& type, const std::string& name,
+ const qpid::types::Variant::Map& options, const ConnectionState* context);
+
+ boost::shared_ptr<sys::Poller> poller;
+ sys::Timer timer;
+ std::auto_ptr<sys::Timer> clusterTimer;
+ Options config;
+ std::auto_ptr<management::ManagementAgent> managementAgent;
+ ProtocolFactoryMap protocolFactories;
+ std::auto_ptr<MessageStore> store;
+ AclModule* acl;
+ DataDir dataDir;
+
+ QueueRegistry queues;
+ ExchangeRegistry exchanges;
+ LinkRegistry links;
+ boost::shared_ptr<sys::ConnectionCodec::Factory> factory;
+ DtxManager dtxManager;
+ SessionManager sessionManager;
+ qmf::org::apache::qpid::broker::Broker* mgmtObject;
+ Vhost::shared_ptr vhostObject;
+ System::shared_ptr systemObject;
+ QueueCleaner queueCleaner;
+ QueueEvents queueEvents;
+ std::vector<Url> knownBrokers;
+ std::vector<Url> getKnownBrokersImpl();
+ bool deferDeliveryImpl(const std::string& queue,
+ const boost::intrusive_ptr<Message>& msg);
+ std::string federationTag;
+ bool recovery;
+ bool inCluster, clusterUpdatee;
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+ ConnectionCounter connectionCounter;
+
+ public:
+ virtual ~Broker();
+
+ QPID_BROKER_EXTERN Broker(const Options& configuration);
+ static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(const Options& configuration);
+ static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(int16_t port = DEFAULT_PORT);
+
+ /**
+ * Return listening port. If called before bind this is
+ * the configured port. If called after it is the actual
+ * port, which will be different if the configured port is
+ * 0.
+ */
+ virtual uint16_t getPort(const std::string& name) const;
+
+ /**
+ * Run the broker. Implements Runnable::run() so the broker
+ * can be run in a separate thread.
+ */
+ virtual void run();
+
+ /** Shut down the broker */
+ virtual void shutdown();
+
+ QPID_BROKER_EXTERN void setStore (boost::shared_ptr<MessageStore>& store);
+ MessageStore& getStore() { return *store; }
+ void setAcl (AclModule* _acl) {acl = _acl;}
+ AclModule* getAcl() { return acl; }
+ QueueRegistry& getQueues() { return queues; }
+ ExchangeRegistry& getExchanges() { return exchanges; }
+ LinkRegistry& getLinks() { return links; }
+ DtxManager& getDtxManager() { return dtxManager; }
+ DataDir& getDataDir() { return dataDir; }
+ Options& getOptions() { return config; }
+ QueueEvents& getQueueEvents() { return queueEvents; }
+
+ void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { expiryPolicy = e; }
+ boost::intrusive_ptr<ExpiryPolicy> getExpiryPolicy() { return expiryPolicy; }
+
+ SessionManager& getSessionManager() { return sessionManager; }
+ const std::string& getFederationTag() const { return federationTag; }
+
+ management::ManagementObject* GetManagementObject (void) const;
+ management::Manageable* GetVhostObject (void) const;
+ management::Manageable::status_t ManagementMethod (uint32_t methodId,
+ management::Args& args,
+ std::string& text);
+
+ /** Add to the broker's protocolFactorys */
+ void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>);
+
+ /** Accept connections */
+ QPID_BROKER_EXTERN void accept();
+
+ /** Create a connection to another broker. */
+ void connect(const std::string& host, const std::string& port,
+ const std::string& transport,
+ boost::function2<void, int, std::string> failed,
+ sys::ConnectionCodec::Factory* =0);
+ /** Create a connection to another broker. */
+ void connect(const Url& url,
+ boost::function2<void, int, std::string> failed,
+ sys::ConnectionCodec::Factory* =0);
+
+ /** Move messages from one queue to another.
+ A zero quantity means to move all messages
+ */
+ uint32_t queueMoveMessages( const std::string& srcQueue,
+ const std::string& destQueue,
+ uint32_t qty);
+
+ boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory(const std::string& name = TCP_TRANSPORT) const;
+
+ /** Expose poller so plugins can register their descriptors. */
+ boost::shared_ptr<sys::Poller> getPoller();
+
+ boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; }
+ void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; }
+
+ /** Timer for local tasks affecting only this broker */
+ sys::Timer& getTimer() { return timer; }
+
+ /** Timer for tasks that must be synchronized if we are in a cluster */
+ sys::Timer& getClusterTimer() { return clusterTimer.get() ? *clusterTimer : timer; }
+ void setClusterTimer(std::auto_ptr<sys::Timer>);
+
+ boost::function<std::vector<Url> ()> getKnownBrokers;
+
+ static QPID_BROKER_EXTERN const std::string TCP_TRANSPORT;
+
+ void setRecovery(bool set) { recovery = set; }
+ bool getRecovery() const { return recovery; }
+
+ /** True of this broker is part of a cluster.
+ * Only valid after early initialization of plugins is complete.
+ */
+ bool isInCluster() const { return inCluster; }
+ void setInCluster(bool set) { inCluster = set; }
+
+ /** True if this broker is joining a cluster and in the process of
+ * receiving a state update.
+ */
+ bool isClusterUpdatee() const { return clusterUpdatee; }
+ void setClusterUpdatee(bool set) { clusterUpdatee = set; }
+
+ management::ManagementAgent* getManagementAgent() { return managementAgent.get(); }
+
+ ConnectionCounter& getConnectionCounter() {return connectionCounter;}
+
+ /**
+ * Never true in a stand-alone broker. In a cluster, return true
+ * to defer delivery of messages deliveredg in a cluster-unsafe
+ * context.
+ *@return true if delivery of a message should be deferred.
+ */
+ boost::function<bool (const std::string& queue,
+ const boost::intrusive_ptr<Message>& msg)> deferDelivery;
+
+ bool isAuthenticating ( ) { return config.auth; }
+
+ typedef boost::function1<void, boost::shared_ptr<Queue> > QueueFunctor;
+
+ std::pair<boost::shared_ptr<Queue>, bool> createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const OwnershipToken* owner,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId);
+ void deleteQueue(const std::string& name,
+ const std::string& userId,
+ const std::string& connectionId,
+ QueueFunctor check = QueueFunctor());
+ std::pair<Exchange::shared_ptr, bool> createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& args,
+ const std::string& userId, const std::string& connectionId);
+ void deleteExchange(const std::string& name, const std::string& userId,
+ const std::string& connectionId);
+ void bind(const std::string& queue,
+ const std::string& exchange,
+ const std::string& key,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId);
+ void unbind(const std::string& queue,
+ const std::string& exchange,
+ const std::string& key,
+ const std::string& userId,
+ const std::string& connectionId);
+};
+
+}}
+
+#endif /*!_Broker_*/
diff --git a/qpid/cpp/src/qpid/broker/BrokerImportExport.h b/qpid/cpp/src/qpid/broker/BrokerImportExport.h
new file mode 100644
index 0000000000..ee05788063
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/BrokerImportExport.h
@@ -0,0 +1,42 @@
+#ifndef QPID_BROKER_IMPORT_EXPORT_H
+#define QPID_BROKER_IMPORT_EXPORT_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+# if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS)
+# define QPID_BROKER_EXTERN __declspec(dllexport)
+# else
+# define QPID_BROKER_EXTERN __declspec(dllimport)
+# endif
+# ifdef _MSC_VER
+# define QPID_BROKER_CLASS_EXTERN
+# define QPID_BROKER_INLINE_EXTERN QPID_BROKER_EXTERN
+# else
+# define QPID_BROKER_CLASS_EXTERN QPID_BROKER_EXTERN
+# define QPID_BROKER_INLINE_EXTERN
+# endif
+#else
+# define QPID_BROKER_EXTERN
+# define QPID_BROKER_CLASS_EXTERN
+# define QPID_BROKER_INLINE_EXTERN
+#endif
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp
new file mode 100644
index 0000000000..c07e63e68c
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Connection.cpp
@@ -0,0 +1,487 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/SessionOutputException.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/sys/ClusterSafe.h"
+
+#include "qpid/log/Statement.h"
+#include "qpid/ptr_map.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qmf/org/apache/qpid/broker/EventClientConnect.h"
+#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h"
+
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <assert.h>
+
+
+
+using namespace qpid::sys;
+using namespace qpid::framing;
+using qpid::ptr_map_ptr;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+struct ConnectionTimeoutTask : public sys::TimerTask {
+ sys::Timer& timer;
+ Connection& connection;
+
+ ConnectionTimeoutTask(uint16_t hb, sys::Timer& t, Connection& c) :
+ TimerTask(Duration(hb*2*TIME_SEC),"ConnectionTimeout"),
+ timer(t),
+ connection(c)
+ {}
+
+ void touch() {
+ restart();
+ }
+
+ void fire() {
+ // If we get here then we've not received any traffic in the timeout period
+ // Schedule closing the connection for the io thread
+ QPID_LOG(error, "Connection " << connection.getMgmtId()
+ << " timed out: closing");
+ connection.abort();
+ }
+};
+
+Connection::Connection(ConnectionOutputHandler* out_,
+ Broker& broker_, const
+ std::string& mgmtId_,
+ const qpid::sys::SecuritySettings& external,
+ bool isLink_,
+ uint64_t objectId_,
+ bool shadow_,
+ bool delayManagement) :
+ ConnectionState(out_, broker_),
+ securitySettings(external),
+ adapter(*this, isLink_, shadow_),
+ isLink(isLink_),
+ mgmtClosing(false),
+ mgmtId(mgmtId_),
+ mgmtObject(0),
+ links(broker_.getLinks()),
+ agent(0),
+ timer(broker_.getTimer()),
+ errorListener(0),
+ objectId(objectId_),
+ shadow(shadow_),
+ outboundTracker(*this)
+{
+ outboundTracker.wrap(out);
+ if (isLink)
+ links.notifyConnection(mgmtId, this);
+ // In a cluster, allow adding the management object to be delayed.
+ if (!delayManagement) addManagementObject();
+ if (!isShadow()) broker.getConnectionCounter().inc_connectionCount();
+}
+
+void Connection::addManagementObject() {
+ assert(agent == 0);
+ assert(mgmtObject == 0);
+ Manageable* parent = broker.GetVhostObject();
+ if (parent != 0) {
+ agent = broker.getManagementAgent();
+ if (agent != 0) {
+ // TODO set last bool true if system connection
+ mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !isLink, false);
+ mgmtObject->set_shadow(shadow);
+ agent->addObject(mgmtObject, objectId);
+ }
+ ConnectionState::setUrl(mgmtId);
+ }
+}
+
+void Connection::requestIOProcessing(boost::function0<void> callback)
+{
+ ScopedLock<Mutex> l(ioCallbackLock);
+ ioCallbacks.push(callback);
+ out.activateOutput();
+}
+
+Connection::~Connection()
+{
+ if (mgmtObject != 0) {
+ mgmtObject->resourceDestroy();
+ // In a cluster, Connections destroyed during shutdown are in
+ // a cluster-unsafe context. Don't raise an event in that case.
+ if (!isLink && isClusterSafe())
+ agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId()));
+ }
+ if (isLink)
+ links.notifyClosed(mgmtId);
+
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+ if (timeoutTimer)
+ timeoutTimer->cancel();
+
+ if (!isShadow()) broker.getConnectionCounter().dec_connectionCount();
+}
+
+void Connection::received(framing::AMQFrame& frame) {
+ // Received frame on connection so delay timeout
+ restartTimeout();
+
+ if (frame.getChannel() == 0 && frame.getMethod()) {
+ adapter.handle(frame);
+ } else {
+ if (adapter.isOpen())
+ getChannel(frame.getChannel()).in(frame);
+ else
+ close(connection::CLOSE_CODE_FRAMING_ERROR, "Connection not yet open, invalid frame received.");
+ }
+
+ if (isLink) //i.e. we are acting as the client to another broker
+ recordFromServer(frame);
+ else
+ recordFromClient(frame);
+}
+
+void Connection::sent(const framing::AMQFrame& frame)
+{
+ if (isLink) //i.e. we are acting as the client to another broker
+ recordFromClient(frame);
+ else
+ recordFromServer(frame);
+}
+
+bool isMessage(const AMQMethodBody* method)
+{
+ return method && method->isA<qpid::framing::MessageTransferBody>();
+}
+
+void Connection::recordFromServer(const framing::AMQFrame& frame)
+{
+ // Don't record management stats in cluster-unsafe contexts
+ if (mgmtObject != 0 && isClusterSafe())
+ {
+ mgmtObject->inc_framesToClient();
+ mgmtObject->inc_bytesToClient(frame.encodedSize());
+ if (isMessage(frame.getMethod())) {
+ mgmtObject->inc_msgsToClient();
+ }
+ }
+}
+
+void Connection::recordFromClient(const framing::AMQFrame& frame)
+{
+ // Don't record management stats in cluster-unsafe contexts
+ if (mgmtObject != 0 && isClusterSafe())
+ {
+ mgmtObject->inc_framesFromClient();
+ mgmtObject->inc_bytesFromClient(frame.encodedSize());
+ if (isMessage(frame.getMethod())) {
+ mgmtObject->inc_msgsFromClient();
+ }
+ }
+}
+
+string Connection::getAuthMechanism()
+{
+ if (!isLink)
+ return string("ANONYMOUS");
+
+ return links.getAuthMechanism(mgmtId);
+}
+
+string Connection::getUsername ( )
+{
+ if (!isLink)
+ return string("anonymous");
+
+ return links.getUsername(mgmtId);
+}
+
+string Connection::getPassword ( )
+{
+ if (!isLink)
+ return string("");
+
+ return links.getPassword(mgmtId);
+}
+
+string Connection::getHost ( )
+{
+ if (!isLink)
+ return string("");
+
+ return links.getHost(mgmtId);
+}
+
+uint16_t Connection::getPort ( )
+{
+ if (!isLink)
+ return 0;
+
+ return links.getPort(mgmtId);
+}
+
+string Connection::getAuthCredentials()
+{
+ if (!isLink)
+ return string();
+
+ if (mgmtObject != 0)
+ {
+ if (links.getAuthMechanism(mgmtId) == "ANONYMOUS")
+ mgmtObject->set_authIdentity("anonymous");
+ else
+ mgmtObject->set_authIdentity(links.getAuthIdentity(mgmtId));
+ }
+
+ return links.getAuthCredentials(mgmtId);
+}
+
+void Connection::notifyConnectionForced(const string& text)
+{
+ if (isLink)
+ links.notifyConnectionForced(mgmtId, text);
+}
+
+void Connection::setUserId(const string& userId)
+{
+ ConnectionState::setUserId(userId);
+ // In a cluster, the cluster code will raise the connect event
+ // when the connection is replicated to the cluster.
+ if (!broker.isInCluster()) raiseConnectEvent();
+}
+
+void Connection::raiseConnectEvent() {
+ if (mgmtObject != 0) {
+ mgmtObject->set_authIdentity(userId);
+ agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId));
+ }
+}
+
+void Connection::setFederationLink(bool b)
+{
+ ConnectionState::setFederationLink(b);
+ if (mgmtObject != 0)
+ mgmtObject->set_federationLink(b);
+}
+
+void Connection::close(connection::CloseCode code, const string& text)
+{
+ QPID_LOG_IF(error, code != connection::CLOSE_CODE_NORMAL, "Connection " << mgmtId << " closed by error: " << text << "(" << code << ")");
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+ if (timeoutTimer)
+ timeoutTimer->cancel();
+ adapter.close(code, text);
+ //make sure we delete dangling pointers from outputTasks before deleting sessions
+ outputTasks.removeAll();
+ channels.clear();
+ getOutput().close();
+}
+
+// Send a close to the client but keep the channels. Used by cluster.
+void Connection::sendClose() {
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+ if (timeoutTimer)
+ timeoutTimer->cancel();
+ adapter.close(connection::CLOSE_CODE_NORMAL, "OK");
+ getOutput().close();
+}
+
+void Connection::idleOut(){}
+
+void Connection::idleIn(){}
+
+void Connection::closed(){ // Physically closed, suspend open sessions.
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+ if (timeoutTimer)
+ timeoutTimer->cancel();
+ try {
+ while (!channels.empty())
+ ptr_map_ptr(channels.begin())->handleDetach();
+ } catch(std::exception& e) {
+ QPID_LOG(error, QPID_MSG("While closing connection: " << e.what()));
+ assert(0);
+ }
+}
+
+void Connection::doIoCallbacks() {
+ {
+ ScopedLock<Mutex> l(ioCallbackLock);
+ // Although IO callbacks execute in the connection thread context, they are
+ // not cluster safe because they are queued for execution in non-IO threads.
+ ClusterUnsafeScope cus;
+ while (!ioCallbacks.empty()) {
+ boost::function0<void> cb = ioCallbacks.front();
+ ioCallbacks.pop();
+ ScopedUnlock<Mutex> ul(ioCallbackLock);
+ cb(); // Lend the IO thread for management processing
+ }
+ }
+}
+
+bool Connection::doOutput() {
+ try {
+ doIoCallbacks();
+ if (mgmtClosing) {
+ closed();
+ close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request");
+ } else {
+ //then do other output as needed:
+ return outputTasks.doOutput();
+ }
+ }catch(const SessionOutputException& e){
+ getChannel(e.channel).handleException(e);
+ return true;
+ }catch(ConnectionException& e){
+ close(e.code, e.getMessage());
+ }catch(std::exception& e){
+ close(connection::CLOSE_CODE_CONNECTION_FORCED, e.what());
+ }
+ return false;
+}
+
+void Connection::sendHeartbeat() {
+ adapter.heartbeat();
+}
+
+void Connection::closeChannel(uint16_t id) {
+ ChannelMap::iterator i = channels.find(id);
+ if (i != channels.end()) channels.erase(i);
+}
+
+SessionHandler& Connection::getChannel(ChannelId id) {
+ ChannelMap::iterator i=channels.find(id);
+ if (i == channels.end()) {
+ i = channels.insert(id, new SessionHandler(*this, id)).first;
+ }
+ return *ptr_map_ptr(i);
+}
+
+ManagementObject* Connection::GetManagementObject(void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ QPID_LOG(debug, "Connection::ManagementMethod [id=" << methodId << "]");
+
+ switch (methodId)
+ {
+ case _qmf::Connection::METHOD_CLOSE :
+ mgmtClosing = true;
+ if (mgmtObject != 0) mgmtObject->set_closing(1);
+ out.activateOutput();
+ status = Manageable::STATUS_OK;
+ break;
+ }
+
+ return status;
+}
+
+void Connection::setSecureConnection(SecureConnection* s)
+{
+ adapter.setSecureConnection(s);
+}
+
+struct ConnectionHeartbeatTask : public sys::TimerTask {
+ sys::Timer& timer;
+ Connection& connection;
+ ConnectionHeartbeatTask(uint16_t hb, sys::Timer& t, Connection& c) :
+ TimerTask(Duration(hb*TIME_SEC), "ConnectionHeartbeat"),
+ timer(t),
+ connection(c)
+ {}
+
+ void fire() {
+ // Setup next firing
+ setupNextFire();
+ timer.add(this);
+
+ // Send Heartbeat
+ connection.sendHeartbeat();
+ }
+};
+
+void Connection::abort()
+{
+ // Make sure that we don't try to send a heartbeat as we're
+ // aborting the connection
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+
+ out.abort();
+}
+
+void Connection::setHeartbeatInterval(uint16_t heartbeat)
+{
+ setHeartbeat(heartbeat);
+ if (heartbeat > 0 && !isShadow()) {
+ heartbeatTimer = new ConnectionHeartbeatTask(heartbeat, timer, *this);
+ timer.add(heartbeatTimer);
+ timeoutTimer = new ConnectionTimeoutTask(heartbeat, timer, *this);
+ timer.add(timeoutTimer);
+ }
+}
+
+void Connection::restartTimeout()
+{
+ if (timeoutTimer)
+ timeoutTimer->touch();
+}
+
+bool Connection::isOpen() { return adapter.isOpen(); }
+
+Connection::OutboundFrameTracker::OutboundFrameTracker(Connection& _con) : con(_con), next(0) {}
+void Connection::OutboundFrameTracker::close() { next->close(); }
+size_t Connection::OutboundFrameTracker::getBuffered() const { return next->getBuffered(); }
+void Connection::OutboundFrameTracker::abort() { next->abort(); }
+void Connection::OutboundFrameTracker::activateOutput() { next->activateOutput(); }
+void Connection::OutboundFrameTracker::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); }
+void Connection::OutboundFrameTracker::send(framing::AMQFrame& f)
+{
+ next->send(f);
+ con.sent(f);
+}
+void Connection::OutboundFrameTracker::wrap(sys::ConnectionOutputHandlerPtr& p)
+{
+ next = p.get();
+ p.set(this);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/Connection.h b/qpid/cpp/src/qpid/broker/Connection.h
new file mode 100644
index 0000000000..8f1aa701ef
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Connection.h
@@ -0,0 +1,216 @@
+#ifndef QPID_BROKER_CONNECTION_H
+#define QPID_BROKER_CONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <memory>
+#include <sstream>
+#include <vector>
+#include <queue>
+
+#include <boost/ptr_container/ptr_map.hpp>
+
+#include "qpid/broker/ConnectionHandler.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/SessionHandler.h"
+#include "qmf/org/apache/qpid/broker/Connection.h"
+#include "qpid/Exception.h"
+#include "qpid/RefCounted.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+#include "qpid/framing/AMQP_ServerOperations.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/ptr_map.h"
+#include "qpid/sys/AggregateOutput.h"
+#include "qpid/sys/ConnectionInputHandler.h"
+#include "qpid/sys/ConnectionOutputHandler.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/TimeoutHandler.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/ptr_container/ptr_map.hpp>
+#include <boost/bind.hpp>
+
+#include <algorithm>
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+class LinkRegistry;
+class SecureConnection;
+struct ConnectionTimeoutTask;
+
+class Connection : public sys::ConnectionInputHandler,
+ public ConnectionState,
+ public RefCounted
+{
+ public:
+ /**
+ * Listener that can be registered with a Connection to be informed of errors.
+ */
+ class ErrorListener
+ {
+ public:
+ virtual ~ErrorListener() {}
+ virtual void sessionError(uint16_t channel, const std::string&) = 0;
+ virtual void connectionError(const std::string&) = 0;
+ };
+
+ Connection(sys::ConnectionOutputHandler* out,
+ Broker& broker,
+ const std::string& mgmtId,
+ const qpid::sys::SecuritySettings&,
+ bool isLink = false,
+ uint64_t objectId = 0,
+ bool shadow=false,
+ bool delayManagement = false);
+
+ ~Connection ();
+
+ /** Get the SessionHandler for channel. Create if it does not already exist */
+ SessionHandler& getChannel(framing::ChannelId channel);
+
+ /** Close the connection */
+ void close(framing::connection::CloseCode code, const std::string& text);
+
+ // ConnectionInputHandler methods
+ void received(framing::AMQFrame& frame);
+ void idleOut();
+ void idleIn();
+ bool doOutput();
+ void closed();
+
+ void closeChannel(framing::ChannelId channel);
+
+ // Manageable entry points
+ management::ManagementObject* GetManagementObject (void) const;
+ management::Manageable::status_t
+ ManagementMethod (uint32_t methodId, management::Args& args, std::string&);
+
+ void requestIOProcessing (boost::function0<void>);
+ void recordFromServer (const framing::AMQFrame& frame);
+ void recordFromClient (const framing::AMQFrame& frame);
+ std::string getAuthMechanism();
+ std::string getAuthCredentials();
+ std::string getUsername();
+ std::string getPassword();
+ std::string getHost();
+ uint16_t getPort();
+ void notifyConnectionForced(const std::string& text);
+ void setUserId(const std::string& uid);
+ void raiseConnectEvent();
+ const std::string& getUserId() const { return ConnectionState::getUserId(); }
+ const std::string& getMgmtId() const { return mgmtId; }
+ management::ManagementAgent* getAgent() const { return agent; }
+ void setFederationLink(bool b);
+ /** Connection does not delete the listener. 0 resets. */
+ void setErrorListener(ErrorListener* l) { errorListener=l; }
+ ErrorListener* getErrorListener() { return errorListener; }
+
+ void setHeartbeatInterval(uint16_t heartbeat);
+ void sendHeartbeat();
+ void restartTimeout();
+ void abort();
+
+ template <class F> void eachSessionHandler(F f) {
+ for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i)
+ f(*ptr_map_ptr(i));
+ }
+
+ void sendClose();
+ void setSecureConnection(SecureConnection* secured);
+
+ /** True if this is a shadow connection in a cluster. */
+ bool isShadow() { return shadow; }
+
+ // Used by cluster to update connection status
+ sys::AggregateOutput& getOutputTasks() { return outputTasks; }
+
+ /** Cluster delays adding management object in the constructor then calls this. */
+ void addManagementObject();
+
+ const qpid::sys::SecuritySettings& getExternalSecuritySettings() const
+ {
+ return securitySettings;
+ }
+
+ /** @return true if the initial connection negotiation is complete. */
+ bool isOpen();
+
+ // Used by cluster during catch-up, see cluster::OutputInterceptor
+ void doIoCallbacks();
+
+ private:
+ typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap;
+ typedef std::vector<boost::shared_ptr<Queue> >::iterator queue_iterator;
+
+ ChannelMap channels;
+ qpid::sys::SecuritySettings securitySettings;
+ ConnectionHandler adapter;
+ const bool isLink;
+ bool mgmtClosing;
+ const std::string mgmtId;
+ sys::Mutex ioCallbackLock;
+ std::queue<boost::function0<void> > ioCallbacks;
+ qmf::org::apache::qpid::broker::Connection* mgmtObject;
+ LinkRegistry& links;
+ management::ManagementAgent* agent;
+ sys::Timer& timer;
+ boost::intrusive_ptr<sys::TimerTask> heartbeatTimer;
+ boost::intrusive_ptr<ConnectionTimeoutTask> timeoutTimer;
+ ErrorListener* errorListener;
+ uint64_t objectId;
+ bool shadow;
+ /**
+ * Chained ConnectionOutputHandler that allows outgoing frames to be
+ * tracked (for updating mgmt stats).
+ */
+ class OutboundFrameTracker : public sys::ConnectionOutputHandler
+ {
+ public:
+ OutboundFrameTracker(Connection&);
+ void close();
+ size_t getBuffered() const;
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t credit);
+ void send(framing::AMQFrame&);
+ void wrap(sys::ConnectionOutputHandlerPtr&);
+ private:
+ Connection& con;
+ sys::ConnectionOutputHandler* next;
+ };
+ OutboundFrameTracker outboundTracker;
+
+
+ void sent(const framing::AMQFrame& f);
+ public:
+ qmf::org::apache::qpid::broker::Connection* getMgmtObject() { return mgmtObject; }
+};
+
+}}
+
+#endif /*!QPID_BROKER_CONNECTION_H*/
diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp
new file mode 100644
index 0000000000..9e0020812b
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/ConnectionFactory.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+
+using framing::ProtocolVersion;
+using qpid::sys::SecuritySettings;
+typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr;
+typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr;
+
+ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {}
+
+ConnectionFactory::~ConnectionFactory() {}
+
+sys::ConnectionCodec*
+ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ if (broker.getConnectionCounter().allowConnection())
+ {
+ QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused");
+ return 0;
+ }
+ if (v == ProtocolVersion(0, 10)) {
+ ConnectionPtr c(new amqp_0_10::Connection(out, id, false));
+ c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, false)));
+ return c.release();
+ }
+ return 0;
+}
+
+sys::ConnectionCodec*
+ConnectionFactory::create(sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ // used to create connections from one broker to another
+ ConnectionPtr c(new amqp_0_10::Connection(out, id, true));
+ c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, true)));
+ return c.release();
+}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.h b/qpid/cpp/src/qpid/broker/ConnectionFactory.h
new file mode 100644
index 0000000000..7c1a9a08e1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionFactory_
+#define _ConnectionFactory_
+
+#include "qpid/sys/ConnectionCodec.h"
+
+namespace qpid {
+namespace broker {
+class Broker;
+
+class ConnectionFactory : public sys::ConnectionCodec::Factory
+{
+ public:
+ ConnectionFactory(Broker& b);
+
+ virtual ~ConnectionFactory();
+
+ sys::ConnectionCodec*
+ create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id,
+ const qpid::sys::SecuritySettings&);
+
+ sys::ConnectionCodec*
+ create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&);
+
+ private:
+ Broker& broker;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp
new file mode 100644
index 0000000000..3f97e5b9de
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -0,0 +1,364 @@
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/SaslFactory.h"
+#include "qpid/broker/ConnectionHandler.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/SecureConnection.h"
+#include "qpid/Url.h"
+#include "qpid/framing/AllInvoker.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/broker/AclModule.h"
+#include "qmf/org/apache/qpid/broker/EventClientConnectFail.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using qpid::sys::SecurityLayer;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace
+{
+const std::string ANONYMOUS = "ANONYMOUS";
+const std::string PLAIN = "PLAIN";
+const std::string en_US = "en_US";
+const std::string QPID_FED_LINK = "qpid.fed_link";
+const std::string QPID_FED_TAG = "qpid.federation_tag";
+const std::string SESSION_FLOW_CONTROL("qpid.session_flow");
+const std::string CLIENT_PROCESS_NAME("qpid.client_process");
+const std::string CLIENT_PID("qpid.client_pid");
+const std::string CLIENT_PPID("qpid.client_ppid");
+const int SESSION_FLOW_CONTROL_VER = 1;
+const std::string SPACE(" ");
+}
+
+void ConnectionHandler::close(connection::CloseCode code, const string& text)
+{
+ handler->proxy.close(code, text);
+}
+
+void ConnectionHandler::heartbeat()
+{
+ handler->proxy.heartbeat();
+}
+
+void ConnectionHandler::handle(framing::AMQFrame& frame)
+{
+ AMQMethodBody* method=frame.getBody()->getMethod();
+ Connection::ErrorListener* errorListener = handler->connection.getErrorListener();
+ try{
+ if (!invoke(static_cast<AMQP_AllOperations::ConnectionHandler&>(*handler.get()), *method)) {
+ handler->connection.getChannel(frame.getChannel()).in(frame);
+ }
+ }catch(ConnectionException& e){
+ if (errorListener) errorListener->connectionError(e.what());
+ handler->proxy.close(e.code, e.what());
+ }catch(std::exception& e){
+ if (errorListener) errorListener->connectionError(e.what());
+ handler->proxy.close(541/*internal error*/, e.what());
+ }
+}
+
+void ConnectionHandler::setSecureConnection(SecureConnection* secured)
+{
+ handler->secured = secured;
+}
+
+ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient, bool isShadow) : handler(new Handler(connection, isClient, isShadow)) {}
+
+ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow) :
+ proxy(c.getOutput()),
+ connection(c), serverMode(!isClient), acl(0), secured(0),
+ isOpen(false)
+{
+ if (serverMode) {
+
+ acl = connection.getBroker().getAcl();
+
+ FieldTable properties;
+ Array mechanisms(0x95);
+
+ properties.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
+
+ authenticator = SaslAuthenticator::createAuthenticator(c, isShadow);
+ authenticator->getMechanisms(mechanisms);
+
+ Array locales(0x95);
+ boost::shared_ptr<FieldValue> l(new Str16Value(en_US));
+ locales.add(l);
+ proxy.start(properties, mechanisms, locales);
+
+ }
+
+ maxFrameSize = (64 * 1024) - 1;
+}
+
+
+ConnectionHandler::Handler::~Handler() {}
+
+
+void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProperties,
+ const string& mechanism,
+ const string& response,
+ const string& /*locale*/)
+{
+ try {
+ authenticator->start(mechanism, response);
+ } catch (std::exception& /*e*/) {
+ management::ManagementAgent* agent = connection.getAgent();
+ if (agent) {
+ string error;
+ string uid;
+ authenticator->getError(error);
+ authenticator->getUid(uid);
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ }
+ throw;
+ }
+ connection.setFederationLink(clientProperties.get(QPID_FED_LINK));
+ connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG));
+ if (connection.isFederationLink()) {
+ if (acl && !acl->authorise(connection.getUserId(),acl::ACT_CREATE,acl::OBJ_LINK,"")){
+ proxy.close(framing::connection::CLOSE_CODE_CONNECTION_FORCED,"ACL denied creating a federation link");
+ return;
+ }
+ QPID_LOG(info, "Connection is a federation link");
+ }
+ if (clientProperties.getAsInt(SESSION_FLOW_CONTROL) == SESSION_FLOW_CONTROL_VER) {
+ connection.setClientThrottling();
+ }
+
+ if (connection.getMgmtObject() != 0) {
+ string procName = clientProperties.getAsString(CLIENT_PROCESS_NAME);
+ uint32_t pid = clientProperties.getAsInt(CLIENT_PID);
+ uint32_t ppid = clientProperties.getAsInt(CLIENT_PPID);
+
+ if (!procName.empty())
+ connection.getMgmtObject()->set_remoteProcessName(procName);
+ if (pid != 0)
+ connection.getMgmtObject()->set_remotePid(pid);
+ if (ppid != 0)
+ connection.getMgmtObject()->set_remoteParentPid(ppid);
+ }
+}
+
+void ConnectionHandler::Handler::secureOk(const string& response)
+{
+ try {
+ authenticator->step(response);
+ } catch (std::exception& /*e*/) {
+ management::ManagementAgent* agent = connection.getAgent();
+ if (agent) {
+ string error;
+ string uid;
+ authenticator->getError(error);
+ authenticator->getUid(uid);
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ }
+ throw;
+ }
+}
+
+void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/,
+ uint16_t framemax, uint16_t heartbeat)
+{
+ connection.setFrameMax(framemax);
+ connection.setHeartbeatInterval(heartbeat);
+}
+
+void ConnectionHandler::Handler::open(const string& /*virtualHost*/,
+ const framing::Array& /*capabilities*/, bool /*insist*/)
+{
+ std::vector<Url> urls = connection.broker.getKnownBrokers();
+ framing::Array array(0x95); // str16 array
+ for (std::vector<Url>::iterator i = urls.begin(); i < urls.end(); ++i)
+ array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str())));
+
+ //install security layer if one has been negotiated:
+ if (secured) {
+ std::auto_ptr<SecurityLayer> sl = authenticator->getSecurityLayer(connection.getFrameMax());
+ if (sl.get()) secured->activateSecurityLayer(sl);
+ }
+
+ isOpen = true;
+ proxy.openOk(array);
+}
+
+
+void ConnectionHandler::Handler::close(uint16_t replyCode, const string& replyText)
+{
+ if (replyCode != 200) {
+ QPID_LOG(warning, "Client closed connection with " << replyCode << ": " << replyText);
+ }
+
+ if (replyCode == framing::connection::CLOSE_CODE_CONNECTION_FORCED)
+ connection.notifyConnectionForced(replyText);
+
+ proxy.closeOk();
+ connection.getOutput().close();
+}
+
+void ConnectionHandler::Handler::closeOk(){
+ connection.getOutput().close();
+}
+
+void ConnectionHandler::Handler::heartbeat(){
+ // For general case, do nothing - the purpose of heartbeats is
+ // just to make sure that there is some traffic on the connection
+ // within the heart beat interval, we check for the traffic and
+ // don't need to do anything in response to heartbeats. The
+ // exception is when we are in fact the client to another broker
+ // (i.e. an inter-broker link), in which case we echo the
+ // heartbeat back to the peer
+ if (!serverMode) proxy.heartbeat();
+}
+
+void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
+ const framing::Array& supportedMechanisms,
+ const framing::Array& /*locales*/)
+{
+ string requestedMechanism = connection.getAuthMechanism();
+
+ std::string username = connection.getUsername();
+
+ std::string password = connection.getPassword();
+ std::string host = connection.getHost();
+ std::string service("qpidd");
+
+ if ( connection.getBroker().isAuthenticating() ) {
+ sasl = SaslFactory::getInstance().create( username,
+ password,
+ service,
+ host,
+ 0, // TODO -- mgoulish Fri Sep 24 2010
+ 256,
+ false ); // disallow interaction
+ }
+ std::string supportedMechanismsList;
+ bool requestedMechanismIsSupported = false;
+ Array::const_iterator i;
+
+ /*
+ If no specific mechanism has been requested, just make
+ a list of all of them, and assert that the one the caller
+ requested is there. ( If *any* are supported! )
+ */
+ if ( requestedMechanism.empty() ) {
+ for ( i = supportedMechanisms.begin(); i != supportedMechanisms.end(); ++i) {
+ if (i != supportedMechanisms.begin())
+ supportedMechanismsList += SPACE;
+ supportedMechanismsList += (*i)->get<std::string>();
+ requestedMechanismIsSupported = true;
+ }
+ }
+ else {
+ requestedMechanismIsSupported = false;
+ /*
+ The caller has requested a mechanism. If it's available,
+ make sure it ends up at the head of the list.
+ */
+ for ( i = supportedMechanisms.begin(); i != supportedMechanisms.end(); ++i) {
+ string currentMechanism = (*i)->get<std::string>();
+
+ if ( requestedMechanism == currentMechanism ) {
+ requestedMechanismIsSupported = true;
+ supportedMechanismsList = currentMechanism + SPACE + supportedMechanismsList;
+ } else {
+ if (i != supportedMechanisms.begin())
+ supportedMechanismsList += SPACE;
+ supportedMechanismsList += currentMechanism;
+ }
+ }
+ }
+
+ connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG));
+
+ FieldTable ft;
+ ft.setInt(QPID_FED_LINK,1);
+ ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
+
+ string response;
+ if (sasl.get()) {
+ const qpid::sys::SecuritySettings& ss = connection.getExternalSecuritySettings();
+ response = sasl->start ( requestedMechanism.empty()
+ ? supportedMechanismsList
+ : requestedMechanism,
+ & ss );
+ proxy.startOk ( ft, sasl->getMechanism(), response, en_US );
+ }
+ else {
+ response = ((char)0) + username + ((char)0) + password;
+ proxy.startOk ( ft, requestedMechanism, response, en_US );
+ }
+
+}
+
+void ConnectionHandler::Handler::secure(const string& challenge )
+{
+ if (sasl.get()) {
+ string response = sasl->step(challenge);
+ proxy.secureOk(response);
+ }
+ else {
+ proxy.secureOk("");
+ }
+}
+
+void ConnectionHandler::Handler::tune(uint16_t channelMax,
+ uint16_t maxFrameSizeProposed,
+ uint16_t /*heartbeatMin*/,
+ uint16_t heartbeatMax)
+{
+ maxFrameSize = std::min(maxFrameSize, maxFrameSizeProposed);
+ connection.setFrameMax(maxFrameSize);
+
+ connection.setHeartbeat(heartbeatMax);
+ proxy.tuneOk(channelMax, maxFrameSize, heartbeatMax);
+ proxy.open("/", Array(), true);
+}
+
+void ConnectionHandler::Handler::openOk(const framing::Array& knownHosts)
+{
+ for (Array::ValueVector::const_iterator i = knownHosts.begin(); i != knownHosts.end(); ++i) {
+ Url url((*i)->get<std::string>());
+ connection.getKnownHosts().push_back(url);
+ }
+
+ if (sasl.get()) {
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer = sasl->getSecurityLayer(maxFrameSize);
+
+ if ( securityLayer.get() ) {
+ secured->activateSecurityLayer(securityLayer, true);
+ }
+
+ saslUserId = sasl->getUserId();
+ }
+
+ isOpen = true;
+}
+
+void ConnectionHandler::Handler::redirect(const string& /*host*/, const framing::Array& /*knownHosts*/)
+{
+
+}
diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.h b/qpid/cpp/src/qpid/broker/ConnectionHandler.h
new file mode 100644
index 0000000000..b32167669e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionAdapter_
+#define _ConnectionAdapter_
+
+#include <memory>
+#include "qpid/Sasl.h"
+#include "qpid/broker/SaslAuthenticator.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQP_AllOperations.h"
+#include "qpid/framing/AMQP_AllProxy.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/Exception.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/sys/SecurityLayer.h"
+
+
+namespace qpid {
+
+namespace sys {
+struct SecuritySettings;
+}
+
+
+namespace broker {
+
+class Connection;
+class SecureConnection;
+
+class ConnectionHandler : public framing::FrameHandler
+{
+ struct Handler : public framing::AMQP_AllOperations::ConnectionHandler
+ {
+ framing::AMQP_AllProxy::Connection proxy;
+ Connection& connection;
+ bool serverMode;
+ std::auto_ptr<SaslAuthenticator> authenticator;
+ AclModule* acl;
+ SecureConnection* secured;
+ bool isOpen;
+
+ Handler(Connection& connection, bool isClient, bool isShadow=false);
+ ~Handler();
+ void startOk(const qpid::framing::FieldTable& clientProperties,
+ const std::string& mechanism, const std::string& response,
+ const std::string& locale);
+ void secureOk(const std::string& response);
+ void tuneOk(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeat);
+ void heartbeat();
+ void open(const std::string& virtualHost,
+ const framing::Array& capabilities, bool insist);
+ void close(uint16_t replyCode, const std::string& replyText);
+ void closeOk();
+
+ void start(const qpid::framing::FieldTable& serverProperties,
+ const framing::Array& mechanisms,
+ const framing::Array& locales);
+
+ void secure(const std::string& challenge);
+
+ void tune(uint16_t channelMax,
+ uint16_t frameMax,
+ uint16_t heartbeatMin,
+ uint16_t heartbeatMax);
+
+ void openOk(const framing::Array& knownHosts);
+
+ void redirect(const std::string& host, const framing::Array& knownHosts);
+
+ std::auto_ptr<Sasl> sasl;
+ typedef boost::function<const qpid::sys::SecuritySettings*()> GetSecuritySettings;
+ std::string saslUserId;
+ uint16_t maxFrameSize;
+ };
+ std::auto_ptr<Handler> handler;
+
+
+ public:
+ ConnectionHandler(Connection& connection, bool isClient, bool isShadow=false );
+ void close(framing::connection::CloseCode code, const std::string& text);
+ void heartbeat();
+ void handle(framing::AMQFrame& frame);
+ void setSecureConnection(SecureConnection* secured);
+ bool isOpen() { return handler->isOpen; }
+};
+
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/ConnectionState.h b/qpid/cpp/src/qpid/broker/ConnectionState.h
new file mode 100644
index 0000000000..9c31a931d8
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionState.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionState_
+#define _ConnectionState_
+
+#include <vector>
+
+#include "qpid/sys/AggregateOutput.h"
+#include "qpid/sys/ConnectionOutputHandlerPtr.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/Url.h"
+#include "qpid/broker/Broker.h"
+
+namespace qpid {
+namespace broker {
+
+class ConnectionState : public ConnectionToken, public management::Manageable
+{
+ protected:
+ sys::ConnectionOutputHandlerPtr out;
+
+ public:
+ ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) :
+ out(o),
+ broker(b),
+ outputTasks(out),
+ framemax(65535),
+ heartbeat(0),
+ heartbeatmax(120),
+ federationLink(true),
+ clientSupportsThrottling(false),
+ clusterOrderOut(0)
+ {}
+
+ virtual ~ConnectionState () {}
+
+ uint32_t getFrameMax() const { return framemax; }
+ uint16_t getHeartbeat() const { return heartbeat; }
+ uint16_t getHeartbeatMax() const { return heartbeatmax; }
+
+ void setFrameMax(uint32_t fm) { framemax = std::max(fm, (uint32_t) 4096); }
+ void setHeartbeat(uint16_t hb) { heartbeat = hb; }
+ void setHeartbeatMax(uint16_t hbm) { heartbeatmax = hbm; }
+
+ virtual void setUserId(const std::string& uid) { userId = uid; }
+ const std::string& getUserId() const { return userId; }
+
+ void setUrl(const std::string& _url) { url = _url; }
+ const std::string& getUrl() const { return url; }
+
+ void setFederationLink(bool b) { federationLink = b; }
+ bool isFederationLink() const { return federationLink; }
+ void setFederationPeerTag(const std::string& tag) { federationPeerTag = std::string(tag); }
+ const std::string& getFederationPeerTag() const { return federationPeerTag; }
+ std::vector<Url>& getKnownHosts() { return knownHosts; }
+
+ void setClientThrottling(bool set=true) { clientSupportsThrottling = set; }
+ bool getClientThrottling() const { return clientSupportsThrottling; }
+
+ Broker& getBroker() { return broker; }
+
+ Broker& broker;
+
+ //contained output tasks
+ sys::AggregateOutput outputTasks;
+
+ sys::ConnectionOutputHandler& getOutput() { return out; }
+ framing::ProtocolVersion getVersion() const { return version; }
+ void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out.set(o); }
+
+ /**
+ * If the broker is part of a cluster, this is a handler provided
+ * by cluster code. It ensures consistent ordering of commands
+ * that are sent based on criteria that are not predictably
+ * ordered cluster-wide, e.g. a timer firing.
+ */
+ framing::FrameHandler* getClusterOrderOutput() { return clusterOrderOut; }
+ void setClusterOrderOutput(framing::FrameHandler& fh) { clusterOrderOut = &fh; }
+
+ virtual void requestIOProcessing (boost::function0<void>) = 0;
+
+ protected:
+ framing::ProtocolVersion version;
+ uint32_t framemax;
+ uint16_t heartbeat;
+ uint16_t heartbeatmax;
+ std::string userId;
+ std::string url;
+ bool federationLink;
+ std::string federationPeerTag;
+ std::vector<Url> knownHosts;
+ bool clientSupportsThrottling;
+ framing::FrameHandler* clusterOrderOut;
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/ConnectionToken.h b/qpid/cpp/src/qpid/broker/ConnectionToken.h
new file mode 100644
index 0000000000..9b40383c80
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConnectionToken.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionToken_
+#define _ConnectionToken_
+
+#include "qpid/broker/OwnershipToken.h"
+namespace qpid {
+ namespace broker {
+ /**
+ * An empty interface allowing opaque implementations of some
+ * form of token to identify a connection.
+ */
+ class ConnectionToken : public OwnershipToken {
+ public:
+ virtual bool isLocal(const ConnectionToken* t) const { return this == t; }
+ virtual ~ConnectionToken(){}
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Consumer.h b/qpid/cpp/src/qpid/broker/Consumer.h
new file mode 100644
index 0000000000..317338a8ad
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Consumer.h
@@ -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.
+ *
+ */
+#ifndef _Consumer_
+#define _Consumer_
+
+#include "qpid/broker/Message.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/OwnershipToken.h"
+
+namespace qpid {
+namespace broker {
+
+class Queue;
+class QueueListeners;
+
+class Consumer {
+ const bool acquires;
+ // inListeners allows QueueListeners to efficiently track if this instance is registered
+ // for notifications without having to search its containers
+ bool inListeners;
+ public:
+ typedef boost::shared_ptr<Consumer> shared_ptr;
+
+ framing::SequenceNumber position;
+
+ Consumer(bool preAcquires = true) : acquires(preAcquires), inListeners(false) {}
+ bool preAcquires() const { return acquires; }
+ virtual bool deliver(QueuedMessage& msg) = 0;
+ virtual void notify() = 0;
+ virtual bool filter(boost::intrusive_ptr<Message>) { return true; }
+ virtual bool accept(boost::intrusive_ptr<Message>) { return true; }
+ virtual OwnershipToken* getSession() = 0;
+ virtual ~Consumer(){}
+ friend class QueueListeners;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp
new file mode 100644
index 0000000000..b30e5f18cb
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Daemon.cpp
@@ -0,0 +1,219 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * TODO: Note this is really a Posix specific implementation and so should be
+ * refactored together with windows/QpiddBroker into a more coherent daemon driver/
+ * platform specific split
+ */
+#include "qpid/broker/Daemon.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/posix/PidFile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace qpid {
+namespace broker {
+
+using namespace std;
+using qpid::sys::PidFile;
+
+Daemon::Daemon(std::string _pidDir) : pidDir(_pidDir) {
+ struct stat s;
+ pid = -1;
+ pipeFds[0] = pipeFds[1] = -1;
+
+ if (::stat(pidDir.c_str(), &s)) {
+ if (errno == ENOENT) {
+ if (::mkdir(pidDir.c_str(), 0755))
+ throw Exception ("Can't create PID directory: " + pidDir);
+ }
+ else
+ throw Exception ("PID directory not found: " + pidDir);
+ }
+}
+
+string Daemon::pidFile(string pidDir, uint16_t port) {
+ ostringstream path;
+ path << pidDir << "/qpidd." << port << ".pid";
+ return path.str();
+}
+
+/*
+ * Rewritten using low-level IO, for compatibility
+ * with earlier Boost versions, i.e. 103200.
+ */
+void Daemon::fork()
+{
+ if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
+ if ((pid = ::fork()) < 0) throw ErrnoException("Daemon fork failed");
+ if (pid == 0) { // Child
+ try {
+ QPID_LOG(debug, "Forked daemon child process");
+
+ // File descriptors
+ if(::close(pipeFds[0])<0) throw ErrnoException("Cannot close read pipe");
+ if(::close(0)<0) throw ErrnoException("Cannot close stdin");
+ if(::close(1)<0) throw ErrnoException("Cannot close stdout");
+ if(::close(2)<0) throw ErrnoException("Cannot close stderr");
+ int fd=::open("/dev/null",O_RDWR); // stdin
+ if(fd != 0) throw ErrnoException("Cannot re-open stdin");
+ if(::dup(fd)<0) throw ErrnoException("Cannot re-open stdout");
+ if(::dup(fd)<0) throw ErrnoException("Cannot re-open stderror");
+
+ // Misc
+ if(setsid()<0) throw ErrnoException("Cannot set session ID");
+ if(chdir(pidDir.c_str()) < 0) throw ErrnoException("Cannot change directory to "+pidDir);
+ umask(027);
+
+ // Child behavior
+ child();
+ }
+ catch (const exception& e) {
+ QPID_LOG(critical, "Unexpected error: " << e.what());
+ uint16_t port = 0;
+ int unused_ret; //Supress warning about ignoring return value.
+ unused_ret = write(pipeFds[1], &port, sizeof(uint16_t));
+
+ std::string pipeFailureMessage = e.what();
+ unused_ret = write ( pipeFds[1],
+ pipeFailureMessage.c_str(),
+ strlen(pipeFailureMessage.c_str())
+ );
+ }
+ }
+ else { // Parent
+ close(pipeFds[1]); // Write side.
+ parent();
+ }
+}
+
+Daemon::~Daemon() {
+ if (!lockFile.empty())
+ unlink(lockFile.c_str());
+}
+
+uint16_t Daemon::wait(int timeout) { // parent waits for child.
+ try {
+ errno = 0;
+ struct timeval tv;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ /*
+ * Rewritten using low-level IO, for compatibility
+ * with earlier Boost versions, i.e. 103200.
+ */
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(pipeFds[0], &fds);
+ int n=select(FD_SETSIZE, &fds, 0, 0, &tv);
+ if(n==0) throw Exception("Timed out waiting for daemon (If store recovery is in progress, use longer wait time)");
+ if(n<0) throw ErrnoException("Error waiting for daemon");
+ uint16_t port = 0;
+ /*
+ * Read the child's port number from the pipe.
+ */
+ int desired_read = sizeof(uint16_t);
+ if ( desired_read > ::read(pipeFds[0], & port, desired_read) )
+ throw Exception("Cannot read from child process.");
+
+ /*
+ * If the port number is 0, the child has put an error message
+ * on the pipe. Get it and throw it.
+ */
+ if ( 0 == port ) {
+ // Skip whitespace
+ char c = ' ';
+ while ( isspace(c) ) {
+ if ( 1 > ::read(pipeFds[0], &c, 1) )
+ throw Exception("Child port == 0, and no error message on pipe.");
+ }
+
+ // Get Message
+ string errmsg;
+ do {
+ errmsg += c;
+ } while (::read(pipeFds[0], &c, 1));
+ throw Exception("Daemon startup failed"+
+ (errmsg.empty() ? string(".") : ": " + errmsg));
+ }
+ return port;
+ }
+ catch (const std::exception& e) {
+ // Print directly to cerr. The caller will catch and log the
+ // exception, but in the case of a daemon parent process we
+ // also need to be sure the error goes to stderr. A
+ // dameon's logging configuration normally does not log to
+ // stderr.
+ std::cerr << e.what() << endl;
+ throw;
+ }
+}
+
+
+/*
+ * When the child is ready, it writes its pid to the
+ * lockfile and its port number on the pipe back to
+ * its parent process. This indicates that the
+ * child has successfully daemonized. When the parent
+ * hears the good news, it ill exit.
+ */
+void Daemon::ready(uint16_t port) { // child
+ lockFile = pidFile(pidDir, port);
+ PidFile lf(lockFile, true);
+
+ /*
+ * Write the PID to the lockfile.
+ */
+ lf.writePid();
+
+ /*
+ * Write the port number to the parent.
+ */
+ int desired_write = sizeof(uint16_t);
+ if ( desired_write > ::write(pipeFds[1], & port, desired_write) ) {
+ throw Exception("Error writing to parent." );
+ }
+
+ QPID_LOG(debug, "Daemon ready on port: " << port);
+}
+
+/*
+ * The parent process reads the child's pid
+ * from the lockfile.
+ */
+pid_t Daemon::getPid(string _pidDir, uint16_t port) {
+ string name = pidFile(_pidDir, port);
+ PidFile lf(name, false);
+ pid_t pid = lf.readPid();
+ if (kill(pid, 0) < 0 && errno != EPERM) {
+ unlink(name.c_str());
+ throw Exception("Removing stale lock file "+name);
+ }
+ return pid;
+}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Daemon.h b/qpid/cpp/src/qpid/broker/Daemon.h
new file mode 100644
index 0000000000..a9cd98bce2
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Daemon.h
@@ -0,0 +1,84 @@
+#ifndef _broker_Daemon_h
+#define _broker_Daemon_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/scoped_ptr.hpp>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+#include <string>
+
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Tools for forking and managing a daemon process.
+ * NB: Only one Daemon instance is allowed in a process.
+ */
+class Daemon : private boost::noncopyable
+{
+ public:
+ /** Check daemon is running on port, throw exception if not */
+ static pid_t getPid(std::string pidDir, uint16_t port);
+
+ Daemon(std::string pidDir);
+
+ virtual ~Daemon();
+
+ /**
+ * Fork a daemon process.
+ * Call parent() in the parent process, child() in the child.
+ */
+ void fork();
+
+ protected:
+
+ /** Called in parent process */
+ virtual void parent() = 0;
+
+ /** Called in child process */
+ virtual void child() = 0;
+
+ /** Call from parent(): wait for child to indicate it is ready.
+ * @timeout in seconds to wait for response.
+ * @return port passed by child to ready().
+ */
+ uint16_t wait(int timeout);
+
+ /** Call from child(): Notify the parent we are ready and write the
+ * PID file.
+ *@param port returned by parent call to wait().
+ */
+ void ready(uint16_t port);
+
+ private:
+ static std::string pidFile(std::string pidDir, uint16_t port);
+
+ pid_t pid;
+ int pipeFds[2];
+ int lockFileFd;
+ std::string lockFile;
+ std::string pidDir;
+};
+
+}} // namespace qpid::broker
+
+#endif /*!_broker_Daemon_h*/
diff --git a/qpid/cpp/src/qpid/broker/Deliverable.h b/qpid/cpp/src/qpid/broker/Deliverable.h
new file mode 100644
index 0000000000..ffb5a77bca
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Deliverable.h
@@ -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.
+ *
+ */
+#ifndef _Deliverable_
+#define _Deliverable_
+
+#include "qpid/broker/Message.h"
+
+namespace qpid {
+ namespace broker {
+ class Deliverable{
+ public:
+ bool delivered;
+ Deliverable() : delivered(false) {}
+
+ virtual Message& getMessage() = 0;
+
+ virtual void deliverTo(const boost::shared_ptr<Queue>& queue) = 0;
+ virtual uint64_t contentSize() { return 0; }
+ virtual ~Deliverable(){}
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp b/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp
new file mode 100644
index 0000000000..3ebb12461c
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/Queue.h"
+
+using namespace qpid::broker;
+
+DeliverableMessage::DeliverableMessage(const boost::intrusive_ptr<Message>& _msg) : msg(_msg)
+{
+}
+
+void DeliverableMessage::deliverTo(const boost::shared_ptr<Queue>& queue)
+{
+ queue->deliver(msg);
+ delivered = true;
+}
+
+Message& DeliverableMessage::getMessage()
+{
+ return *msg;
+}
+
+uint64_t DeliverableMessage::contentSize ()
+{
+ return msg->contentSize ();
+}
diff --git a/qpid/cpp/src/qpid/broker/DeliverableMessage.h b/qpid/cpp/src/qpid/broker/DeliverableMessage.h
new file mode 100644
index 0000000000..c8d21001eb
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.h
@@ -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.
+ *
+ */
+#ifndef _DeliverableMessage_
+#define _DeliverableMessage_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+ namespace broker {
+ class QPID_BROKER_CLASS_EXTERN DeliverableMessage : public Deliverable{
+ boost::intrusive_ptr<Message> msg;
+ public:
+ QPID_BROKER_EXTERN DeliverableMessage(const boost::intrusive_ptr<Message>& msg);
+ QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue);
+ QPID_BROKER_EXTERN Message& getMessage();
+ QPID_BROKER_EXTERN uint64_t contentSize();
+ virtual ~DeliverableMessage(){}
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DeliveryAdapter.h b/qpid/cpp/src/qpid/broker/DeliveryAdapter.h
new file mode 100644
index 0000000000..b0bec60890
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliveryAdapter.h
@@ -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.
+ *
+ */
+#ifndef _DeliveryAdapter_
+#define _DeliveryAdapter_
+
+#include "qpid/broker/DeliveryId.h"
+#include "qpid/broker/Message.h"
+#include "qpid/framing/amqp_types.h"
+
+namespace qpid {
+namespace broker {
+
+class DeliveryRecord;
+
+/**
+ * The intention behind this interface is to separate the generic
+ * handling of some form of message delivery to clients that is
+ * contained in the version independent Channel class from the
+ * details required for a particular situation or
+ * version. i.e. where the existing adapters allow (through
+ * supporting the generated interface for a version of the
+ * protocol) inputs of a channel to be adapted to the version
+ * independent part, this does the same for the outputs.
+ */
+class DeliveryAdapter
+{
+ public:
+ virtual void deliver(DeliveryRecord&, bool sync) = 0;
+ virtual ~DeliveryAdapter(){}
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DeliveryId.h b/qpid/cpp/src/qpid/broker/DeliveryId.h
new file mode 100644
index 0000000000..05b19f032e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliveryId.h
@@ -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.
+ *
+ */
+#ifndef _DeliveryId_
+#define _DeliveryId_
+
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceNumberSet.h"
+
+namespace qpid {
+namespace broker {
+
+ typedef framing::SequenceNumber DeliveryId;
+ typedef framing::SequenceNumberSet DeliveryIds;
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp
new file mode 100644
index 0000000000..58dcc6d7c7
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/MessageTransferBody.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+using std::string;
+
+DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg,
+ const Queue::shared_ptr& _queue,
+ const std::string& _tag,
+ bool _acquired,
+ bool accepted,
+ bool _windowing,
+ uint32_t _credit) : msg(_msg),
+ queue(_queue),
+ tag(_tag),
+ acquired(_acquired),
+ acceptExpected(!accepted),
+ cancelled(false),
+ completed(false),
+ ended(accepted && acquired),
+ windowing(_windowing),
+ credit(msg.payload ? msg.payload->getRequiredCredit() : _credit)
+{}
+
+bool DeliveryRecord::setEnded()
+{
+ ended = true;
+ //reset msg pointer, don't need to hold on to it anymore
+ msg.payload = boost::intrusive_ptr<Message>();
+ QPID_LOG(debug, "DeliveryRecord::setEnded() id=" << id);
+ return isRedundant();
+}
+
+void DeliveryRecord::redeliver(SemanticState* const session) {
+ if (!ended) {
+ if(cancelled){
+ //if subscription was cancelled, requeue it (waiting for
+ //final confirmation for AMQP WG on this case)
+ requeue();
+ }else{
+ msg.payload->redeliver();//mark as redelivered
+ session->deliver(*this, false);
+ }
+ }
+}
+
+void DeliveryRecord::deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize)
+{
+ id = deliveryId;
+ if (msg.payload->getRedelivered()){
+ msg.payload->getProperties<framing::DeliveryProperties>()->setRedelivered(true);
+ }
+ msg.payload->adjustTtl();
+
+ framing::AMQFrame method((framing::MessageTransferBody(framing::ProtocolVersion(), tag, acceptExpected ? 0 : 1, acquired ? 0 : 1)));
+ method.setEof(false);
+ h.handle(method);
+ msg.payload->sendHeader(h, framesize);
+ msg.payload->sendContent(*queue, h, framesize);
+}
+
+void DeliveryRecord::requeue() const
+{
+ if (acquired && !ended) {
+ msg.payload->redeliver();
+ queue->requeue(msg);
+ }
+}
+
+void DeliveryRecord::release(bool setRedelivered)
+{
+ if (acquired && !ended) {
+ if (setRedelivered) msg.payload->redeliver();
+ queue->requeue(msg);
+ acquired = false;
+ setEnded();
+ } else {
+ QPID_LOG(debug, "Ignoring release for " << id << " acquired=" << acquired << ", ended =" << ended);
+ }
+}
+
+void DeliveryRecord::complete() {
+ completed = true;
+}
+
+bool DeliveryRecord::accept(TransactionContext* ctxt) {
+ if (acquired && !ended) {
+ queue->dequeue(ctxt, msg);
+ setEnded();
+ QPID_LOG(debug, "Accepted " << id);
+ }
+ return isRedundant();
+}
+
+void DeliveryRecord::dequeue(TransactionContext* ctxt) const{
+ if (acquired && !ended) {
+ queue->dequeue(ctxt, msg);
+ }
+}
+
+void DeliveryRecord::committed() const{
+ queue->dequeueCommitted(msg);
+}
+
+void DeliveryRecord::reject()
+{
+ if (acquired && !ended) {
+ Exchange::shared_ptr alternate = queue->getAlternateExchange();
+ if (alternate) {
+ DeliverableMessage delivery(msg.payload);
+ alternate->routeWithAlternate(delivery);
+ QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to "
+ << alternate->getName());
+ } else {
+ //just drop it
+ QPID_LOG(info, "Dropping rejected message from " << queue->getName());
+ }
+ dequeue();
+ setEnded();
+ }
+}
+
+uint32_t DeliveryRecord::getCredit() const
+{
+ return credit;
+}
+
+void DeliveryRecord::acquire(DeliveryIds& results) {
+ if (queue->acquire(msg)) {
+ acquired = true;
+ results.push_back(id);
+ if (!acceptExpected) {
+ if (ended) { QPID_LOG(error, "Can't dequeue ended message"); }
+ else { queue->dequeue(0, msg); setEnded(); }
+ }
+ } else {
+ QPID_LOG(info, "Message already acquired " << id.getValue());
+ }
+}
+
+void DeliveryRecord::cancel(const std::string& cancelledTag)
+{
+ if (tag == cancelledTag)
+ cancelled = true;
+}
+
+AckRange DeliveryRecord::findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last)
+{
+ DeliveryRecords::iterator start = lower_bound(records.begin(), records.end(), first);
+ // Find end - position it just after the last record in range
+ DeliveryRecords::iterator end = lower_bound(records.begin(), records.end(), last);
+ if (end != records.end() && end->getId() == last) ++end;
+ return AckRange(start, end);
+}
+
+
+namespace qpid {
+namespace broker {
+
+std::ostream& operator<<(std::ostream& out, const DeliveryRecord& r)
+{
+ out << "{" << "id=" << r.id.getValue();
+ out << ", tag=" << r.tag << "}";
+ out << ", queue=" << r.queue->getName() << "}";
+ return out;
+}
+
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/DeliveryRecord.h b/qpid/cpp/src/qpid/broker/DeliveryRecord.h
new file mode 100644
index 0000000000..d388ba94be
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DeliveryRecord.h
@@ -0,0 +1,143 @@
+#ifndef QPID_BROKER_DELIVERYRECORD_H
+#define QPID_BROKER_DELIVERYRECORD_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <algorithm>
+#include <deque>
+#include <vector>
+#include <ostream>
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/DeliveryId.h"
+#include "qpid/broker/Message.h"
+
+namespace qpid {
+namespace broker {
+
+class TransactionContext;
+class SemanticState;
+struct AckRange;
+
+/**
+ * Record of a delivery for which an ack is outstanding.
+ */
+class DeliveryRecord
+{
+ QueuedMessage msg;
+ mutable boost::shared_ptr<Queue> queue;
+ std::string tag;
+ DeliveryId id;
+ bool acquired : 1;
+ bool acceptExpected : 1;
+ bool cancelled : 1;
+ bool completed : 1;
+ bool ended : 1;
+ bool windowing : 1;
+
+ /**
+ * Record required credit on construction as the pointer to the
+ * message may be reset once we no longer need to deliver it
+ * (e.g. when it is accepted), but we will still need to be able
+ * to reallocate credit when it is completed (which could happen
+ * after that).
+ */
+ uint32_t credit;
+
+ public:
+ QPID_BROKER_EXTERN DeliveryRecord(const QueuedMessage& msg,
+ const boost::shared_ptr<Queue>& queue,
+ const std::string& tag,
+ bool acquired,
+ bool accepted,
+ bool windowing,
+ uint32_t credit=0 // Only used if msg is empty.
+ );
+
+ bool coveredBy(const framing::SequenceSet* const range) const { return range->contains(id); }
+
+ void dequeue(TransactionContext* ctxt = 0) const;
+ void requeue() const;
+ void release(bool setRedelivered);
+ void reject();
+ void cancel(const std::string& tag);
+ void redeliver(SemanticState* const);
+ void acquire(DeliveryIds& results);
+ void complete();
+ bool accept(TransactionContext* ctxt); // Returns isRedundant()
+ bool setEnded(); // Returns isRedundant()
+ void committed() const;
+
+ bool isAcquired() const { return acquired; }
+ bool isComplete() const { return completed; }
+ bool isRedundant() const { return ended && (!windowing || completed); }
+ bool isCancelled() const { return cancelled; }
+ bool isAccepted() const { return !acceptExpected; }
+ bool isEnded() const { return ended; }
+ bool isWindowing() const { return windowing; }
+
+ uint32_t getCredit() const;
+ const std::string& getTag() const { return tag; }
+
+ void deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize);
+ void setId(DeliveryId _id) { id = _id; }
+
+ typedef std::deque<DeliveryRecord> DeliveryRecords;
+ static AckRange findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last);
+ const QueuedMessage& getMessage() const { return msg; }
+ framing::SequenceNumber getId() const { return id; }
+ boost::shared_ptr<Queue> getQueue() const { return queue; }
+
+ friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&);
+};
+
+inline bool operator<(const DeliveryRecord& a, const DeliveryRecord& b) { return a.getId() < b.getId(); }
+inline bool operator<(const framing::SequenceNumber& a, const DeliveryRecord& b) { return a < b.getId(); }
+inline bool operator<(const DeliveryRecord& a, const framing::SequenceNumber& b) { return a.getId() < b; }
+
+struct AcquireFunctor
+{
+ DeliveryIds& results;
+
+ AcquireFunctor(DeliveryIds& _results) : results(_results) {}
+
+ void operator()(DeliveryRecord& record)
+ {
+ record.acquire(results);
+ }
+};
+
+typedef DeliveryRecord::DeliveryRecords DeliveryRecords;
+
+struct AckRange
+{
+ DeliveryRecords::iterator start;
+ DeliveryRecords::iterator end;
+ AckRange(DeliveryRecords::iterator _start, DeliveryRecords::iterator _end) : start(_start), end(_end) {}
+};
+
+}
+}
+
+
+#endif /*!QPID_BROKER_DELIVERYRECORD_H*/
diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
new file mode 100644
index 0000000000..060f80f60d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/log/Statement.h"
+#include "qpid/broker/FedOps.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/DirectExchange.h"
+#include <iostream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using qpid::management::Manageable;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace
+{
+ const std::string qpidExclusiveBinding("qpid.exclusive-binding");
+}
+
+DirectExchange::DirectExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type(typeName);
+}
+
+DirectExchange::DirectExchange(const string& _name, bool _durable,
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type(typeName);
+}
+
+bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args)
+{
+ string fedOp(fedOpBind);
+ string fedTags;
+ string fedOrigin;
+ bool exclusiveBinding = false;
+ if (args) {
+ fedOp = args->getAsString(qpidFedOp);
+ fedTags = args->getAsString(qpidFedTags);
+ fedOrigin = args->getAsString(qpidFedOrigin);
+ exclusiveBinding = args->get(qpidExclusiveBinding); // only direct exchanges take exclusive bindings
+ }
+
+ bool propagate = false;
+
+ if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
+ Mutex::ScopedLock l(lock);
+ Binding::shared_ptr b(new Binding(routingKey, queue, this, FieldTable(), fedOrigin));
+ BoundKey& bk = bindings[routingKey];
+ if (exclusiveBinding) bk.queues.clear();
+
+ QPID_LOG(debug, "Bind key [" << routingKey << "] to queue " << queue->getName()
+ << " (origin=" << fedOrigin << ")");
+
+ if (bk.queues.add_unless(b, MatchQueue(queue))) {
+ b->startManagement();
+ propagate = bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ // queue already present - still need to track fedOrigin
+ bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
+ return false;
+ }
+ } else if (fedOp == fedOpUnbind) {
+ Mutex::ScopedLock l(lock);
+ BoundKey& bk = bindings[routingKey];
+
+ QPID_LOG(debug, "Bind - fedOpUnbind key [" << routingKey << "] queue " << queue->getName()
+ << " (origin=" << fedOrigin << ")" << " (count=" << bk.fedBinding.count() << ")");
+
+ propagate = bk.fedBinding.delOrigin(queue->getName(), fedOrigin);
+ if (bk.fedBinding.countFedBindings(queue->getName()) == 0)
+ unbind(queue, routingKey, args);
+
+ } else if (fedOp == fedOpReorigin) {
+ /** gather up all the keys that need rebinding in a local vector
+ * while holding the lock. Then propagate once the lock is
+ * released
+ */
+ std::vector<std::string> keys2prop;
+ {
+ Mutex::ScopedLock l(lock);
+ for (Bindings::iterator iter = bindings.begin();
+ iter != bindings.end(); iter++) {
+ const BoundKey& bk = iter->second;
+ if (bk.fedBinding.hasLocal()) {
+ keys2prop.push_back(iter->first);
+ }
+ }
+ } /* lock dropped */
+ for (std::vector<std::string>::const_iterator key = keys2prop.begin();
+ key != keys2prop.end(); key++) {
+ propagateFedOp( *key, string(), fedOpBind, string());
+ }
+ }
+
+ routeIVE();
+ if (propagate)
+ propagateFedOp(routingKey, fedTags, fedOp, fedOrigin);
+ return true;
+}
+
+bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args)
+{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ bool propagate = false;
+
+ QPID_LOG(debug, "Unbinding key [" << routingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName() << " origin=" << fedOrigin << ")" );
+ {
+ Mutex::ScopedLock l(lock);
+ BoundKey& bk = bindings[routingKey];
+ if (bk.queues.remove_if(MatchQueue(queue))) {
+ propagate = bk.fedBinding.delOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // If I delete my local binding, propagate this unbind to any upstream brokers
+ if (propagate)
+ propagateFedOp(routingKey, string(), fedOpUnbind, string());
+ return true;
+}
+
+void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/)
+{
+ PreRoute pr(msg, this);
+ ConstBindingList b;
+ {
+ Mutex::ScopedLock l(lock);
+ b = bindings[routingKey].queues.snapshot();
+ }
+ doRoute(msg, b);
+}
+
+
+bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const)
+{
+ Mutex::ScopedLock l(lock);
+ if (routingKey) {
+ Bindings::iterator i = bindings.find(*routingKey);
+
+ if (i == bindings.end())
+ return false;
+ if (!queue)
+ return true;
+
+ Queues::ConstPtr p = i->second.queues.snapshot();
+ return p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end();
+ } else if (!queue) {
+ //if no queue or routing key is specified, just report whether any bindings exist
+ return bindings.size() > 0;
+ } else {
+ for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) {
+ Queues::ConstPtr p = i->second.queues.snapshot();
+ if (p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end()) return true;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+DirectExchange::~DirectExchange() {}
+
+const std::string DirectExchange::typeName("direct");
diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.h b/qpid/cpp/src/qpid/broker/DirectExchange.h
new file mode 100644
index 0000000000..a6f9cf91af
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DirectExchange.h
@@ -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.
+ *
+ */
+#ifndef _DirectExchange_
+#define _DirectExchange_
+
+#include <map>
+#include <vector>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace broker {
+class DirectExchange : public virtual Exchange {
+ typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Queues;
+ struct BoundKey {
+ Queues queues;
+ FedBinding fedBinding;
+ };
+ typedef std::map<std::string, BoundKey> Bindings;
+ Bindings bindings;
+ qpid::sys::Mutex lock;
+
+public:
+ static const std::string typeName;
+
+ QPID_BROKER_EXTERN DirectExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN DirectExchange(const std::string& _name,
+ bool _durable,
+ const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ QPID_BROKER_EXTERN virtual bool bind(boost::shared_ptr<Queue> queue,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+ virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual bool isBound(boost::shared_ptr<Queue> queue,
+ const std::string* const routingKey,
+ const qpid::framing::FieldTable* const args);
+
+ QPID_BROKER_EXTERN virtual ~DirectExchange();
+
+ virtual bool supportsDynamicBinding() { return true; }
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DtxAck.cpp b/qpid/cpp/src/qpid/broker/DtxAck.cpp
new file mode 100644
index 0000000000..bca3f90bbe
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxAck.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxAck.h"
+#include "qpid/log/Statement.h"
+
+using std::bind1st;
+using std::bind2nd;
+using std::mem_fun_ref;
+using namespace qpid::broker;
+
+DtxAck::DtxAck(const qpid::framing::SequenceSet& acked, DeliveryRecords& unacked)
+{
+ remove_copy_if(unacked.begin(), unacked.end(), inserter(pending, pending.end()),
+ not1(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked)));
+}
+
+bool DtxAck::prepare(TransactionContext* ctxt) throw()
+{
+ try{
+ //record dequeue in the store
+ for (DeliveryRecords::iterator i = pending.begin(); i != pending.end(); i++) {
+ i->dequeue(ctxt);
+ }
+ return true;
+ }catch(...){
+ QPID_LOG(error, "Failed to prepare");
+ return false;
+ }
+}
+
+void DtxAck::commit() throw()
+{
+ try {
+ for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::committed));
+ pending.clear();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to commit: " << e.what());
+ } catch(...) {
+ QPID_LOG(error, "Failed to commit (unknown error)");
+ }
+
+}
+
+void DtxAck::rollback() throw()
+{
+ try {
+ for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue));
+ pending.clear();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to complete rollback: " << e.what());
+ } catch(...) {
+ QPID_LOG(error, "Failed to complete rollback (unknown error)");
+ }
+
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxAck.h b/qpid/cpp/src/qpid/broker/DtxAck.h
new file mode 100644
index 0000000000..166147e58d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxAck.h
@@ -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.
+ *
+ */
+#ifndef _DtxAck_
+#define _DtxAck_
+
+#include <algorithm>
+#include <functional>
+#include <list>
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/TxOp.h"
+
+namespace qpid {
+ namespace broker {
+ class DtxAck : public TxOp{
+ DeliveryRecords pending;
+
+ public:
+ DtxAck(const framing::SequenceSet& acked, DeliveryRecords& unacked);
+ virtual bool prepare(TransactionContext* ctxt) throw();
+ virtual void commit() throw();
+ virtual void rollback() throw();
+ virtual ~DtxAck(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DtxBuffer.cpp b/qpid/cpp/src/qpid/broker/DtxBuffer.cpp
new file mode 100644
index 0000000000..f1b8169cf7
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxBuffer.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxBuffer.h"
+
+using namespace qpid::broker;
+using qpid::sys::Mutex;
+
+DtxBuffer::DtxBuffer(const std::string& _xid)
+ : xid(_xid), ended(false), suspended(false), failed(false), expired(false) {}
+
+DtxBuffer::~DtxBuffer() {}
+
+void DtxBuffer::markEnded()
+{
+ Mutex::ScopedLock locker(lock);
+ ended = true;
+}
+
+bool DtxBuffer::isEnded()
+{
+ Mutex::ScopedLock locker(lock);
+ return ended;
+}
+
+void DtxBuffer::setSuspended(bool isSuspended)
+{
+ suspended = isSuspended;
+}
+
+bool DtxBuffer::isSuspended()
+{
+ return suspended;
+}
+
+void DtxBuffer::fail()
+{
+ Mutex::ScopedLock locker(lock);
+ rollback();
+ failed = true;
+ ended = true;
+}
+
+bool DtxBuffer::isRollbackOnly()
+{
+ Mutex::ScopedLock locker(lock);
+ return failed;
+}
+
+const std::string& DtxBuffer::getXid()
+{
+ return xid;
+}
+
+void DtxBuffer::timedout()
+{
+ Mutex::ScopedLock locker(lock);
+ expired = true;
+ fail();
+}
+
+bool DtxBuffer::isExpired()
+{
+ Mutex::ScopedLock locker(lock);
+ return expired;
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxBuffer.h b/qpid/cpp/src/qpid/broker/DtxBuffer.h
new file mode 100644
index 0000000000..1511cb032f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxBuffer.h
@@ -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.
+ *
+ */
+#ifndef _DtxBuffer_
+#define _DtxBuffer_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/TxBuffer.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+ namespace broker {
+ class DtxBuffer : public TxBuffer{
+ sys::Mutex lock;
+ const std::string xid;
+ bool ended;
+ bool suspended;
+ bool failed;
+ bool expired;
+
+ public:
+ typedef boost::shared_ptr<DtxBuffer> shared_ptr;
+
+ QPID_BROKER_EXTERN DtxBuffer(const std::string& xid = "");
+ QPID_BROKER_EXTERN ~DtxBuffer();
+ QPID_BROKER_EXTERN void markEnded();
+ bool isEnded();
+ void setSuspended(bool suspended);
+ bool isSuspended();
+ void fail();
+ bool isRollbackOnly();
+ void timedout();
+ bool isExpired();
+ const std::string& getXid();
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DtxManager.cpp b/qpid/cpp/src/qpid/broker/DtxManager.cpp
new file mode 100644
index 0000000000..3caa41c3f4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxManager.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxManager.h"
+#include "qpid/broker/DtxTimeout.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/ptr_map.h"
+
+#include <boost/format.hpp>
+#include <iostream>
+
+using boost::intrusive_ptr;
+using qpid::sys::Mutex;
+using qpid::ptr_map_ptr;
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(t) {}
+
+DtxManager::~DtxManager() {}
+
+void DtxManager::start(const std::string& xid, DtxBuffer::shared_ptr ops)
+{
+ createWork(xid)->add(ops);
+}
+
+void DtxManager::join(const std::string& xid, DtxBuffer::shared_ptr ops)
+{
+ getWork(xid)->add(ops);
+}
+
+void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops)
+{
+ createWork(xid)->recover(txn, ops);
+}
+
+bool DtxManager::prepare(const std::string& xid)
+{
+ QPID_LOG(debug, "preparing: " << xid);
+ try {
+ return getWork(xid)->prepare();
+ } catch (DtxTimeoutException& e) {
+ remove(xid);
+ throw e;
+ }
+}
+
+bool DtxManager::commit(const std::string& xid, bool onePhase)
+{
+ QPID_LOG(debug, "committing: " << xid);
+ try {
+ bool result = getWork(xid)->commit(onePhase);
+ remove(xid);
+ return result;
+ } catch (DtxTimeoutException& e) {
+ remove(xid);
+ throw e;
+ }
+}
+
+void DtxManager::rollback(const std::string& xid)
+{
+ QPID_LOG(debug, "rolling back: " << xid);
+ try {
+ getWork(xid)->rollback();
+ remove(xid);
+ } catch (DtxTimeoutException& e) {
+ remove(xid);
+ throw e;
+ }
+}
+
+DtxWorkRecord* DtxManager::getWork(const std::string& xid)
+{
+ Mutex::ScopedLock locker(lock);
+ WorkMap::iterator i = work.find(xid);
+ if (i == work.end()) {
+ throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
+ }
+ return ptr_map_ptr(i);
+}
+
+void DtxManager::remove(const std::string& xid)
+{
+ Mutex::ScopedLock locker(lock);
+ WorkMap::iterator i = work.find(xid);
+ if (i == work.end()) {
+ throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
+ } else {
+ work.erase(i);
+ }
+}
+
+DtxWorkRecord* DtxManager::createWork(std::string xid)
+{
+ Mutex::ScopedLock locker(lock);
+ WorkMap::iterator i = work.find(xid);
+ if (i != work.end()) {
+ throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)"));
+ } else {
+ return ptr_map_ptr(work.insert(xid, new DtxWorkRecord(xid, store)).first);
+ }
+}
+
+void DtxManager::setTimeout(const std::string& xid, uint32_t secs)
+{
+ DtxWorkRecord* record = getWork(xid);
+ intrusive_ptr<DtxTimeout> timeout = record->getTimeout();
+ if (timeout.get()) {
+ if (timeout->timeout == secs) return;//no need to do anything further if timeout hasn't changed
+ timeout->cancel();
+ }
+ timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid));
+ record->setTimeout(timeout);
+ timer.add(timeout);
+}
+
+uint32_t DtxManager::getTimeout(const std::string& xid)
+{
+ intrusive_ptr<DtxTimeout> timeout = getWork(xid)->getTimeout();
+ return !timeout ? 0 : timeout->timeout;
+}
+
+void DtxManager::timedout(const std::string& xid)
+{
+ Mutex::ScopedLock locker(lock);
+ WorkMap::iterator i = work.find(xid);
+ if (i == work.end()) {
+ QPID_LOG(warning, "Transaction timeout failed: no record for xid");
+ } else {
+ ptr_map_ptr(i)->timedout();
+ //TODO: do we want to have a timed task to cleanup, or can we rely on an explicit completion?
+ //timer.add(intrusive_ptr<TimerTask>(new DtxCleanup(60*30/*30 mins*/, *this, xid)));
+ }
+}
+
+DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
+ : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), mgr(_mgr), xid(_xid) {}
+
+void DtxManager::DtxCleanup::fire()
+{
+ try {
+ mgr.remove(xid);
+ } catch (ConnectionException& /*e*/) {
+ //assume it was explicitly cleaned up after a call to prepare, commit or rollback
+ }
+}
+
+void DtxManager::setStore (TransactionalStore* _store)
+{
+ store = _store;
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxManager.h b/qpid/cpp/src/qpid/broker/DtxManager.h
new file mode 100644
index 0000000000..680b62eeb2
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxManager.h
@@ -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.
+ *
+ */
+#ifndef _DtxManager_
+#define _DtxManager_
+
+#include <boost/ptr_container/ptr_map.hpp>
+#include "qpid/broker/DtxBuffer.h"
+#include "qpid/broker/DtxWorkRecord.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace broker {
+
+class DtxManager{
+ typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap;
+
+ struct DtxCleanup : public sys::TimerTask
+ {
+ DtxManager& mgr;
+ const std::string& xid;
+
+ DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid);
+ void fire();
+ };
+
+ WorkMap work;
+ TransactionalStore* store;
+ qpid::sys::Mutex lock;
+ qpid::sys::Timer& timer;
+
+ void remove(const std::string& xid);
+ DtxWorkRecord* getWork(const std::string& xid);
+ DtxWorkRecord* createWork(std::string xid);
+
+public:
+ DtxManager(qpid::sys::Timer&);
+ ~DtxManager();
+ void start(const std::string& xid, DtxBuffer::shared_ptr work);
+ void join(const std::string& xid, DtxBuffer::shared_ptr work);
+ void recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr work);
+ bool prepare(const std::string& xid);
+ bool commit(const std::string& xid, bool onePhase);
+ void rollback(const std::string& xid);
+ void setTimeout(const std::string& xid, uint32_t secs);
+ uint32_t getTimeout(const std::string& xid);
+ void timedout(const std::string& xid);
+ void setStore(TransactionalStore* store);
+};
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DtxTimeout.cpp b/qpid/cpp/src/qpid/broker/DtxTimeout.cpp
new file mode 100644
index 0000000000..c4c52ec40a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxTimeout.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxTimeout.h"
+#include "qpid/broker/DtxManager.h"
+#include "qpid/sys/Time.h"
+
+using namespace qpid::broker;
+
+DtxTimeout::DtxTimeout(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
+ : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxTimeout"), timeout(_timeout), mgr(_mgr), xid(_xid)
+{
+}
+
+void DtxTimeout::fire()
+{
+ mgr.timedout(xid);
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxTimeout.h b/qpid/cpp/src/qpid/broker/DtxTimeout.h
new file mode 100644
index 0000000000..680a210e4f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxTimeout.h
@@ -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.
+ *
+ */
+#ifndef _DtxTimeout_
+#define _DtxTimeout_
+
+#include "qpid/Exception.h"
+#include "qpid/sys/Timer.h"
+
+namespace qpid {
+namespace broker {
+
+class DtxManager;
+
+struct DtxTimeoutException : public Exception {};
+
+struct DtxTimeout : public sys::TimerTask
+{
+ const uint32_t timeout;
+ DtxManager& mgr;
+ const std::string xid;
+
+ DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid);
+ void fire();
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp
new file mode 100644
index 0000000000..9f33e698db
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxWorkRecord.h"
+#include "qpid/framing/reply_exceptions.h"
+#include <boost/format.hpp>
+#include <boost/mem_fn.hpp>
+using boost::mem_fn;
+using qpid::sys::Mutex;
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const _store) :
+ xid(_xid), store(_store), completed(false), rolledback(false), prepared(false), expired(false) {}
+
+DtxWorkRecord::~DtxWorkRecord()
+{
+ if (timeout.get()) {
+ timeout->cancel();
+ }
+}
+
+bool DtxWorkRecord::prepare()
+{
+ Mutex::ScopedLock locker(lock);
+ if (check()) {
+ txn = store->begin(xid);
+ if (prepare(txn.get())) {
+ store->prepare(*txn);
+ prepared = true;
+ } else {
+ abort();
+ //TODO: this should probably be flagged as internal error
+ }
+ } else {
+ //some part of the work has been marked rollback only
+ abort();
+ }
+ return prepared;
+}
+
+bool DtxWorkRecord::prepare(TransactionContext* _txn)
+{
+ bool succeeded(true);
+ for (Work::iterator i = work.begin(); succeeded && i != work.end(); i++) {
+ succeeded = (*i)->prepare(_txn);
+ }
+ return succeeded;
+}
+
+bool DtxWorkRecord::commit(bool onePhase)
+{
+ Mutex::ScopedLock locker(lock);
+ if (check()) {
+ if (prepared) {
+ //already prepared i.e. 2pc
+ if (onePhase) {
+ throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!"));
+ }
+
+ store->commit(*txn);
+ txn.reset();
+
+ std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit));
+ return true;
+ } else {
+ //1pc commit optimisation, don't need a 2pc transaction context:
+ if (!onePhase) {
+ throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!"));
+ }
+ std::auto_ptr<TransactionContext> localtxn = store->begin();
+ if (prepare(localtxn.get())) {
+ store->commit(*localtxn);
+ std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit));
+ return true;
+ } else {
+ store->abort(*localtxn);
+ abort();
+ //TODO: this should probably be flagged as internal error
+ return false;
+ }
+ }
+ } else {
+ //some part of the work has been marked rollback only
+ abort();
+ return false;
+ }
+}
+
+void DtxWorkRecord::rollback()
+{
+ Mutex::ScopedLock locker(lock);
+ check();
+ abort();
+}
+
+void DtxWorkRecord::add(DtxBuffer::shared_ptr ops)
+{
+ Mutex::ScopedLock locker(lock);
+ if (expired) {
+ throw DtxTimeoutException();
+ }
+ if (completed) {
+ throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!"));
+ }
+ work.push_back(ops);
+}
+
+bool DtxWorkRecord::check()
+{
+ if (expired) {
+ throw DtxTimeoutException();
+ }
+ if (!completed) {
+ //iterate through all DtxBuffers and ensure they are all ended
+ for (Work::iterator i = work.begin(); i != work.end(); i++) {
+ if (!(*i)->isEnded()) {
+ throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " not completed!"));
+ } else if ((*i)->isRollbackOnly()) {
+ rolledback = true;
+ }
+ }
+ completed = true;
+ }
+ return !rolledback;
+}
+
+void DtxWorkRecord::abort()
+{
+ if (txn.get()) {
+ store->abort(*txn);
+ txn.reset();
+ }
+ std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::rollback));
+}
+
+void DtxWorkRecord::recover(std::auto_ptr<TPCTransactionContext> _txn, DtxBuffer::shared_ptr ops)
+{
+ add(ops);
+ txn = _txn;
+ ops->markEnded();
+ completed = true;
+ prepared = true;
+}
+
+void DtxWorkRecord::timedout()
+{
+ Mutex::ScopedLock locker(lock);
+ expired = true;
+ rolledback = true;
+ if (!completed) {
+ for (Work::iterator i = work.begin(); i != work.end(); i++) {
+ if (!(*i)->isEnded()) {
+ (*i)->timedout();
+ }
+ }
+ }
+ abort();
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxWorkRecord.h b/qpid/cpp/src/qpid/broker/DtxWorkRecord.h
new file mode 100644
index 0000000000..aec2d2aed4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/DtxWorkRecord.h
@@ -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.
+ *
+ */
+#ifndef _DtxWorkRecord_
+#define _DtxWorkRecord_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/DtxBuffer.h"
+#include "qpid/broker/DtxTimeout.h"
+#include "qpid/broker/TransactionalStore.h"
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Mutex.h"
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Represents the work done under a particular distributed transaction
+ * across potentially multiple channels. Identified by a xid. Allows
+ * that work to be prepared, committed and rolled-back.
+ */
+class DtxWorkRecord
+{
+ typedef std::vector<DtxBuffer::shared_ptr> Work;
+
+ const std::string xid;
+ TransactionalStore* const store;
+ bool completed;
+ bool rolledback;
+ bool prepared;
+ bool expired;
+ boost::intrusive_ptr<DtxTimeout> timeout;
+ Work work;
+ std::auto_ptr<TPCTransactionContext> txn;
+ qpid::sys::Mutex lock;
+
+ bool check();
+ void abort();
+ bool prepare(TransactionContext* txn);
+public:
+ QPID_BROKER_EXTERN DtxWorkRecord(const std::string& xid,
+ TransactionalStore* const store);
+ QPID_BROKER_EXTERN ~DtxWorkRecord();
+ QPID_BROKER_EXTERN bool prepare();
+ QPID_BROKER_EXTERN bool commit(bool onePhase);
+ QPID_BROKER_EXTERN void rollback();
+ QPID_BROKER_EXTERN void add(DtxBuffer::shared_ptr ops);
+ void recover(std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops);
+ void timedout();
+ void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; }
+ boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; }
+};
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp
new file mode 100644
index 0000000000..622cc81002
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Exchange.cpp
@@ -0,0 +1,403 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/FedOps.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/sys/ExceptionHolder.h"
+#include <stdexcept>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using qpid::framing::Buffer;
+using qpid::framing::FieldTable;
+using qpid::sys::Mutex;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace
+{
+ const std::string qpidMsgSequence("qpid.msg_sequence");
+ const std::string qpidSequenceCounter("qpid.sequence_counter");
+ const std::string qpidIVE("qpid.ive");
+ const std::string QPID_MANAGEMENT("qpid.management");
+}
+
+
+Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) {
+ if (parent){
+ if (parent->sequence || parent->ive) parent->sequenceLock.lock();
+
+ if (parent->sequence){
+ parent->sequenceNo++;
+ msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo);
+ }
+ if (parent->ive) {
+ parent->lastMsg = &( msg.getMessage());
+ }
+ }
+}
+
+Exchange::PreRoute::~PreRoute(){
+ if (parent && (parent->sequence || parent->ive)){
+ parent->sequenceLock.unlock();
+ }
+}
+
+namespace {
+/** Store information about an exception to be thrown later.
+ * If multiple exceptions are stored, save the first of the "most severe"
+ * exceptions, SESSION is les sever than CONNECTION etc.
+ */
+class ExInfo {
+ public:
+ enum Type { NONE, SESSION, CONNECTION, OTHER };
+
+ ExInfo(string exchange) : type(NONE), exchange(exchange) {}
+ void store(Type type_, const qpid::sys::ExceptionHolder& exception_, const boost::shared_ptr<Queue>& queue) {
+ QPID_LOG(warning, "Exchange " << exchange << " cannot deliver to queue "
+ << queue->getName() << ": " << exception_.what());
+ if (type < type_) { // Replace less severe exception
+ type = type_;
+ exception = exception_;
+ }
+ }
+
+ void raise() {
+ exception.raise();
+ }
+
+ private:
+ Type type;
+ string exchange;
+ qpid::sys::ExceptionHolder exception;
+};
+}
+
+void Exchange::doRoute(Deliverable& msg, ConstBindingList b)
+{
+ int count = 0;
+
+ if (b.get()) {
+ // Block the content release if the message is transient AND there is more than one binding
+ if (!msg.getMessage().isPersistent() && b->size() > 1) {
+ msg.getMessage().blockContentRelease();
+ }
+
+
+ ExInfo error(getName()); // Save exception to throw at the end.
+ for(std::vector<Binding::shared_ptr>::const_iterator i = b->begin(); i != b->end(); i++, count++) {
+ try {
+ msg.deliverTo((*i)->queue);
+ if ((*i)->mgmtBinding != 0)
+ (*i)->mgmtBinding->inc_msgMatched();
+ }
+ catch (const SessionException& e) {
+ error.store(ExInfo::SESSION, framing::createSessionException(e.code, e.what()),(*i)->queue);
+ }
+ catch (const ConnectionException& e) {
+ error.store(ExInfo::CONNECTION, framing::createConnectionException(e.code, e.what()), (*i)->queue);
+ }
+ catch (const std::exception& e) {
+ error.store(ExInfo::OTHER, qpid::sys::ExceptionHolder(new Exception(e.what())), (*i)->queue);
+ }
+ }
+ error.raise();
+ }
+
+ if (mgmtExchange != 0)
+ {
+ mgmtExchange->inc_msgReceives ();
+ mgmtExchange->inc_byteReceives (msg.contentSize ());
+ if (count == 0)
+ {
+ //QPID_LOG(warning, "Exchange " << getName() << " could not route message; no matching binding found");
+ mgmtExchange->inc_msgDrops ();
+ mgmtExchange->inc_byteDrops (msg.contentSize ());
+ }
+ else
+ {
+ mgmtExchange->inc_msgRoutes (count);
+ mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
+ }
+ }
+}
+
+void Exchange::routeIVE(){
+ if (ive && lastMsg.get()){
+ DeliverableMessage dmsg(lastMsg);
+ route(dmsg, lastMsg->getRoutingKey(), lastMsg->getApplicationHeaders());
+ }
+}
+
+
+Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
+ name(_name), durable(false), persistenceId(0), sequence(false),
+ sequenceNo(0), ive(false), mgmtExchange(0), broker(b), destroyed(false)
+{
+ if (parent != 0 && broker != 0)
+ {
+ ManagementAgent* agent = broker->getManagementAgent();
+ if (agent != 0)
+ {
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
+ agent->addObject(mgmtExchange, 0, durable);
+ }
+ }
+}
+
+Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args,
+ Manageable* parent, Broker* b)
+ : name(_name), durable(_durable), alternateUsers(0), persistenceId(0),
+ args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b), destroyed(false)
+{
+ if (parent != 0 && broker != 0)
+ {
+ ManagementAgent* agent = broker->getManagementAgent();
+ if (agent != 0)
+ {
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
+ mgmtExchange->set_arguments(ManagementAgent::toMap(args));
+ agent->addObject(mgmtExchange, 0, durable);
+ }
+ }
+
+ sequence = _args.get(qpidMsgSequence);
+ if (sequence) {
+ QPID_LOG(debug, "Configured exchange " << _name << " with Msg sequencing");
+ args.setInt64(std::string(qpidSequenceCounter), sequenceNo);
+ }
+
+ ive = _args.get(qpidIVE);
+ if (ive) {
+ if (broker && broker->isInCluster())
+ throw framing::NotImplementedException("Cannot use Initial Value Exchanges in a cluster");
+ QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value");
+ }
+}
+
+Exchange::~Exchange ()
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->resourceDestroy ();
+}
+
+void Exchange::setAlternate(Exchange::shared_ptr _alternate)
+{
+ alternate = _alternate;
+ if (mgmtExchange != 0) {
+ if (alternate.get() != 0)
+ mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId());
+ else
+ mgmtExchange->clr_altExchange();
+ }
+}
+
+void Exchange::setPersistenceId(uint64_t id) const
+{
+ persistenceId = id;
+}
+
+Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffer)
+{
+ string name;
+ string type;
+ string altName;
+ FieldTable args;
+
+ buffer.getShortString(name);
+ bool durable(buffer.getOctet());
+ buffer.getShortString(type);
+ buffer.get(args);
+ // For backwards compatibility on restoring exchanges from before the alt-exchange update, perform check
+ if (buffer.available())
+ buffer.getShortString(altName);
+
+ try {
+ Exchange::shared_ptr exch = exchanges.declare(name, type, durable, args).first;
+ exch->sequenceNo = args.getAsInt64(qpidSequenceCounter);
+ exch->alternateName.assign(altName);
+ return exch;
+ } catch (const UnknownExchangeTypeException&) {
+ QPID_LOG(warning, "Could not create exchange " << name << "; type " << type << " is not recognised");
+ return Exchange::shared_ptr();
+ }
+}
+
+void Exchange::encode(Buffer& buffer) const
+{
+ buffer.putShortString(name);
+ buffer.putOctet(durable);
+ buffer.putShortString(getType());
+ if (args.isSet(qpidSequenceCounter))
+ args.setInt64(std::string(qpidSequenceCounter),sequenceNo);
+ buffer.put(args);
+ buffer.putShortString(alternate.get() ? alternate->getName() : string(""));
+}
+
+uint32_t Exchange::encodedSize() const
+{
+ return name.size() + 1/*short string size*/
+ + 1 /*durable*/
+ + getType().size() + 1/*short string size*/
+ + (alternate.get() ? alternate->getName().size() : 0) + 1/*short string size*/
+ + args.encodedSize();
+}
+
+void Exchange::recoveryComplete(ExchangeRegistry& exchanges)
+{
+ if (!alternateName.empty()) {
+ try {
+ Exchange::shared_ptr ae = exchanges.get(alternateName);
+ setAlternate(ae);
+ } catch (const NotFoundException&) {
+ QPID_LOG(warning, "Could not set alternate exchange \"" << alternateName << "\": does not exist.");
+ }
+ }
+}
+
+ManagementObject* Exchange::GetManagementObject (void) const
+{
+ return (ManagementObject*) mgmtExchange;
+}
+
+void Exchange::registerDynamicBridge(DynamicBridge* db)
+{
+ if (!supportsDynamicBinding())
+ throw Exception("Exchange type does not support dynamic binding");
+
+ {
+ Mutex::ScopedLock l(bridgeLock);
+ for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin();
+ iter != bridgeVector.end(); iter++)
+ (*iter)->sendReorigin();
+
+ bridgeVector.push_back(db);
+ }
+
+ FieldTable args;
+ args.setString(qpidFedOp, fedOpReorigin);
+ bind(Queue::shared_ptr(), string(), &args);
+}
+
+void Exchange::removeDynamicBridge(DynamicBridge* db)
+{
+ Mutex::ScopedLock l(bridgeLock);
+ for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin();
+ iter != bridgeVector.end(); iter++)
+ if (*iter == db) {
+ bridgeVector.erase(iter);
+ break;
+ }
+}
+
+void Exchange::handleHelloRequest()
+{
+}
+
+void Exchange::propagateFedOp(const string& routingKey, const string& tags, const string& op, const string& origin, qpid::framing::FieldTable* extra_args)
+{
+ Mutex::ScopedLock l(bridgeLock);
+ string myOp(op.empty() ? fedOpBind : op);
+
+ for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin();
+ iter != bridgeVector.end(); iter++)
+ (*iter)->propagateBinding(routingKey, tags, op, origin, extra_args);
+}
+
+Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* _parent,
+ FieldTable _args, const string& _origin)
+ : parent(_parent), queue(_queue), key(_key), args(_args), origin(_origin), mgmtBinding(0)
+{
+}
+
+Exchange::Binding::~Binding ()
+{
+ if (mgmtBinding != 0) {
+ ManagementObject* mo = queue->GetManagementObject();
+ if (mo != 0)
+ static_cast<_qmf::Queue*>(mo)->dec_bindingCount();
+ mgmtBinding->resourceDestroy ();
+ }
+}
+
+void Exchange::Binding::startManagement()
+{
+ if (parent != 0)
+ {
+ Broker* broker = parent->getBroker();
+ if (broker != 0) {
+ ManagementAgent* agent = broker->getManagementAgent();
+ if (agent != 0) {
+ ManagementObject* mo = queue->GetManagementObject();
+ if (mo != 0) {
+ management::ObjectId queueId = mo->getObjectId();
+
+ mgmtBinding = new _qmf::Binding
+ (agent, this, (Manageable*) parent, queueId, key, ManagementAgent::toMap(args));
+ if (!origin.empty())
+ mgmtBinding->set_origin(origin);
+ agent->addObject(mgmtBinding);
+ static_cast<_qmf::Queue*>(mo)->inc_bindingCount();
+ }
+ }
+ }
+ }
+}
+
+ManagementObject* Exchange::Binding::GetManagementObject () const
+{
+ return (ManagementObject*) mgmtBinding;
+}
+
+Exchange::MatchQueue::MatchQueue(Queue::shared_ptr q) : queue(q) {}
+
+bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b)
+{
+ return b->queue == queue;
+}
+
+void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) {
+ msg->getProperties<DeliveryProperties>()->setExchange(getName());
+}
+
+bool Exchange::routeWithAlternate(Deliverable& msg)
+{
+ route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders());
+ if (!msg.delivered && alternate) {
+ alternate->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders());
+ }
+ return msg.delivered;
+}
diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h
new file mode 100644
index 0000000000..b12af9a1dd
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Exchange.h
@@ -0,0 +1,248 @@
+#ifndef _broker_Exchange_h
+#define _broker_Exchange_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/PersistableExchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Exchange.h"
+#include "qmf/org/apache/qpid/broker/Binding.h"
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+class ExchangeRegistry;
+
+class QPID_BROKER_CLASS_EXTERN Exchange : public PersistableExchange, public management::Manageable {
+public:
+ struct Binding : public management::Manageable {
+ typedef boost::shared_ptr<Binding> shared_ptr;
+ typedef std::vector<Binding::shared_ptr> vector;
+
+ Exchange* parent;
+ boost::shared_ptr<Queue> queue;
+ const std::string key;
+ const framing::FieldTable args;
+ std::string origin;
+ qmf::org::apache::qpid::broker::Binding* mgmtBinding;
+
+ Binding(const std::string& key, boost::shared_ptr<Queue> queue, Exchange* parent = 0,
+ framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string());
+ ~Binding();
+ void startManagement();
+ management::ManagementObject* GetManagementObject() const;
+ };
+
+private:
+ const std::string name;
+ const bool durable;
+ std::string alternateName;
+ boost::shared_ptr<Exchange> alternate;
+ uint32_t alternateUsers;
+ mutable uint64_t persistenceId;
+
+protected:
+ mutable qpid::framing::FieldTable args;
+ bool sequence;
+ mutable qpid::sys::Mutex sequenceLock;
+ int64_t sequenceNo;
+ bool ive;
+ boost::intrusive_ptr<Message> lastMsg;
+
+ class PreRoute{
+ public:
+ PreRoute(Deliverable& msg, Exchange* _p);
+ ~PreRoute();
+ private:
+ Exchange* parent;
+ };
+
+ typedef boost::shared_ptr<const std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > ConstBindingList;
+ typedef boost::shared_ptr< std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > BindingList;
+ void doRoute(Deliverable& msg, ConstBindingList b);
+ void routeIVE();
+
+
+ struct MatchQueue {
+ const boost::shared_ptr<Queue> queue;
+ MatchQueue(boost::shared_ptr<Queue> q);
+ bool operator()(Exchange::Binding::shared_ptr b);
+ };
+
+ /** A FedBinding keeps track of information that Federation needs
+ to know when to propagate changes.
+
+ Dynamic federation needs to know which exchanges have at least
+ one local binding. The bindings on these exchanges need to be
+ propagated.
+
+ Federated binds and unbinds need to know which federation
+ origins are associated with the bindings for each queue. When
+ origins are added or deleted, the corresponding bindings need
+ to be propagated.
+
+ fedBindings[queueName] contains the origins associated with
+ the given queue.
+ */
+
+ class FedBinding {
+ uint32_t localBindings;
+
+ typedef std::set<std::string> originSet;
+ std::map<std::string, originSet> fedBindings;
+
+ public:
+ FedBinding() : localBindings(0) {}
+ bool hasLocal() const { return localBindings != 0; }
+
+ /** Returns true if propagation is needed. */
+ bool addOrigin(const std::string& queueName, const std::string& origin) {
+ if (origin.empty()) {
+ localBindings++;
+ return localBindings == 1;
+ }
+ fedBindings[queueName].insert(origin);
+ return true;
+ }
+
+ /** Returns true if propagation is needed. */
+ bool delOrigin(const std::string& queueName, const std::string& origin){
+ if (origin.empty()) { // no remote == local binding
+ if (localBindings > 0)
+ localBindings--;
+ return localBindings == 0;
+ }
+ size_t match = fedBindings[queueName].erase(origin);
+ if (fedBindings[queueName].empty())
+ fedBindings.erase(queueName);
+ return match != 0;
+ }
+
+ uint32_t count() {
+ return localBindings + fedBindings.size();
+ }
+
+ uint32_t countFedBindings(const std::string& queueName) {
+ // don't use '[]' - it may increase size of fedBindings!
+ std::map<std::string, originSet>::iterator i;
+ if ((i = fedBindings.find(queueName)) != fedBindings.end())
+ return i->second.size();
+ return 0;
+ }
+ };
+
+ qmf::org::apache::qpid::broker::Exchange* mgmtExchange;
+
+public:
+ typedef boost::shared_ptr<Exchange> shared_ptr;
+
+ QPID_BROKER_EXTERN explicit Exchange(const std::string& name, management::Manageable* parent = 0,
+ Broker* broker = 0);
+ QPID_BROKER_EXTERN Exchange(const std::string& _name, bool _durable, const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_INLINE_EXTERN virtual ~Exchange();
+
+ const std::string& getName() const { return name; }
+ bool isDurable() { return durable; }
+ qpid::framing::FieldTable& getArgs() { return args; }
+
+ Exchange::shared_ptr getAlternate() { return alternate; }
+ void setAlternate(Exchange::shared_ptr _alternate);
+ void incAlternateUsers() { alternateUsers++; }
+ void decAlternateUsers() { alternateUsers--; }
+ bool inUseAsAlternate() { return alternateUsers > 0; }
+
+ virtual std::string getType() const = 0;
+
+ /**
+ * bind() is used for two distinct purposes:
+ *
+ * 1. To create a binding, in the conventional sense
+ *
+ * 2. As a vehicle for any FedOp, currently including federated
+ * binding, federated unbinding, federated reorigin.
+ *
+ */
+
+ virtual bool bind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
+ virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
+ virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args) = 0;
+ QPID_BROKER_EXTERN virtual void setProperties(const boost::intrusive_ptr<Message>&);
+ virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
+
+ //PersistableExchange:
+ QPID_BROKER_EXTERN void setPersistenceId(uint64_t id) const;
+ uint64_t getPersistenceId() const { return persistenceId; }
+ QPID_BROKER_EXTERN uint32_t encodedSize() const;
+ QPID_BROKER_EXTERN virtual void encode(framing::Buffer& buffer) const;
+
+ static QPID_BROKER_EXTERN Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer);
+
+ // Manageable entry points
+ QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject(void) const;
+
+ // Federation hooks
+ class DynamicBridge {
+ public:
+ virtual ~DynamicBridge() {}
+ virtual void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin, qpid::framing::FieldTable* extra_args=0) = 0;
+ virtual void sendReorigin() = 0;
+ virtual bool containsLocalTag(const std::string& tagList) const = 0;
+ virtual const std::string& getLocalTag() const = 0;
+ };
+
+ void registerDynamicBridge(DynamicBridge* db);
+ void removeDynamicBridge(DynamicBridge* db);
+ virtual bool supportsDynamicBinding() { return false; }
+ Broker* getBroker() const { return broker; }
+ /**
+ * Notify exchange that recovery has completed.
+ */
+ void recoveryComplete(ExchangeRegistry& exchanges);
+
+ bool routeWithAlternate(Deliverable& message);
+
+ void destroy() { destroyed = true; }
+ bool isDestroyed() const { return destroyed; }
+
+protected:
+ qpid::sys::Mutex bridgeLock;
+ std::vector<DynamicBridge*> bridgeVector;
+ Broker* broker;
+ bool destroyed;
+
+ QPID_BROKER_EXTERN virtual void handleHelloRequest();
+ void propagateFedOp(const std::string& routingKey, const std::string& tags,
+ const std::string& op, const std::string& origin,
+ qpid::framing::FieldTable* extra_args=0);
+};
+
+}}
+
+#endif /*!_broker_Exchange.cpp_h*/
diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp
new file mode 100644
index 0000000000..1c8d26c4f7
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/HeadersExchange.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/management/ManagementDirectExchange.h"
+#include "qpid/management/ManagementTopicExchange.h"
+#include "qpid/framing/reply_exceptions.h"
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+using std::pair;
+using std::string;
+using qpid::framing::FieldTable;
+
+pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type){
+
+ return declare(name, type, false, FieldTable());
+}
+
+pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type,
+ bool durable, const FieldTable& args){
+ RWlock::ScopedWlock locker(lock);
+ ExchangeMap::iterator i = exchanges.find(name);
+ if (i == exchanges.end()) {
+ Exchange::shared_ptr exchange;
+
+ if (type == TopicExchange::typeName){
+ exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent, broker));
+ }else if(type == DirectExchange::typeName){
+ exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent, broker));
+ }else if(type == FanOutExchange::typeName){
+ exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent, broker));
+ }else if (type == HeadersExchange::typeName) {
+ exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker));
+ }else if (type == ManagementDirectExchange::typeName) {
+ exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker));
+ }else if (type == ManagementTopicExchange::typeName) {
+ exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker));
+ }else{
+ FunctionMap::iterator i = factory.find(type);
+ if (i == factory.end()) {
+ throw UnknownExchangeTypeException();
+ } else {
+ exchange = i->second(name, durable, args, parent, broker);
+ }
+ }
+ exchanges[name] = exchange;
+ return std::pair<Exchange::shared_ptr, bool>(exchange, true);
+ } else {
+ return std::pair<Exchange::shared_ptr, bool>(i->second, false);
+ }
+}
+
+void ExchangeRegistry::destroy(const string& name){
+ if (name.empty() ||
+ (name.find("amq.") == 0 &&
+ (name == "amq.direct" || name == "amq.fanout" || name == "amq.topic" || name == "amq.match")) ||
+ name == "qpid.management")
+ throw framing::NotAllowedException(QPID_MSG("Cannot delete default exchange: '" << name << "'"));
+ RWlock::ScopedWlock locker(lock);
+ ExchangeMap::iterator i = exchanges.find(name);
+ if (i != exchanges.end()) {
+ i->second->destroy();
+ exchanges.erase(i);
+ }
+}
+
+Exchange::shared_ptr ExchangeRegistry::get(const string& name){
+ RWlock::ScopedRlock locker(lock);
+ ExchangeMap::iterator i = exchanges.find(name);
+ if (i == exchanges.end())
+ throw framing::NotFoundException(QPID_MSG("Exchange not found: " << name));
+ return i->second;
+}
+
+bool ExchangeRegistry::registerExchange(const Exchange::shared_ptr& ex) {
+ return exchanges.insert(ExchangeMap::value_type(ex->getName(), ex)).second;
+}
+
+void ExchangeRegistry::registerType(const std::string& type, FactoryFunction f)
+{
+ factory[type] = f;
+}
+
+
+namespace
+{
+const std::string empty;
+}
+
+Exchange::shared_ptr ExchangeRegistry::getDefault()
+{
+ return get(empty);
+}
diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.h b/qpid/cpp/src/qpid/broker/ExchangeRegistry.h
new file mode 100644
index 0000000000..2b75a8f3cf
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.h
@@ -0,0 +1,93 @@
+#ifndef _broker_ExchangeRegistry_h
+#define _broker_ExchangeRegistry_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/management/Manageable.h"
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+#include <algorithm>
+#include <map>
+
+namespace qpid {
+namespace broker {
+
+struct UnknownExchangeTypeException{};
+
+class ExchangeRegistry{
+ public:
+ typedef boost::function5<Exchange::shared_ptr, const std::string&,
+ bool, const qpid::framing::FieldTable&, qpid::management::Manageable*, qpid::broker::Broker*> FactoryFunction;
+
+ ExchangeRegistry (Broker* b = 0) : parent(0), broker(b) {}
+ QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> declare
+ (const std::string& name, const std::string& type);
+ QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> declare
+ (const std::string& name,
+ const std::string& type,
+ bool durable,
+ const qpid::framing::FieldTable& args = framing::FieldTable());
+ QPID_BROKER_EXTERN void destroy(const std::string& name);
+ QPID_BROKER_EXTERN Exchange::shared_ptr get(const std::string& name);
+ Exchange::shared_ptr getDefault();
+
+ /**
+ * Register the manageable parent for declared exchanges
+ */
+ void setParent (management::Manageable* _parent) { parent = _parent; }
+
+ /** Register an exchange instance.
+ *@return true if registered, false if exchange with same name is already registered.
+ */
+ bool registerExchange(const Exchange::shared_ptr&);
+
+ QPID_BROKER_EXTERN void registerType(const std::string& type, FactoryFunction);
+
+ /** Call f for each exchange in the registry. */
+ template <class F> void eachExchange(F f) const {
+ qpid::sys::RWlock::ScopedRlock l(lock);
+ for (ExchangeMap::const_iterator i = exchanges.begin(); i != exchanges.end(); ++i)
+ f(i->second);
+ }
+
+ private:
+ typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap;
+ typedef std::map<std::string, FactoryFunction > FunctionMap;
+
+ ExchangeMap exchanges;
+ FunctionMap factory;
+ mutable qpid::sys::RWlock lock;
+ management::Manageable* parent;
+ Broker* broker;
+};
+
+}} // namespace qpid::broker
+
+
+#endif /*!_broker_ExchangeRegistry_h*/
diff --git a/qpid/cpp/src/qpid/broker/ExpiryPolicy.cpp b/qpid/cpp/src/qpid/broker/ExpiryPolicy.cpp
new file mode 100644
index 0000000000..64a12d918a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ExpiryPolicy.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/broker/Message.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace broker {
+
+ExpiryPolicy::~ExpiryPolicy() {}
+
+void ExpiryPolicy::willExpire(Message&) {}
+
+bool ExpiryPolicy::hasExpired(Message& m) {
+ return m.getExpiration() < sys::AbsTime::now();
+}
+
+void ExpiryPolicy::forget(Message&) {}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/ExpiryPolicy.h b/qpid/cpp/src/qpid/broker/ExpiryPolicy.h
new file mode 100644
index 0000000000..a723eb0aa8
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ExpiryPolicy.h
@@ -0,0 +1,46 @@
+#ifndef QPID_BROKER_EXPIRYPOLICY_H
+#define QPID_BROKER_EXPIRYPOLICY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qpid/broker/BrokerImportExport.h"
+
+namespace qpid {
+namespace broker {
+
+class Message;
+
+/**
+ * Default expiry policy.
+ */
+class QPID_BROKER_CLASS_EXTERN ExpiryPolicy : public RefCounted
+{
+ public:
+ QPID_BROKER_EXTERN virtual ~ExpiryPolicy();
+ QPID_BROKER_EXTERN virtual void willExpire(Message&);
+ QPID_BROKER_EXTERN virtual bool hasExpired(Message&);
+ QPID_BROKER_EXTERN virtual void forget(Message&);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_EXPIRYPOLICY_H*/
diff --git a/qpid/cpp/src/qpid/broker/Fairshare.cpp b/qpid/cpp/src/qpid/broker/Fairshare.cpp
new file mode 100644
index 0000000000..17270ffd8d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Fairshare.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Fairshare.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/log/Statement.h"
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/assign/list_of.hpp>
+
+namespace qpid {
+namespace broker {
+
+Fairshare::Fairshare(size_t levels, uint limit) :
+ PriorityQueue(levels),
+ limits(levels, limit), priority(levels-1), count(0) {}
+
+
+void Fairshare::setLimit(size_t level, uint limit)
+{
+ limits[level] = limit;
+}
+
+bool Fairshare::limitReached()
+{
+ uint l = limits[priority];
+ return l && ++count > l;
+}
+
+uint Fairshare::currentLevel()
+{
+ if (limitReached()) {
+ return nextLevel();
+ } else {
+ return priority;
+ }
+}
+
+uint Fairshare::nextLevel()
+{
+ count = 1;
+ if (priority) --priority;
+ else priority = levels-1;
+ return priority;
+}
+
+bool Fairshare::isNull()
+{
+ for (int i = 0; i < levels; i++) if (limits[i]) return false;
+ return true;
+}
+
+bool Fairshare::getState(uint& p, uint& c) const
+{
+ p = priority;
+ c = count;
+ return true;
+}
+
+bool Fairshare::setState(uint p, uint c)
+{
+ priority = p;
+ count = c;
+ return true;
+}
+
+bool Fairshare::findFrontLevel(uint& p, PriorityLevels& messages)
+{
+ const uint start = p = currentLevel();
+ do {
+ if (!messages[p].empty()) return true;
+ } while ((p = nextLevel()) != start);
+ return false;
+}
+
+
+
+bool Fairshare::getState(const Messages& m, uint& priority, uint& count)
+{
+ const Fairshare* fairshare = dynamic_cast<const Fairshare*>(&m);
+ return fairshare && fairshare->getState(priority, count);
+}
+
+bool Fairshare::setState(Messages& m, uint priority, uint count)
+{
+ Fairshare* fairshare = dynamic_cast<Fairshare*>(&m);
+ return fairshare && fairshare->setState(priority, count);
+}
+
+int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys)
+{
+ qpid::framing::FieldTable::ValuePtr v;
+ std::vector<std::string>::const_iterator i = keys.begin();
+ while (!v && i != keys.end()) {
+ v = settings.get(*i++);
+ }
+
+ if (!v) {
+ return 0;
+ } else if (v->convertsTo<int>()) {
+ return v->get<int>();
+ } else if (v->convertsTo<std::string>()){
+ std::string s = v->get<std::string>();
+ try {
+ return boost::lexical_cast<int>(s);
+ } catch(const boost::bad_lexical_cast&) {
+ QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << s);
+ return 0;
+ }
+ } else {
+ QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << *v);
+ return 0;
+ }
+}
+
+int getIntegerSettingForKey(const qpid::framing::FieldTable& settings, const std::string& key)
+{
+ return getIntegerSetting(settings, boost::assign::list_of<std::string>(key));
+}
+
+int getSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys, int minvalue, int maxvalue)
+{
+ return std::max(minvalue,std::min(getIntegerSetting(settings, keys), maxvalue));
+}
+
+std::auto_ptr<Fairshare> getFairshareForKey(const qpid::framing::FieldTable& settings, uint levels, const std::string& key)
+{
+ uint defaultLimit = getIntegerSettingForKey(settings, key);
+ std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit));
+ for (uint i = 0; i < levels; i++) {
+ std::string levelKey = (boost::format("%1%-%2%") % key % i).str();
+ if(settings.isSet(levelKey)) {
+ fairshare->setLimit(i, getIntegerSettingForKey(settings, levelKey));
+ }
+ }
+ if (!fairshare->isNull()) {
+ return fairshare;
+ } else {
+ return std::auto_ptr<Fairshare>();
+ }
+}
+
+std::auto_ptr<Fairshare> getFairshare(const qpid::framing::FieldTable& settings,
+ uint levels,
+ const std::vector<std::string>& keys)
+{
+ std::auto_ptr<Fairshare> fairshare;
+ for (std::vector<std::string>::const_iterator i = keys.begin(); i != keys.end() && !fairshare.get(); ++i) {
+ fairshare = getFairshareForKey(settings, levels, *i);
+ }
+ return fairshare;
+}
+
+std::auto_ptr<Messages> Fairshare::create(const qpid::framing::FieldTable& settings)
+{
+ using boost::assign::list_of;
+ std::auto_ptr<Messages> result;
+ size_t levels = getSetting(settings, list_of<std::string>("qpid.priorities")("x-qpid-priorities"), 1, 100);
+ if (levels) {
+ std::auto_ptr<Fairshare> fairshare =
+ getFairshare(settings, levels, list_of<std::string>("qpid.fairshare")("x-qpid-fairshare"));
+ if (fairshare.get()) result = fairshare;
+ else result = std::auto_ptr<Messages>(new PriorityQueue(levels));
+ }
+ return result;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Fairshare.h b/qpid/cpp/src/qpid/broker/Fairshare.h
new file mode 100644
index 0000000000..1b25721e0c
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Fairshare.h
@@ -0,0 +1,61 @@
+#ifndef QPID_BROKER_FAIRSHARE_H
+#define QPID_BROKER_FAIRSHARE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/PriorityQueue.h"
+
+namespace qpid {
+namespace framing {
+class FieldTable;
+}
+namespace broker {
+
+/**
+ * Modifies a basic prioirty queue by limiting the number of messages
+ * from each priority level that are dispatched before allowing
+ * dispatch from the next level.
+ */
+class Fairshare : public PriorityQueue
+{
+ public:
+ Fairshare(size_t levels, uint limit);
+ bool getState(uint& priority, uint& count) const;
+ bool setState(uint priority, uint count);
+ void setLimit(size_t level, uint limit);
+ bool isNull();
+ static std::auto_ptr<Messages> create(const qpid::framing::FieldTable& settings);
+ static bool getState(const Messages&, uint& priority, uint& count);
+ static bool setState(Messages&, uint priority, uint count);
+ private:
+ std::vector<uint> limits;
+
+ uint priority;
+ uint count;
+
+ uint currentLevel();
+ uint nextLevel();
+ bool limitReached();
+ bool findFrontLevel(uint& p, PriorityLevels&);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_FAIRSHARE_H*/
diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp
new file mode 100644
index 0000000000..5879fa0892
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/log/Statement.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/FedOps.h"
+#include <algorithm>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+FanOutExchange::FanOutExchange(const std::string& _name, Manageable* _parent, Broker* b) :
+ Exchange(_name, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+FanOutExchange::FanOutExchange(const std::string& _name, bool _durable,
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* args)
+{
+ string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind);
+ string fedTags(args ? args->getAsString(qpidFedTags) : "");
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ bool propagate = false;
+
+ if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
+ Binding::shared_ptr binding (new Binding ("", queue, this, FieldTable(), fedOrigin));
+ if (bindings.add_unless(binding, MatchQueue(queue))) {
+ binding->startManagement();
+ propagate = fedBinding.addOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ // queue already present - still need to track fedOrigin
+ fedBinding.addOrigin(queue->getName(), fedOrigin);
+ return false;
+ }
+ } else if (fedOp == fedOpUnbind) {
+ propagate = fedBinding.delOrigin(queue->getName(), fedOrigin);
+ if (fedBinding.countFedBindings(queue->getName()) == 0)
+ unbind(queue, "", args);
+ } else if (fedOp == fedOpReorigin) {
+ if (fedBinding.hasLocal()) {
+ propagateFedOp(string(), string(), fedOpBind, string());
+ }
+ }
+
+ routeIVE();
+ if (propagate)
+ propagateFedOp(string(), fedTags, fedOp, fedOrigin);
+ return true;
+}
+
+bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* args)
+{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ bool propagate = false;
+
+ QPID_LOG(debug, "Unbinding queue " << queue->getName()
+ << " from exchange " << getName() << " origin=" << fedOrigin << ")" );
+
+ if (bindings.remove_if(MatchQueue(queue))) {
+ propagate = fedBinding.delOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ } else {
+ return false;
+ }
+
+ if (propagate)
+ propagateFedOp(string(), string(), fedOpUnbind, string());
+ return true;
+}
+
+void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/)
+{
+ PreRoute pr(msg, this);
+ doRoute(msg, bindings.snapshot());
+}
+
+bool FanOutExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const)
+{
+ BindingsArray::ConstPtr ptr = bindings.snapshot();
+ return ptr && std::find_if(ptr->begin(), ptr->end(), MatchQueue(queue)) != ptr->end();
+}
+
+
+FanOutExchange::~FanOutExchange() {}
+
+const std::string FanOutExchange::typeName("fanout");
diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.h b/qpid/cpp/src/qpid/broker/FanOutExchange.h
new file mode 100644
index 0000000000..1a7d486796
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/FanOutExchange.h
@@ -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.
+ *
+ */
+#ifndef _FanOutExchange_
+#define _FanOutExchange_
+
+#include <map>
+#include <vector>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/broker/Queue.h"
+
+namespace qpid {
+namespace broker {
+
+class FanOutExchange : public virtual Exchange {
+ typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> BindingsArray;
+ BindingsArray bindings;
+ FedBinding fedBinding;
+ public:
+ static const std::string typeName;
+
+ QPID_BROKER_EXTERN FanOutExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN FanOutExchange(const std::string& _name,
+ bool _durable,
+ const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const std::string* const routingKey,
+ const qpid::framing::FieldTable* const args);
+
+ QPID_BROKER_EXTERN virtual ~FanOutExchange();
+ virtual bool supportsDynamicBinding() { return true; }
+};
+
+}
+}
+
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/FedOps.h b/qpid/cpp/src/qpid/broker/FedOps.h
new file mode 100644
index 0000000000..dc4a38e244
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/FedOps.h
@@ -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.
+ *
+ */
+
+/*
+ * Strings used to identify federated operations and operands.
+ */
+
+namespace
+{
+ const std::string qpidFedOp("qpid.fed.op"); // a federation primitive
+ const std::string qpidFedTags("qpid.fed.tags"); // a unique id for a broker
+ const std::string qpidFedOrigin("qpid.fed.origin"); // the tag of the broker on which a propagated binding originated
+
+ // Operands for qpidFedOp - each identifies a federation primitive
+
+ const std::string fedOpBind("B");
+ const std::string fedOpUnbind("U");
+ const std::string fedOpReorigin("R");
+ const std::string fedOpHello("H");
+}
diff --git a/qpid/cpp/src/qpid/broker/HandlerImpl.h b/qpid/cpp/src/qpid/broker/HandlerImpl.h
new file mode 100644
index 0000000000..aae636e818
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/HandlerImpl.h
@@ -0,0 +1,53 @@
+#ifndef _broker_HandlerImpl_h
+#define _broker_HandlerImpl_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/SemanticState.h"
+#include "qpid/broker/SessionContext.h"
+#include "qpid/broker/ConnectionState.h"
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+/**
+ * Base template for protocol handler implementations.
+ * Provides convenience methods for getting common session objects.
+ */
+class HandlerImpl {
+ protected:
+ SemanticState& state;
+ SessionContext& session;
+
+ HandlerImpl(SemanticState& s) : state(s), session(s.getSession()) {}
+
+ framing::AMQP_ClientProxy& getProxy() { return session.getProxy(); }
+ ConnectionState& getConnection() { return session.getConnection(); }
+ Broker& getBroker() { return session.getConnection().getBroker(); }
+};
+
+}} // namespace qpid::broker
+
+
+
+#endif /*!_broker_HandlerImpl_h*/
+
+
diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp
new file mode 100644
index 0000000000..abcaa5f69d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp
@@ -0,0 +1,341 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/HeadersExchange.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+// TODO aconway 2006-09-20: More efficient matching algorithm.
+// The current search algorithm really sucks.
+// Fieldtables are heavy, maybe use shared_ptr to do handle-body.
+
+using namespace qpid::broker;
+
+namespace {
+ const std::string x_match("x-match");
+ // possible values for x-match
+ const std::string all("all");
+ const std::string any("any");
+ const std::string empty;
+
+ // federation related args and values
+ const std::string qpidFedOp("qpid.fed.op");
+ const std::string qpidFedTags("qpid.fed.tags");
+ const std::string qpidFedOrigin("qpid.fed.origin");
+
+ const std::string fedOpBind("B");
+ const std::string fedOpUnbind("U");
+ const std::string fedOpReorigin("R");
+ const std::string fedOpHello("H");
+}
+
+HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent, Broker* b) :
+ Exchange(_name, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+HeadersExchange::HeadersExchange(const std::string& _name, bool _durable,
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+std::string HeadersExchange::getMatch(const FieldTable* args)
+{
+ if (!args) {
+ throw InternalErrorException(QPID_MSG("No arguments given."));
+ }
+ FieldTable::ValuePtr what = args->get(x_match);
+ if (!what) {
+ return empty;
+ }
+ if (!what->convertsTo<std::string>()) {
+ throw InternalErrorException(QPID_MSG("Invalid x-match binding format to headers exchange. Must be a string [\"all\" or \"any\"]"));
+ }
+ return what->get<std::string>();
+}
+
+bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+{
+ string fedOp(fedOpBind);
+ string fedTags;
+ string fedOrigin;
+ if (args) {
+ fedOp = args->getAsString(qpidFedOp);
+ fedTags = args->getAsString(qpidFedTags);
+ fedOrigin = args->getAsString(qpidFedOrigin);
+ }
+
+ bool propagate = false;
+
+ // The federation args get propagated directly, so we need to identify
+ // the non federation args in case a federated propagate is needed
+ FieldTable extra_args;
+ getNonFedArgs(args, extra_args);
+
+ if (fedOp.empty() || fedOp == fedOpBind) {
+ // x-match arg MUST be present for a bind call
+ std::string x_match_value = getMatch(args);
+
+ if (x_match_value != all && x_match_value != any) {
+ throw InternalErrorException(QPID_MSG("Invalid or missing x-match value binding to headers exchange. Must be a string [\"all\" or \"any\"]"));
+ }
+
+ {
+ Mutex::ScopedLock l(lock);
+ Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args));
+ BoundKey bk(binding);
+ if (bindings.add_unless(bk, MatchArgs(queue, args))) {
+ binding->startManagement();
+ propagate = bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
+ return false;
+ }
+ } // lock dropped
+
+ } else if (fedOp == fedOpUnbind) {
+ Mutex::ScopedLock l(lock);
+
+ FedUnbindModifier modifier(queue->getName(), fedOrigin);
+ bindings.modify_if(MatchKey(queue, bindingKey), modifier);
+ propagate = modifier.shouldPropagate;
+ if (modifier.shouldUnbind) {
+ unbind(queue, bindingKey, args);
+ }
+
+ } else if (fedOp == fedOpReorigin) {
+ Bindings::ConstPtr p = bindings.snapshot();
+ if (p.get())
+ {
+ Mutex::ScopedLock l(lock);
+ for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i)
+ {
+ if ((*i).fedBinding.hasLocal()) {
+ propagateFedOp( (*i).binding->key, string(), fedOpBind, string());
+ }
+ }
+ }
+ }
+ routeIVE();
+ if (propagate) {
+ FieldTable * prop_args = (extra_args.count() != 0 ? &extra_args : 0);
+ propagateFedOp(bindingKey, fedTags, fedOp, fedOrigin, prop_args);
+ }
+
+ return true;
+}
+
+bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable *args){
+ bool propagate = false;
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ {
+ Mutex::ScopedLock l(lock);
+
+ FedUnbindModifier modifier(queue->getName(), fedOrigin);
+ MatchKey match_key(queue, bindingKey);
+ bindings.modify_if(match_key, modifier);
+ propagate = modifier.shouldPropagate;
+ if (modifier.shouldUnbind) {
+ if (bindings.remove_if(match_key)) {
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ if (propagate) {
+ propagateFedOp(bindingKey, string(), fedOpUnbind, string());
+ }
+ return true;
+}
+
+
+void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args)
+{
+ if (!args) {
+ //can't match if there were no headers passed in
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgReceives();
+ mgmtExchange->inc_byteReceives(msg.contentSize());
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ return;
+ }
+
+ PreRoute pr(msg, this);
+
+ BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ Bindings::ConstPtr p = bindings.snapshot();
+ if (p.get()) {
+ for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) {
+ if (match((*i).binding->args, *args)) {
+ b->push_back((*i).binding);
+ }
+ }
+ }
+ doRoute(msg, b);
+}
+
+
+bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const args)
+{
+ Bindings::ConstPtr p = bindings.snapshot();
+ if (p.get()){
+ for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) {
+ if ( (!args || equal((*i).binding->args, *args)) && (!queue || (*i).binding->queue == queue)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void HeadersExchange::getNonFedArgs(const FieldTable* args, FieldTable& nonFedArgs)
+{
+ if (!args)
+ {
+ return;
+ }
+
+ for (qpid::framing::FieldTable::ValueMap::const_iterator i=args->begin(); i != args->end(); ++i)
+ {
+ const string & name(i->first);
+ if (name == qpidFedOp ||
+ name == qpidFedTags ||
+ name == qpidFedOrigin)
+ {
+ continue;
+ }
+ nonFedArgs.insert((*i));
+ }
+}
+
+HeadersExchange::~HeadersExchange() {}
+
+const std::string HeadersExchange::typeName("headers");
+
+namespace
+{
+
+ bool match_values(const FieldValue& bind, const FieldValue& msg) {
+ return bind.getType() == 0xf0 || bind == msg;
+ }
+
+}
+
+
+bool HeadersExchange::match(const FieldTable& bind, const FieldTable& msg) {
+ typedef FieldTable::ValueMap Map;
+ std::string what = getMatch(&bind);
+ if (what == all) {
+ for (Map::const_iterator i = bind.begin();
+ i != bind.end();
+ ++i)
+ {
+ if (i->first != x_match)
+ {
+ Map::const_iterator j = msg.find(i->first);
+ if (j == msg.end()) return false;
+ if (!match_values(*(i->second), *(j->second))) return false;
+ }
+ }
+ return true;
+ } else if (what == any) {
+ for (Map::const_iterator i = bind.begin();
+ i != bind.end();
+ ++i)
+ {
+ if (i->first != x_match)
+ {
+ Map::const_iterator j = msg.find(i->first);
+ if (j != msg.end()) {
+ if (match_values(*(i->second), *(j->second))) return true;
+ }
+ }
+ }
+ return false;
+ } else {
+ return false;
+ }
+}
+
+bool HeadersExchange::equal(const FieldTable& a, const FieldTable& b) {
+ typedef FieldTable::ValueMap Map;
+ for (Map::const_iterator i = a.begin();
+ i != a.end();
+ ++i)
+ {
+ Map::const_iterator j = b.find(i->first);
+ if (j == b.end()) return false;
+ if (!match_values(*(i->second), *(j->second))) return false;
+ }
+ return true;
+}
+
+//---------
+HeadersExchange::MatchArgs::MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a) : queue(q), args(a) {}
+
+bool HeadersExchange::MatchArgs::operator()(BoundKey & bk)
+{
+ return bk.binding->queue == queue && bk.binding->args == *args;
+}
+
+//---------
+HeadersExchange::MatchKey::MatchKey(Queue::shared_ptr q, const std::string& k) : queue(q), key(k) {}
+
+bool HeadersExchange::MatchKey::operator()(BoundKey & bk)
+{
+ return bk.binding->queue == queue && bk.binding->key == key;
+}
+
+//----------
+HeadersExchange::FedUnbindModifier::FedUnbindModifier(const string& queueName, const string& origin) : queueName(queueName), fedOrigin(origin), shouldUnbind(false), shouldPropagate(false) {}
+HeadersExchange::FedUnbindModifier::FedUnbindModifier() : shouldUnbind(false), shouldPropagate(false) {}
+
+bool HeadersExchange::FedUnbindModifier::operator()(BoundKey & bk)
+{
+ shouldPropagate = bk.fedBinding.delOrigin(queueName, fedOrigin);
+ if (bk.fedBinding.countFedBindings(queueName) == 0)
+ {
+ shouldUnbind = true;
+ }
+ return true;
+}
+
diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.h b/qpid/cpp/src/qpid/broker/HeadersExchange.h
new file mode 100644
index 0000000000..3b939d6851
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/HeadersExchange.h
@@ -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.
+ *
+ */
+#ifndef _HeadersExchange_
+#define _HeadersExchange_
+
+#include <vector>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/Queue.h"
+
+namespace qpid {
+namespace broker {
+
+
+class HeadersExchange : public virtual Exchange {
+
+ struct BoundKey
+ {
+ Binding::shared_ptr binding;
+ FedBinding fedBinding;
+ BoundKey(Binding::shared_ptr binding_) : binding(binding_) {}
+ };
+
+ struct MatchArgs
+ {
+ const Queue::shared_ptr queue;
+ const qpid::framing::FieldTable* args;
+ MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a);
+ bool operator()(BoundKey & bk);
+ };
+
+ struct MatchKey
+ {
+ const Queue::shared_ptr queue;
+ const std::string& key;
+ MatchKey(Queue::shared_ptr q, const std::string& k);
+ bool operator()(BoundKey & bk);
+ };
+
+ struct FedUnbindModifier
+ {
+ std::string queueName;
+ std::string fedOrigin;
+ bool shouldUnbind;
+ bool shouldPropagate;
+ FedUnbindModifier();
+ FedUnbindModifier(const std::string& queueName, const std::string& origin);
+ bool operator()(BoundKey & bk);
+ };
+
+ typedef qpid::sys::CopyOnWriteArray<BoundKey> Bindings;
+
+ Bindings bindings;
+ qpid::sys::Mutex lock;
+
+ static std::string getMatch(const framing::FieldTable* args);
+
+ protected:
+ void getNonFedArgs(const framing::FieldTable* args,
+ framing::FieldTable& nonFedArgs);
+
+ public:
+ static const std::string typeName;
+
+ QPID_BROKER_EXTERN HeadersExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN HeadersExchange(const std::string& _name,
+ bool _durable,
+ const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const std::string* const routingKey,
+ const qpid::framing::FieldTable* const args);
+
+ QPID_BROKER_EXTERN virtual ~HeadersExchange();
+
+ virtual bool supportsDynamicBinding() { return true; }
+
+ static QPID_BROKER_EXTERN bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs);
+ static bool equal(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs);
+};
+
+
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp b/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp
new file mode 100644
index 0000000000..a811a86492
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/LegacyLVQ.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/QueuedMessage.h"
+
+namespace qpid {
+namespace broker {
+
+LegacyLVQ::LegacyLVQ(const std::string& k, bool b, Broker* br) : MessageMap(k), noBrowse(b), broker(br) {}
+
+void LegacyLVQ::setNoBrowse(bool b)
+{
+ noBrowse = b;
+}
+
+bool LegacyLVQ::remove(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ Ordering::iterator i = messages.find(position);
+ if (i != messages.end() && i->second.payload == message.payload) {
+ message = i->second;
+ erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool LegacyLVQ::next(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ if (MessageMap::next(position, message)) {
+ if (!noBrowse) index.erase(getKey(message));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool LegacyLVQ::push(const QueuedMessage& added, QueuedMessage& removed)
+{
+ //Hack to disable LVQ behaviour on cluster update:
+ if (broker && broker->isClusterUpdatee()) {
+ messages[added.position] = added;
+ return false;
+ } else {
+ return MessageMap::push(added, removed);
+ }
+}
+
+const QueuedMessage& LegacyLVQ::replace(const QueuedMessage& original, const QueuedMessage& update)
+{
+ //add the new message into the original position of the replaced message
+ Ordering::iterator i = messages.find(original.position);
+ i->second = update;
+ i->second.position = original.position;
+ return i->second;
+}
+
+void LegacyLVQ::removeIf(Predicate p)
+{
+ //Note: This method is currently called periodically on the timer
+ //thread to expire messages. In a clustered broker this means that
+ //the purging does not occur on the cluster event dispatch thread
+ //and consequently that is not totally ordered w.r.t other events
+ //(including publication of messages). The cluster does ensure
+ //that the actual expiration of messages (as distinct from the
+ //removing of those expired messages from the queue) *is*
+ //consistently ordered w.r.t. cluster events. This means that
+ //delivery of messages is in general consistent across the cluster
+ //inspite of any non-determinism in the triggering of a
+ //purge. However at present purging a last value queue (of the
+ //legacy sort) could potentially cause inconsistencies in the
+ //cluster (as the order w.r.t publications can affect the order in
+ //which messages appear in the queue). Consequently periodic
+ //purging of an LVQ is not enabled if the broker is clustered
+ //(expired messages will be removed on delivery and consolidated
+ //by key as part of normal LVQ operation).
+
+ //TODO: Is there a neater way to check whether broker is
+ //clustered? Here we assume that if the clustered timer is the
+ //same as the regular timer, we are not clustered:
+ if (!broker || &(broker->getClusterTimer()) == &(broker->getTimer()))
+ MessageMap::removeIf(p);
+}
+
+std::auto_ptr<Messages> LegacyLVQ::updateOrReplace(std::auto_ptr<Messages> current,
+ const std::string& key, bool noBrowse, Broker* broker)
+{
+ LegacyLVQ* lvq = dynamic_cast<LegacyLVQ*>(current.get());
+ if (lvq) {
+ lvq->setNoBrowse(noBrowse);
+ return current;
+ } else {
+ return std::auto_ptr<Messages>(new LegacyLVQ(key, noBrowse, broker));
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/LegacyLVQ.h b/qpid/cpp/src/qpid/broker/LegacyLVQ.h
new file mode 100644
index 0000000000..dd0fd7aaec
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/LegacyLVQ.h
@@ -0,0 +1,59 @@
+#ifndef QPID_BROKER_LEGACYLVQ_H
+#define QPID_BROKER_LEGACYLVQ_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/MessageMap.h"
+#include <memory>
+
+namespace qpid {
+namespace broker {
+class Broker;
+
+/**
+ * This class encapsulates the behaviour of the old style LVQ where a
+ * message replacing another messages for the given key will use the
+ * position in the queue of the previous message. This however causes
+ * problems for browsing. Either browsers stop the coalescing of
+ * messages by key (default) or they may mis updates (if the no-browse
+ * option is specified).
+ */
+class LegacyLVQ : public MessageMap
+{
+ public:
+ LegacyLVQ(const std::string& key, bool noBrowse = false, Broker* broker = 0);
+ bool remove(const framing::SequenceNumber&, QueuedMessage&);
+ bool next(const framing::SequenceNumber&, QueuedMessage&);
+ bool push(const QueuedMessage& added, QueuedMessage& removed);
+ void removeIf(Predicate);
+ void setNoBrowse(bool);
+ static std::auto_ptr<Messages> updateOrReplace(std::auto_ptr<Messages> current,
+ const std::string& key, bool noBrowse,
+ Broker* broker);
+ protected:
+ bool noBrowse;
+ Broker* broker;
+
+ const QueuedMessage& replace(const QueuedMessage&, const QueuedMessage&);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_LEGACYLVQ_H*/
diff --git a/qpid/cpp/src/qpid/broker/Link.cpp b/qpid/cpp/src/qpid/broker/Link.cpp
new file mode 100644
index 0000000000..9ab4379a69
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Link.cpp
@@ -0,0 +1,474 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Link.h"
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
+#include "qmf/org/apache/qpid/broker/EventBrokerLinkUp.h"
+#include "qmf/org/apache/qpid/broker/EventBrokerLinkDown.h"
+#include "boost/bind.hpp"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/broker/AclModule.h"
+
+using namespace qpid::broker;
+using qpid::framing::Buffer;
+using qpid::framing::FieldTable;
+using qpid::framing::UnauthorizedAccessException;
+using qpid::framing::connection::CLOSE_CODE_CONNECTION_FORCED;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+using qpid::sys::Mutex;
+using std::stringstream;
+using std::string;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+Link::Link(LinkRegistry* _links,
+ MessageStore* _store,
+ string& _host,
+ uint16_t _port,
+ string& _transport,
+ bool _durable,
+ string& _authMechanism,
+ string& _username,
+ string& _password,
+ Broker* _broker,
+ Manageable* parent)
+ : links(_links), store(_store), host(_host), port(_port),
+ transport(_transport),
+ durable(_durable),
+ authMechanism(_authMechanism), username(_username), password(_password),
+ persistenceId(0), mgmtObject(0), broker(_broker), state(0),
+ visitCount(0),
+ currentInterval(1),
+ closing(false),
+ updateUrls(false),
+ channelCounter(1),
+ connection(0),
+ agent(0)
+{
+ if (parent != 0 && broker != 0)
+ {
+ agent = broker->getManagementAgent();
+ if (agent != 0)
+ {
+ mgmtObject = new _qmf::Link(agent, this, parent, _host, _port, _transport, _durable);
+ agent->addObject(mgmtObject, 0, durable);
+ }
+ }
+ setStateLH(STATE_WAITING);
+}
+
+Link::~Link ()
+{
+ if (state == STATE_OPERATIONAL && connection != 0)
+ connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
+
+ if (mgmtObject != 0)
+ mgmtObject->resourceDestroy ();
+}
+
+void Link::setStateLH (int newState)
+{
+ if (newState == state)
+ return;
+
+ state = newState;
+
+ if (hideManagement())
+ return;
+
+ switch (state)
+ {
+ case STATE_WAITING : mgmtObject->set_state("Waiting"); break;
+ case STATE_CONNECTING : mgmtObject->set_state("Connecting"); break;
+ case STATE_OPERATIONAL : mgmtObject->set_state("Operational"); break;
+ case STATE_FAILED : mgmtObject->set_state("Failed"); break;
+ case STATE_CLOSED : mgmtObject->set_state("Closed"); break;
+ case STATE_PASSIVE : mgmtObject->set_state("Passive"); break;
+ }
+}
+
+void Link::startConnectionLH ()
+{
+ try {
+ // Set the state before calling connect. It is possible that connect
+ // will fail synchronously and call Link::closed before returning.
+ setStateLH(STATE_CONNECTING);
+ broker->connect (host, boost::lexical_cast<std::string>(port), transport,
+ boost::bind (&Link::closed, this, _1, _2));
+ QPID_LOG (debug, "Inter-broker link connecting to " << host << ":" << port);
+ } catch(std::exception& e) {
+ setStateLH(STATE_WAITING);
+ if (!hideManagement())
+ mgmtObject->set_lastError (e.what());
+ }
+}
+
+void Link::established ()
+{
+ stringstream addr;
+ addr << host << ":" << port;
+ QPID_LOG (info, "Inter-broker link established to " << addr.str());
+
+ if (!hideManagement() && agent)
+ agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str()));
+
+ {
+ Mutex::ScopedLock mutex(lock);
+ setStateLH(STATE_OPERATIONAL);
+ currentInterval = 1;
+ visitCount = 0;
+ if (closing)
+ destroy();
+ }
+}
+
+void Link::closed (int, std::string text)
+{
+ Mutex::ScopedLock mutex(lock);
+ QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text);
+
+ connection = 0;
+
+ if (state == STATE_OPERATIONAL) {
+ stringstream addr;
+ addr << host << ":" << port;
+ QPID_LOG (warning, "Inter-broker link disconnected from " << addr.str());
+ if (!hideManagement() && agent)
+ agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
+ }
+
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ (*i)->closed();
+ created.push_back(*i);
+ }
+ active.clear();
+
+ if (state != STATE_FAILED)
+ {
+ setStateLH(STATE_WAITING);
+ if (!hideManagement())
+ mgmtObject->set_lastError (text);
+ }
+
+ if (closing)
+ destroy();
+}
+
+void Link::destroy ()
+{
+ Bridges toDelete;
+ {
+ Mutex::ScopedLock mutex(lock);
+
+ QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management");
+ if (connection)
+ connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
+
+ setStateLH(STATE_CLOSED);
+
+ // Move the bridges to be deleted into a local vector so there is no
+ // corruption of the iterator caused by bridge deletion.
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ (*i)->closed();
+ toDelete.push_back(*i);
+ }
+ active.clear();
+
+ for (Bridges::iterator i = created.begin(); i != created.end(); i++)
+ toDelete.push_back(*i);
+ created.clear();
+ }
+ // Now delete all bridges on this link (don't hold the lock for this).
+ for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++)
+ (*i)->destroy();
+ toDelete.clear();
+ links->destroy (host, port);
+}
+
+void Link::add(Bridge::shared_ptr bridge)
+{
+ Mutex::ScopedLock mutex(lock);
+ created.push_back (bridge);
+}
+
+void Link::cancel(Bridge::shared_ptr bridge)
+{
+ {
+ Mutex::ScopedLock mutex(lock);
+
+ for (Bridges::iterator i = created.begin(); i != created.end(); i++) {
+ if ((*i).get() == bridge.get()) {
+ created.erase(i);
+ break;
+ }
+ }
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ if ((*i).get() == bridge.get()) {
+ cancellations.push_back(bridge);
+ bridge->closed();
+ active.erase(i);
+ break;
+ }
+ }
+ }
+ if (!cancellations.empty()) {
+ connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
+ }
+}
+
+void Link::ioThreadProcessing()
+{
+ Mutex::ScopedLock mutex(lock);
+
+ if (state != STATE_OPERATIONAL)
+ return;
+ QPID_LOG(debug, "Link::ioThreadProcessing()");
+
+ //process any pending creates and/or cancellations
+ if (!created.empty()) {
+ for (Bridges::iterator i = created.begin(); i != created.end(); ++i) {
+ active.push_back(*i);
+ (*i)->create(*connection);
+ }
+ created.clear();
+ }
+ if (!cancellations.empty()) {
+ for (Bridges::iterator i = cancellations.begin(); i != cancellations.end(); ++i) {
+ (*i)->cancel(*connection);
+ }
+ cancellations.clear();
+ }
+}
+
+void Link::setConnection(Connection* c)
+{
+ Mutex::ScopedLock mutex(lock);
+ connection = c;
+ updateUrls = true;
+}
+
+void Link::maintenanceVisit ()
+{
+ Mutex::ScopedLock mutex(lock);
+
+ if (connection && updateUrls) {
+ urls.reset(connection->getKnownHosts());
+ QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << urls);
+ updateUrls = false;
+ }
+
+ if (state == STATE_WAITING)
+ {
+ visitCount++;
+ if (visitCount >= currentInterval)
+ {
+ visitCount = 0;
+ //switch host and port to next in url list if possible
+ if (!tryFailover()) {
+ currentInterval *= 2;
+ if (currentInterval > MAX_INTERVAL)
+ currentInterval = MAX_INTERVAL;
+ startConnectionLH();
+ }
+ }
+ }
+ else if (state == STATE_OPERATIONAL && (!created.empty() || !cancellations.empty()) && connection != 0)
+ connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
+}
+
+void Link::reconnect(const qpid::Address& a)
+{
+ Mutex::ScopedLock mutex(lock);
+ host = a.host;
+ port = a.port;
+ transport = a.protocol;
+ startConnectionLH();
+ if (!hideManagement()) {
+ stringstream errorString;
+ errorString << "Failed over to " << a;
+ mgmtObject->set_lastError(errorString.str());
+ }
+}
+
+bool Link::tryFailover()
+{
+ Address next;
+ if (urls.next(next) &&
+ (next.host != host || next.port != port || next.protocol != transport)) {
+ links->changeAddress(Address(transport, host, port), next);
+ QPID_LOG(debug, "Link failing over to " << host << ":" << port);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Management updates for a linke are inconsistent in a cluster, so they are
+// suppressed.
+bool Link::hideManagement() const {
+ return !mgmtObject || ( broker && broker->isInCluster());
+}
+
+uint Link::nextChannel()
+{
+ Mutex::ScopedLock mutex(lock);
+
+ return channelCounter++;
+}
+
+void Link::notifyConnectionForced(const string text)
+{
+ Mutex::ScopedLock mutex(lock);
+
+ setStateLH(STATE_FAILED);
+ if (!hideManagement())
+ mgmtObject->set_lastError(text);
+}
+
+void Link::setPersistenceId(uint64_t id) const
+{
+ persistenceId = id;
+}
+
+const string& Link::getName() const
+{
+ return host;
+}
+
+Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer)
+{
+ string host;
+ uint16_t port;
+ string transport;
+ string authMechanism;
+ string username;
+ string password;
+
+ buffer.getShortString(host);
+ port = buffer.getShort();
+ buffer.getShortString(transport);
+ bool durable(buffer.getOctet());
+ buffer.getShortString(authMechanism);
+ buffer.getShortString(username);
+ buffer.getShortString(password);
+
+ return links.declare(host, port, transport, durable, authMechanism, username, password).first;
+}
+
+void Link::encode(Buffer& buffer) const
+{
+ buffer.putShortString(string("link"));
+ buffer.putShortString(host);
+ buffer.putShort(port);
+ buffer.putShortString(transport);
+ buffer.putOctet(durable ? 1 : 0);
+ buffer.putShortString(authMechanism);
+ buffer.putShortString(username);
+ buffer.putShortString(password);
+}
+
+uint32_t Link::encodedSize() const
+{
+ return host.size() + 1 // short-string (host)
+ + 5 // short-string ("link")
+ + 2 // port
+ + transport.size() + 1 // short-string(transport)
+ + 1 // durable
+ + authMechanism.size() + 1
+ + username.size() + 1
+ + password.size() + 1;
+}
+
+ManagementObject* Link::GetManagementObject (void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& text)
+{
+ switch (op)
+ {
+ case _qmf::Link::METHOD_CLOSE :
+ if (!closing) {
+ closing = true;
+ if (state != STATE_CONNECTING && connection) {
+ //connection can only be closed on the connections own IO processing thread
+ connection->requestIOProcessing(boost::bind(&Link::destroy, this));
+ }
+ }
+ return Manageable::STATUS_OK;
+
+ case _qmf::Link::METHOD_BRIDGE :
+ _qmf::ArgsLinkBridge& iargs = (_qmf::ArgsLinkBridge&) args;
+ QPID_LOG(debug, "Link::bridge() request received");
+
+ // Durable bridges are only valid on durable links
+ if (iargs.i_durable && !durable) {
+ text = "Can't create a durable route on a non-durable link";
+ return Manageable::STATUS_USER;
+ }
+
+ if (iargs.i_dynamic) {
+ Exchange::shared_ptr exchange = getBroker()->getExchanges().get(iargs.i_src);
+ if (exchange.get() == 0) {
+ text = "Exchange not found";
+ return Manageable::STATUS_USER;
+ }
+ if (!exchange->supportsDynamicBinding()) {
+ text = "Exchange type does not support dynamic routing";
+ return Manageable::STATUS_USER;
+ }
+ }
+
+ std::pair<Bridge::shared_ptr, bool> result =
+ links->declare (host, port, iargs.i_durable, iargs.i_src,
+ iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue,
+ iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes,
+ iargs.i_dynamic, iargs.i_sync);
+
+ if (result.second && iargs.i_durable)
+ store->create(*result.first);
+
+ return Manageable::STATUS_OK;
+ }
+
+ return Manageable::STATUS_UNKNOWN_METHOD;
+}
+
+void Link::setPassive(bool passive)
+{
+ Mutex::ScopedLock mutex(lock);
+ if (passive) {
+ setStateLH(STATE_PASSIVE);
+ } else {
+ if (state == STATE_PASSIVE) {
+ setStateLH(STATE_WAITING);
+ } else {
+ QPID_LOG(warning, "Ignoring attempt to activate non-passive link");
+ }
+ }
+}
diff --git a/qpid/cpp/src/qpid/broker/Link.h b/qpid/cpp/src/qpid/broker/Link.h
new file mode 100644
index 0000000000..4badd8b3a1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Link.h
@@ -0,0 +1,145 @@
+#ifndef _broker_Link_h
+#define _broker_Link_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/PersistableConfig.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/RetryList.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/broker/Link.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+
+namespace qpid {
+ namespace broker {
+
+ class LinkRegistry;
+ class Broker;
+ class Connection;
+
+ class Link : public PersistableConfig, public management::Manageable {
+ private:
+ sys::Mutex lock;
+ LinkRegistry* links;
+ MessageStore* store;
+ std::string host;
+ uint16_t port;
+ std::string transport;
+ bool durable;
+ std::string authMechanism;
+ std::string username;
+ std::string password;
+ mutable uint64_t persistenceId;
+ qmf::org::apache::qpid::broker::Link* mgmtObject;
+ Broker* broker;
+ int state;
+ uint32_t visitCount;
+ uint32_t currentInterval;
+ bool closing;
+ RetryList urls;
+ bool updateUrls;
+
+ typedef std::vector<Bridge::shared_ptr> Bridges;
+ Bridges created; // Bridges pending creation
+ Bridges active; // Bridges active
+ Bridges cancellations; // Bridges pending cancellation
+ uint channelCounter;
+ Connection* connection;
+ management::ManagementAgent* agent;
+
+ static const int STATE_WAITING = 1;
+ static const int STATE_CONNECTING = 2;
+ static const int STATE_OPERATIONAL = 3;
+ static const int STATE_FAILED = 4;
+ static const int STATE_CLOSED = 5;
+ static const int STATE_PASSIVE = 6;
+
+ static const uint32_t MAX_INTERVAL = 32;
+
+ void setStateLH (int newState);
+ void startConnectionLH(); // Start the IO Connection
+ void destroy(); // Called when mgmt deletes this link
+ void ioThreadProcessing(); // Called on connection's IO thread by request
+ bool tryFailover(); // Called during maintenance visit
+ bool hideManagement() const;
+
+ public:
+ typedef boost::shared_ptr<Link> shared_ptr;
+
+ Link(LinkRegistry* links,
+ MessageStore* store,
+ std::string& host,
+ uint16_t port,
+ std::string& transport,
+ bool durable,
+ std::string& authMechanism,
+ std::string& username,
+ std::string& password,
+ Broker* broker,
+ management::Manageable* parent = 0);
+ virtual ~Link();
+
+ std::string getHost() { return host; }
+ uint16_t getPort() { return port; }
+ bool isDurable() { return durable; }
+ void maintenanceVisit ();
+ uint nextChannel();
+ void add(Bridge::shared_ptr);
+ void cancel(Bridge::shared_ptr);
+
+ void established(); // Called when connection is created
+ void closed(int, std::string); // Called when connection goes away
+ void setConnection(Connection*); // Set pointer to the AMQP Connection
+ void reconnect(const Address&); //called by LinkRegistry
+
+ std::string getAuthMechanism() { return authMechanism; }
+ std::string getUsername() { return username; }
+ std::string getPassword() { return password; }
+ Broker* getBroker() { return broker; }
+
+ void notifyConnectionForced(const std::string text);
+ void setPassive(bool p);
+
+ // PersistableConfig:
+ void setPersistenceId(uint64_t id) const;
+ uint64_t getPersistenceId() const { return persistenceId; }
+ uint32_t encodedSize() const;
+ void encode(framing::Buffer& buffer) const;
+ const std::string& getName() const;
+
+ static Link::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer);
+
+ // Manageable entry points
+ management::ManagementObject* GetManagementObject(void) const;
+ management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&);
+
+ };
+ }
+}
+
+
+#endif /*!_broker_Link.cpp_h*/
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.cpp b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
new file mode 100644
index 0000000000..e9885f5462
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include <iostream>
+#include <boost/format.hpp>
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+using std::string;
+using std::pair;
+using std::stringstream;
+using boost::intrusive_ptr;
+using boost::format;
+using boost::str;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+#define LINK_MAINT_INTERVAL 2
+
+// TODO: This constructor is only used by the store unit tests -
+// That probably indicates that LinkRegistry isn't correctly
+// factored: The persistence element and maintenance element
+// should be factored separately
+LinkRegistry::LinkRegistry () :
+ broker(0), timer(0),
+ parent(0), store(0), passive(false), passiveChanged(false),
+ realm("")
+{
+}
+
+LinkRegistry::LinkRegistry (Broker* _broker) :
+ broker(_broker), timer(&broker->getTimer()),
+ maintenanceTask(new Periodic(*this)),
+ parent(0), store(0), passive(false), passiveChanged(false),
+ realm(broker->getOptions().realm)
+{
+ timer->add(maintenanceTask);
+}
+
+LinkRegistry::~LinkRegistry()
+{
+ // This test is only necessary if the default constructor above is present
+ if (maintenanceTask)
+ maintenanceTask->cancel();
+}
+
+LinkRegistry::Periodic::Periodic (LinkRegistry& _links) :
+ TimerTask (Duration (LINK_MAINT_INTERVAL * TIME_SEC),"LinkRegistry"), links(_links) {}
+
+void LinkRegistry::Periodic::fire ()
+{
+ links.periodicMaintenance ();
+ setupNextFire();
+ links.timer->add(this);
+}
+
+void LinkRegistry::periodicMaintenance ()
+{
+ Mutex::ScopedLock locker(lock);
+
+ linksToDestroy.clear();
+ bridgesToDestroy.clear();
+ if (passiveChanged) {
+ if (passive) { QPID_LOG(info, "Passivating links"); }
+ else { QPID_LOG(info, "Activating links"); }
+ for (LinkMap::iterator i = links.begin(); i != links.end(); i++) {
+ i->second->setPassive(passive);
+ }
+ passiveChanged = false;
+ }
+ for (LinkMap::iterator i = links.begin(); i != links.end(); i++)
+ i->second->maintenanceVisit();
+ //now process any requests for re-addressing
+ for (AddressMap::iterator i = reMappings.begin(); i != reMappings.end(); i++)
+ updateAddress(i->first, i->second);
+ reMappings.clear();
+}
+
+void LinkRegistry::changeAddress(const qpid::Address& oldAddress, const qpid::Address& newAddress)
+{
+ //done on periodic maintenance thread; hold changes in separate
+ //map to avoid modifying the link map that is iterated over
+ reMappings[createKey(oldAddress)] = newAddress;
+}
+
+bool LinkRegistry::updateAddress(const std::string& oldKey, const qpid::Address& newAddress)
+{
+ std::string newKey = createKey(newAddress);
+ if (links.find(newKey) != links.end()) {
+ QPID_LOG(error, "Attempted to update key from " << oldKey << " to " << newKey << " which is already in use");
+ return false;
+ } else {
+ LinkMap::iterator i = links.find(oldKey);
+ if (i == links.end()) {
+ QPID_LOG(error, "Attempted to update key from " << oldKey << " which does not exist, to " << newKey);
+ return false;
+ } else {
+ links[newKey] = i->second;
+ i->second->reconnect(newAddress);
+ links.erase(oldKey);
+ QPID_LOG(info, "Updated link key from " << oldKey << " to " << newKey);
+ return true;
+ }
+ }
+}
+
+pair<Link::shared_ptr, bool> LinkRegistry::declare(string& host,
+ uint16_t port,
+ string& transport,
+ bool durable,
+ string& authMechanism,
+ string& username,
+ string& password)
+
+{
+ Mutex::ScopedLock locker(lock);
+ string key = createKey(host, port);
+
+ LinkMap::iterator i = links.find(key);
+ if (i == links.end())
+ {
+ Link::shared_ptr link;
+
+ link = Link::shared_ptr (new Link (this, store, host, port, transport, durable,
+ authMechanism, username, password,
+ broker, parent));
+ if (passive) link->setPassive(true);
+ links[key] = link;
+ return std::pair<Link::shared_ptr, bool>(link, true);
+ }
+ return std::pair<Link::shared_ptr, bool>(i->second, false);
+}
+
+pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host,
+ uint16_t port,
+ bool durable,
+ std::string& src,
+ std::string& dest,
+ std::string& key,
+ bool isQueue,
+ bool isLocal,
+ std::string& tag,
+ std::string& excludes,
+ bool dynamic,
+ uint16_t sync)
+{
+ Mutex::ScopedLock locker(lock);
+ QPID_LOG(debug, "Bridge declared " << host << ": " << port << " from " << src << " to " << dest << " (" << key << ")");
+
+ string linkKey = createKey(host, port);
+ stringstream keystream;
+ keystream << linkKey << "!" << src << "!" << dest << "!" << key;
+ string bridgeKey = keystream.str();
+
+ LinkMap::iterator l = links.find(linkKey);
+ if (l == links.end())
+ return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false);
+
+ BridgeMap::iterator b = bridges.find(bridgeKey);
+ if (b == bridges.end())
+ {
+ _qmf::ArgsLinkBridge args;
+ Bridge::shared_ptr bridge;
+
+ args.i_durable = durable;
+ args.i_src = src;
+ args.i_dest = dest;
+ args.i_key = key;
+ args.i_srcIsQueue = isQueue;
+ args.i_srcIsLocal = isLocal;
+ args.i_tag = tag;
+ args.i_excludes = excludes;
+ args.i_dynamic = dynamic;
+ args.i_sync = sync;
+
+ bridge = Bridge::shared_ptr
+ (new Bridge (l->second.get(), l->second->nextChannel(),
+ boost::bind(&LinkRegistry::destroy, this,
+ host, port, src, dest, key), args));
+ bridges[bridgeKey] = bridge;
+ l->second->add(bridge);
+ return std::pair<Bridge::shared_ptr, bool>(bridge, true);
+ }
+ return std::pair<Bridge::shared_ptr, bool>(b->second, false);
+}
+
+void LinkRegistry::destroy(const string& host, const uint16_t port)
+{
+ Mutex::ScopedLock locker(lock);
+ string key = createKey(host, port);
+
+ LinkMap::iterator i = links.find(key);
+ if (i != links.end())
+ {
+ if (i->second->isDurable() && store)
+ store->destroy(*(i->second));
+ linksToDestroy[key] = i->second;
+ links.erase(i);
+ }
+}
+
+void LinkRegistry::destroy(const std::string& host,
+ const uint16_t port,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key)
+{
+ Mutex::ScopedLock locker(lock);
+ string linkKey = createKey(host, port);
+ stringstream keystream;
+ keystream << linkKey << "!" << src << "!" << dest << "!" << key;
+ string bridgeKey = keystream.str();
+
+ LinkMap::iterator l = links.find(linkKey);
+ if (l == links.end())
+ return;
+
+ BridgeMap::iterator b = bridges.find(bridgeKey);
+ if (b == bridges.end())
+ return;
+
+ l->second->cancel(b->second);
+ if (b->second->isDurable())
+ store->destroy(*(b->second));
+ bridgesToDestroy[bridgeKey] = b->second;
+ bridges.erase(b);
+}
+
+void LinkRegistry::setStore (MessageStore* _store)
+{
+ store = _store;
+}
+
+MessageStore* LinkRegistry::getStore() const {
+ return store;
+}
+
+Link::shared_ptr LinkRegistry::findLink(const std::string& keyOrMgmtId)
+{
+ // Convert keyOrMgmtId to a host:port key.
+ //
+ // TODO aconway 2011-02-01: centralize code that constructs/parses
+ // connection management IDs. Currently sys:: protocol factories
+ // and IO plugins construct the IDs and LinkRegistry parses them.
+ size_t separator = keyOrMgmtId.find('-');
+ if (separator == std::string::npos) separator = 0;
+ std::string key = keyOrMgmtId.substr(separator+1, std::string::npos);
+
+ Mutex::ScopedLock locker(lock);
+ LinkMap::iterator l = links.find(key);
+ if (l != links.end()) return l->second;
+ else return Link::shared_ptr();
+}
+
+void LinkRegistry::notifyConnection(const std::string& key, Connection* c)
+{
+ Link::shared_ptr link = findLink(key);
+ if (link) {
+ link->established();
+ link->setConnection(c);
+ c->setUserId(str(format("%1%@%2%") % link->getUsername() % realm));
+ }
+}
+
+void LinkRegistry::notifyClosed(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (link) {
+ link->closed(0, "Closed by peer");
+ }
+}
+
+void LinkRegistry::notifyConnectionForced(const std::string& key, const std::string& text)
+{
+ Link::shared_ptr link = findLink(key);
+ if (link) {
+ link->notifyConnectionForced(text);
+ }
+}
+
+std::string LinkRegistry::getAuthMechanism(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (link)
+ return link->getAuthMechanism();
+ return string("ANONYMOUS");
+}
+
+std::string LinkRegistry::getAuthCredentials(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
+
+ string result;
+ result += '\0';
+ result += link->getUsername();
+ result += '\0';
+ result += link->getPassword();
+
+ return result;
+}
+
+std::string LinkRegistry::getUsername(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
+
+ return link->getUsername();
+}
+
+std::string LinkRegistry::getHost(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
+
+ return link->getHost();
+}
+
+uint16_t LinkRegistry::getPort(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return 0;
+
+ return link->getPort();
+}
+
+std::string LinkRegistry::getPassword(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
+
+ return link->getPassword();
+}
+
+std::string LinkRegistry::getAuthIdentity(const std::string& key)
+{
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
+
+ return link->getUsername();
+}
+
+
+std::string LinkRegistry::createKey(const qpid::Address& a) {
+ // TODO aconway 2010-05-11: key should also include protocol/transport to
+ // be unique. Requires refactor of LinkRegistry interface.
+ return createKey(a.host, a.port);
+}
+
+std::string LinkRegistry::createKey(const std::string& host, uint16_t port) {
+ // TODO aconway 2010-05-11: key should also include protocol/transport to
+ // be unique. Requires refactor of LinkRegistry interface.
+ stringstream keystream;
+ keystream << host << ":" << port;
+ return keystream.str();
+}
+
+void LinkRegistry::setPassive(bool p)
+{
+ Mutex::ScopedLock locker(lock);
+ passiveChanged = p != passive;
+ passive = p;
+ //will activate or passivate links on maintenance visit
+}
+
+void LinkRegistry::eachLink(boost::function<void(boost::shared_ptr<Link>)> f) {
+ for (LinkMap::iterator i = links.begin(); i != links.end(); ++i) f(i->second);
+}
+
+void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f) {
+ for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second);
+}
+
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.h b/qpid/cpp/src/qpid/broker/LinkRegistry.h
new file mode 100644
index 0000000000..4c97e4f9d8
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.h
@@ -0,0 +1,163 @@
+#ifndef _broker_LinkRegistry_h
+#define _broker_LinkRegistry_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <map>
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/Address.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/management/Manageable.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace broker {
+
+ class Link;
+ class Broker;
+ class Connection;
+ class LinkRegistry {
+
+ // Declare a timer task to manage the establishment of link connections and the
+ // re-establishment of lost link connections.
+ struct Periodic : public sys::TimerTask
+ {
+ LinkRegistry& links;
+
+ Periodic(LinkRegistry& links);
+ virtual ~Periodic() {};
+ void fire();
+ };
+
+ typedef std::map<std::string, boost::shared_ptr<Link> > LinkMap;
+ typedef std::map<std::string, Bridge::shared_ptr> BridgeMap;
+ typedef std::map<std::string, Address> AddressMap;
+
+ LinkMap links;
+ LinkMap linksToDestroy;
+ BridgeMap bridges;
+ BridgeMap bridgesToDestroy;
+ AddressMap reMappings;
+
+ qpid::sys::Mutex lock;
+ Broker* broker;
+ sys::Timer* timer;
+ boost::intrusive_ptr<qpid::sys::TimerTask> maintenanceTask;
+ management::Manageable* parent;
+ MessageStore* store;
+ bool passive;
+ bool passiveChanged;
+ std::string realm;
+
+ void periodicMaintenance ();
+ bool updateAddress(const std::string& oldKey, const Address& newAddress);
+ boost::shared_ptr<Link> findLink(const std::string& key);
+ static std::string createKey(const Address& address);
+ static std::string createKey(const std::string& host, uint16_t port);
+
+ public:
+ LinkRegistry (); // Only used in store tests
+ LinkRegistry (Broker* _broker);
+ ~LinkRegistry();
+
+ std::pair<boost::shared_ptr<Link>, bool>
+ declare(std::string& host,
+ uint16_t port,
+ std::string& transport,
+ bool durable,
+ std::string& authMechanism,
+ std::string& username,
+ std::string& password);
+ std::pair<Bridge::shared_ptr, bool>
+ declare(std::string& host,
+ uint16_t port,
+ bool durable,
+ std::string& src,
+ std::string& dest,
+ std::string& key,
+ bool isQueue,
+ bool isLocal,
+ std::string& id,
+ std::string& excludes,
+ bool dynamic,
+ uint16_t sync);
+
+ void destroy(const std::string& host, const uint16_t port);
+ void destroy(const std::string& host,
+ const uint16_t port,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key);
+
+ /**
+ * Register the manageable parent for declared queues
+ */
+ void setParent (management::Manageable* _parent) { parent = _parent; }
+
+ /**
+ * Set the store to use. May only be called once.
+ */
+ void setStore (MessageStore*);
+
+ /**
+ * Return the message store used.
+ */
+ MessageStore* getStore() const;
+
+ void notifyConnection (const std::string& key, Connection* c);
+ void notifyClosed (const std::string& key);
+ void notifyConnectionForced (const std::string& key, const std::string& text);
+ std::string getAuthMechanism (const std::string& key);
+ std::string getAuthCredentials (const std::string& key);
+ std::string getAuthIdentity (const std::string& key);
+ std::string getUsername (const std::string& key);
+ std::string getPassword (const std::string& key);
+ std::string getHost (const std::string& key);
+ uint16_t getPort (const std::string& key);
+
+ /**
+ * Called by links failing over to new address
+ */
+ void changeAddress(const Address& oldAddress, const Address& newAddress);
+ /**
+ * Called to alter passive state. In passive state the links
+ * and bridges managed by a link registry will be recorded and
+ * updated but links won't actually establish connections and
+ * bridges won't therefore pull or push any messages.
+ */
+ void setPassive(bool);
+
+
+ /** Iterate over each link in the registry. Used for cluster updates. */
+ void eachLink(boost::function<void(boost::shared_ptr<Link>)> f);
+ /** Iterate over each bridge in the registry. Used for cluster updates. */
+ void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f);
+ };
+}
+}
+
+
+#endif /*!_broker_LinkRegistry_h*/
diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp
new file mode 100644
index 0000000000..763dc55e40
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Message.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/StringUtils.h"
+#include "qpid/framing/frame_functors.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/SendContent.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/TypeFilter.h"
+#include "qpid/log/Statement.h"
+
+#include <time.h>
+
+using boost::intrusive_ptr;
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::sys::TIME_MSEC;
+using qpid::sys::FAR_FUTURE;
+using std::string;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace broker {
+
+TransferAdapter Message::TRANSFER;
+
+Message::Message(const framing::SequenceNumber& id) :
+ frames(id), persistenceId(0), redelivered(false), loaded(false),
+ staged(false), forcePersistentPolicy(false), publisher(0), adapter(0),
+ expiration(FAR_FUTURE), dequeueCallback(0),
+ inCallback(false), requiredCredit(0), isManagementMessage(false)
+{}
+
+Message::Message(const Message& original) :
+ PersistableMessage(), frames(original.frames), persistenceId(0), redelivered(false), loaded(false),
+ staged(false), forcePersistentPolicy(false), publisher(0), adapter(0),
+ expiration(original.expiration), dequeueCallback(0),
+ inCallback(false), requiredCredit(0)
+{
+ setExpiryPolicy(original.expiryPolicy);
+}
+
+Message::~Message()
+{
+ if (expiryPolicy)
+ expiryPolicy->forget(*this);
+}
+
+void Message::forcePersistent()
+{
+ // only set forced bit if we actually need to force.
+ if (! getAdapter().isPersistent(frames) ){
+ forcePersistentPolicy = true;
+ }
+}
+
+bool Message::isForcedPersistent()
+{
+ return forcePersistentPolicy;
+}
+
+std::string Message::getRoutingKey() const
+{
+ return getAdapter().getRoutingKey(frames);
+}
+
+std::string Message::getExchangeName() const
+{
+ return getAdapter().getExchange(frames);
+}
+
+const boost::shared_ptr<Exchange> Message::getExchange(ExchangeRegistry& registry) const
+{
+ if (!exchange) {
+ exchange = registry.get(getExchangeName());
+ }
+ return exchange;
+}
+
+bool Message::isImmediate() const
+{
+ return getAdapter().isImmediate(frames);
+}
+
+const FieldTable* Message::getApplicationHeaders() const
+{
+ return getAdapter().getApplicationHeaders(frames);
+}
+
+std::string Message::getAppId() const
+{
+ return getAdapter().getAppId(frames);
+}
+
+bool Message::isPersistent() const
+{
+ return (getAdapter().isPersistent(frames) || forcePersistentPolicy);
+}
+
+bool Message::requiresAccept()
+{
+ return getAdapter().requiresAccept(frames);
+}
+
+uint32_t Message::getRequiredCredit()
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (!requiredCredit) {
+ //add up payload for all header and content frames in the frameset
+ SumBodySize sum;
+ frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>());
+ requiredCredit = sum.getSize();
+ }
+ return requiredCredit;
+}
+
+void Message::encode(framing::Buffer& buffer) const
+{
+ //encode method and header frames
+ EncodeFrame f1(buffer);
+ frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>());
+
+ //then encode the payload of each content frame
+ framing::EncodeBody f2(buffer);
+ frames.map_if(f2, TypeFilter<CONTENT_BODY>());
+}
+
+void Message::encodeContent(framing::Buffer& buffer) const
+{
+ //encode the payload of each content frame
+ EncodeBody f2(buffer);
+ frames.map_if(f2, TypeFilter<CONTENT_BODY>());
+}
+
+uint32_t Message::encodedSize() const
+{
+ return encodedHeaderSize() + encodedContentSize();
+}
+
+uint32_t Message::encodedContentSize() const
+{
+ return frames.getContentSize();
+}
+
+uint32_t Message::encodedHeaderSize() const
+{
+ //add up the size for all method and header frames in the frameset
+ SumFrameSize sum;
+ frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>());
+ return sum.getSize();
+}
+
+void Message::decodeHeader(framing::Buffer& buffer)
+{
+ AMQFrame method;
+ method.decode(buffer);
+ frames.append(method);
+
+ AMQFrame header;
+ header.decode(buffer);
+ frames.append(header);
+}
+
+void Message::decodeContent(framing::Buffer& buffer)
+{
+ if (buffer.available()) {
+ //get the data as a string and set that as the content
+ //body on a frame then add that frame to the frameset
+ AMQFrame frame((AMQContentBody()));
+ frame.castBody<AMQContentBody>()->decode(buffer, buffer.available());
+ frame.setFirstSegment(false);
+ frames.append(frame);
+ } else {
+ //adjust header flags
+ MarkLastSegment f;
+ frames.map_if(f, TypeFilter<HEADER_BODY>());
+ }
+ //mark content loaded
+ loaded = true;
+}
+
+// Used for testing only
+void Message::tryReleaseContent()
+{
+ if (checkContentReleasable()) {
+ releaseContent();
+ }
+}
+
+void Message::releaseContent(MessageStore* s)
+{
+ //deprecated, use setStore(store); releaseContent(); instead
+ if (!store) setStore(s);
+ releaseContent();
+}
+
+void Message::releaseContent()
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (store) {
+ if (!getPersistenceId()) {
+ intrusive_ptr<PersistableMessage> pmsg(this);
+ store->stage(pmsg);
+ staged = true;
+ }
+ //ensure required credit is cached before content frames are released
+ getRequiredCredit();
+ //remove any content frames from the frameset
+ frames.remove(TypeFilter<CONTENT_BODY>());
+ setContentReleased();
+ }
+}
+
+void Message::destroy()
+{
+ if (staged) {
+ if (store) {
+ store->destroy(*this);
+ } else {
+ QPID_LOG(error, "Message content was staged but no store is set so it can't be destroyed");
+ }
+ }
+}
+
+bool Message::getContentFrame(const Queue& queue, AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const
+{
+ intrusive_ptr<const PersistableMessage> pmsg(this);
+
+ bool done = false;
+ string& data = frame.castBody<AMQContentBody>()->getData();
+ store->loadContent(queue, pmsg, data, offset, maxContentSize);
+ done = data.size() < maxContentSize;
+ frame.setBof(false);
+ frame.setEof(true);
+ QPID_LOG(debug, "loaded frame" << frame);
+ if (offset > 0) {
+ frame.setBos(false);
+ }
+ if (!done) {
+ frame.setEos(false);
+ } else return false;
+ return true;
+}
+
+void Message::sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (isContentReleased() && !frames.isComplete()) {
+ sys::Mutex::ScopedUnlock u(lock);
+ uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
+ bool morecontent = true;
+ for (uint64_t offset = 0; morecontent; offset += maxContentSize)
+ {
+ AMQFrame frame((AMQContentBody()));
+ morecontent = getContentFrame(queue, frame, maxContentSize, offset);
+ out.handle(frame);
+ }
+ } else {
+ Count c;
+ frames.map_if(c, TypeFilter<CONTENT_BODY>());
+
+ SendContent f(out, maxFrameSize, c.getCount());
+ frames.map_if(f, TypeFilter<CONTENT_BODY>());
+ }
+}
+
+void Message::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/) const
+{
+ sys::Mutex::ScopedLock l(lock);
+ Relay f(out);
+ frames.map_if(f, TypeFilter<HEADER_BODY>());
+}
+
+// TODO aconway 2007-11-09: Obsolete, remove. Was used to cover over
+// 0-8/0-9 message differences.
+MessageAdapter& Message::getAdapter() const
+{
+ if (!adapter) {
+ if(frames.isA<MessageTransferBody>()) {
+ adapter = &TRANSFER;
+ } else {
+ const AMQMethodBody* method = frames.getMethod();
+ if (!method) throw Exception("Can't adapt message with no method");
+ else throw Exception(QPID_MSG("Can't adapt message based on " << *method));
+ }
+ }
+ return *adapter;
+}
+
+uint64_t Message::contentSize() const
+{
+ return frames.getContentSize();
+}
+
+bool Message::isContentLoaded() const
+{
+ return loaded;
+}
+
+
+namespace
+{
+const std::string X_QPID_TRACE("x-qpid.trace");
+}
+
+bool Message::isExcluded(const std::vector<std::string>& excludes) const
+{
+ const FieldTable* headers = getApplicationHeaders();
+ if (headers) {
+ std::string traceStr = headers->getAsString(X_QPID_TRACE);
+ if (traceStr.size()) {
+ std::vector<std::string> trace = split(traceStr, ", ");
+
+ for (std::vector<std::string>::const_iterator i = excludes.begin(); i != excludes.end(); i++) {
+ for (std::vector<std::string>::const_iterator j = trace.begin(); j != trace.end(); j++) {
+ if (*i == *j) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void Message::addTraceId(const std::string& id)
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (isA<MessageTransferBody>()) {
+ FieldTable& headers = getProperties<MessageProperties>()->getApplicationHeaders();
+ std::string trace = headers.getAsString(X_QPID_TRACE);
+ if (trace.empty()) {
+ headers.setString(X_QPID_TRACE, id);
+ } else if (trace.find(id) == std::string::npos) {
+ trace += ",";
+ trace += id;
+ headers.setString(X_QPID_TRACE, trace);
+ }
+ }
+}
+
+void Message::setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e)
+{
+ DeliveryProperties* props = getProperties<DeliveryProperties>();
+ if (props->getTtl()) {
+ // AMQP requires setting the expiration property to be posix
+ // time_t in seconds. TTL is in milliseconds
+ if (!props->getExpiration()) {
+ //only set expiration in delivery properties if not already set
+ time_t now = ::time(0);
+ props->setExpiration(now + (props->getTtl()/1000));
+ }
+ // Use higher resolution time for the internal expiry calculation.
+ Duration ttl(std::min(props->getTtl() * TIME_MSEC, (uint64_t) std::numeric_limits<int64_t>::max()));//Prevent overflow
+ expiration = AbsTime(AbsTime::now(), ttl);
+ setExpiryPolicy(e);
+ }
+}
+
+void Message::adjustTtl()
+{
+ DeliveryProperties* props = getProperties<DeliveryProperties>();
+ if (props->getTtl()) {
+ sys::Mutex::ScopedLock l(lock);
+ if (expiration < FAR_FUTURE) {
+ sys::Duration d(sys::AbsTime::now(), getExpiration());
+ props->setTtl(int64_t(d) > 0 ? int64_t(d)/1000000 : 1); // convert from ns to ms; set to 1 if expired
+ }
+ }
+}
+
+void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) {
+ expiryPolicy = e;
+ if (expiryPolicy)
+ expiryPolicy->willExpire(*this);
+}
+
+bool Message::hasExpired()
+{
+ return expiryPolicy && expiryPolicy->hasExpired(*this);
+}
+
+namespace {
+struct ScopedSet {
+ sys::Monitor& lock;
+ bool& flag;
+ ScopedSet(sys::Monitor& l, bool& f) : lock(l), flag(f) {
+ sys::Monitor::ScopedLock sl(lock);
+ flag = true;
+ }
+ ~ScopedSet(){
+ sys::Monitor::ScopedLock sl(lock);
+ flag = false;
+ lock.notifyAll();
+ }
+};
+}
+
+void Message::allDequeuesComplete() {
+ ScopedSet ss(callbackLock, inCallback);
+ MessageCallback* cb = dequeueCallback;
+ if (cb && *cb) (*cb)(intrusive_ptr<Message>(this));
+}
+
+void Message::setDequeueCompleteCallback(MessageCallback& cb) {
+ sys::Mutex::ScopedLock l(callbackLock);
+ while (inCallback) callbackLock.wait();
+ dequeueCallback = &cb;
+}
+
+void Message::resetDequeueCompleteCallback() {
+ sys::Mutex::ScopedLock l(callbackLock);
+ while (inCallback) callbackLock.wait();
+ dequeueCallback = 0;
+}
+
+uint8_t Message::getPriority() const {
+ return getAdapter().getPriority(frames);
+}
+
+framing::FieldTable& Message::getOrInsertHeaders()
+{
+ return getProperties<MessageProperties>()->getApplicationHeaders();
+}
+
+bool Message::getIsManagementMessage() const { return isManagementMessage; }
+void Message::setIsManagementMessage(bool b) { isManagementMessage = b; }
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h
new file mode 100644
index 0000000000..d85ee434db
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Message.h
@@ -0,0 +1,196 @@
+#ifndef _broker_Message_h
+#define _broker_Message_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/PersistableMessage.h"
+#include "qpid/broker/MessageAdapter.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+
+namespace qpid {
+
+namespace framing {
+class FieldTable;
+class SequenceNumber;
+}
+
+namespace broker {
+class ConnectionToken;
+class Exchange;
+class ExchangeRegistry;
+class MessageStore;
+class Queue;
+class ExpiryPolicy;
+
+class Message : public PersistableMessage {
+public:
+ typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback;
+
+ QPID_BROKER_EXTERN Message(const framing::SequenceNumber& id = framing::SequenceNumber());
+ QPID_BROKER_EXTERN Message(const Message&);
+ QPID_BROKER_EXTERN ~Message();
+
+ uint64_t getPersistenceId() const { return persistenceId; }
+ void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; }
+
+ bool getRedelivered() const { return redelivered; }
+ void redeliver() { redelivered = true; }
+
+ const ConnectionToken* getPublisher() const { return publisher; }
+ void setPublisher(ConnectionToken* p) { publisher = p; }
+
+ const framing::SequenceNumber& getCommandId() { return frames.getId(); }
+
+ QPID_BROKER_EXTERN uint64_t contentSize() const;
+
+ QPID_BROKER_EXTERN std::string getRoutingKey() const;
+ const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const;
+ QPID_BROKER_EXTERN std::string getExchangeName() const;
+ bool isImmediate() const;
+ QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() const;
+ QPID_BROKER_EXTERN std::string getAppId() const;
+ framing::FieldTable& getOrInsertHeaders();
+ QPID_BROKER_EXTERN bool isPersistent() const;
+ bool requiresAccept();
+
+ QPID_BROKER_EXTERN void setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e);
+ void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e);
+ bool hasExpired();
+ sys::AbsTime getExpiration() const { return expiration; }
+ void adjustTtl();
+
+ framing::FrameSet& getFrames() { return frames; }
+ const framing::FrameSet& getFrames() const { return frames; }
+
+ template <class T> T* getProperties() {
+ qpid::framing::AMQHeaderBody* p = frames.getHeaders();
+ return p->get<T>(true);
+ }
+
+ template <class T> const T* getProperties() const {
+ const qpid::framing::AMQHeaderBody* p = frames.getHeaders();
+ return p->get<T>(true);
+ }
+
+ template <class T> const T* hasProperties() const {
+ const qpid::framing::AMQHeaderBody* p = frames.getHeaders();
+ return p->get<T>();
+ }
+
+ template <class T> const T* getMethod() const {
+ return frames.as<T>();
+ }
+
+ template <class T> T* getMethod() {
+ return frames.as<T>();
+ }
+
+ template <class T> bool isA() const {
+ return frames.isA<T>();
+ }
+
+ uint32_t getRequiredCredit();
+
+ void encode(framing::Buffer& buffer) const;
+ void encodeContent(framing::Buffer& buffer) const;
+
+ /**
+ * @returns the size of the buffer needed to encode this
+ * message in its entirety
+ */
+ uint32_t encodedSize() const;
+ /**
+ * @returns the size of the buffer needed to encode the
+ * 'header' of this message (not just the header frame,
+ * but other meta data e.g.routing key and exchange)
+ */
+ uint32_t encodedHeaderSize() const;
+ uint32_t encodedContentSize() const;
+
+ QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer);
+ QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer);
+
+ void QPID_BROKER_EXTERN tryReleaseContent();
+ void releaseContent();
+ void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead
+ void destroy();
+
+ bool getContentFrame(const Queue& queue, framing::AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const;
+ QPID_BROKER_EXTERN void sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const;
+ void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize) const;
+
+ QPID_BROKER_EXTERN bool isContentLoaded() const;
+
+ bool isExcluded(const std::vector<std::string>& excludes) const;
+ void addTraceId(const std::string& id);
+
+ void forcePersistent();
+ bool isForcedPersistent();
+
+
+ /** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */
+ void setDequeueCompleteCallback(MessageCallback& cb);
+ void resetDequeueCompleteCallback();
+
+ uint8_t getPriority() const;
+ bool getIsManagementMessage() const;
+ void setIsManagementMessage(bool b);
+ private:
+ MessageAdapter& getAdapter() const;
+ void allDequeuesComplete();
+
+ mutable sys::Mutex lock;
+ framing::FrameSet frames;
+ mutable boost::shared_ptr<Exchange> exchange;
+ mutable uint64_t persistenceId;
+ bool redelivered;
+ bool loaded;
+ bool staged;
+ bool forcePersistentPolicy; // used to force message as durable, via a broker policy
+ ConnectionToken* publisher;
+ mutable MessageAdapter* adapter;
+ qpid::sys::AbsTime expiration;
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+
+ static TransferAdapter TRANSFER;
+
+ mutable boost::intrusive_ptr<Message> empty;
+
+ sys::Monitor callbackLock;
+ MessageCallback* dequeueCallback;
+ bool inCallback;
+
+ uint32_t requiredCredit;
+ bool isManagementMessage;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/MessageAdapter.cpp b/qpid/cpp/src/qpid/broker/MessageAdapter.cpp
new file mode 100644
index 0000000000..0eb4a6fa22
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageAdapter.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/MessageAdapter.h"
+
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/MessageTransferBody.h"
+
+namespace {
+ const std::string empty;
+}
+
+namespace qpid {
+namespace broker{
+
+ std::string TransferAdapter::getRoutingKey(const framing::FrameSet& f)
+ {
+ const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>();
+ return p ? p->getRoutingKey() : empty;
+ }
+
+ std::string TransferAdapter::getExchange(const framing::FrameSet& f)
+ {
+ return f.as<framing::MessageTransferBody>()->getDestination();
+ }
+
+ bool TransferAdapter::isImmediate(const framing::FrameSet&)
+ {
+ //TODO: delete this, immediate is no longer part of the spec
+ return false;
+ }
+
+ const framing::FieldTable* TransferAdapter::getApplicationHeaders(const framing::FrameSet& f)
+ {
+ const framing::MessageProperties* p = f.getHeaders()->get<framing::MessageProperties>();
+ return p ? &(p->getApplicationHeaders()) : 0;
+ }
+
+ bool TransferAdapter::isPersistent(const framing::FrameSet& f)
+ {
+ const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>();
+ return p && p->getDeliveryMode() == 2;
+ }
+
+ bool TransferAdapter::requiresAccept(const framing::FrameSet& f)
+ {
+ const framing::MessageTransferBody* b = f.as<framing::MessageTransferBody>();
+ return b && b->getAcceptMode() == 0/*EXPLICIT == 0*/;
+ }
+
+ uint8_t TransferAdapter::getPriority(const framing::FrameSet& f)
+ {
+ const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>();
+ return p ? p->getPriority() : 0;
+ }
+
+ std::string TransferAdapter::getAppId(const framing::FrameSet& f)
+ {
+ const framing::MessageProperties* p = f.getHeaders()->get<framing::MessageProperties>();
+ return p ? p->getAppId() : empty;
+ }
+}}
diff --git a/qpid/cpp/src/qpid/broker/MessageAdapter.h b/qpid/cpp/src/qpid/broker/MessageAdapter.h
new file mode 100644
index 0000000000..df50db4063
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageAdapter.h
@@ -0,0 +1,62 @@
+#ifndef _broker_MessageAdapter_h
+#define _broker_MessageAdapter_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FrameSet.h"
+
+namespace qpid {
+namespace broker {
+
+// TODO aconway 2007-11-09: No longer needed, we only have one type of message.
+struct MessageAdapter
+{
+ virtual ~MessageAdapter() {}
+
+ virtual std::string getRoutingKey(const framing::FrameSet& f) = 0;
+ virtual std::string getExchange(const framing::FrameSet& f) = 0;
+ virtual bool isImmediate(const framing::FrameSet& f) = 0;
+ virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f) = 0;
+ virtual bool isPersistent(const framing::FrameSet& f) = 0;
+ virtual bool requiresAccept(const framing::FrameSet& f) = 0;
+ virtual uint8_t getPriority(const framing::FrameSet& f) = 0;
+ virtual std::string getAppId(const framing::FrameSet& f) = 0;
+};
+
+struct TransferAdapter : MessageAdapter
+{
+ virtual std::string getRoutingKey(const framing::FrameSet& f);
+ virtual std::string getExchange(const framing::FrameSet& f);
+ virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f);
+ virtual bool isPersistent(const framing::FrameSet& f);
+ bool isImmediate(const framing::FrameSet&);
+ bool requiresAccept(const framing::FrameSet& f);
+ uint8_t getPriority(const framing::FrameSet& f);
+ virtual std::string getAppId(const framing::FrameSet& f);
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp
new file mode 100644
index 0000000000..a6d605c296
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/MessageBuilder.h"
+
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/reply_exceptions.h"
+
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace
+{
+ std::string type_str(uint8_t type);
+ const std::string QPID_MANAGEMENT("qpid.management");
+}
+
+MessageBuilder::MessageBuilder(MessageStore* const _store) :
+ state(DORMANT), store(_store) {}
+
+void MessageBuilder::handle(AMQFrame& frame)
+{
+ uint8_t type = frame.getBody()->type();
+ switch(state) {
+ case METHOD:
+ checkType(METHOD_BODY, type);
+ state = HEADER;
+ break;
+ case HEADER:
+ if (type == CONTENT_BODY) {
+ //TODO: rethink how to handle non-existent headers(?)...
+ //didn't get a header: add in a dummy
+ AMQFrame header((AMQHeaderBody()));
+ header.setBof(false);
+ header.setEof(false);
+ message->getFrames().append(header);
+ } else if (type != HEADER_BODY) {
+ throw CommandInvalidException(
+ QPID_MSG("Invalid frame sequence for message, expected header or content got "
+ << type_str(type) << ")"));
+ }
+ state = CONTENT;
+ break;
+ case CONTENT:
+ checkType(CONTENT_BODY, type);
+ break;
+ default:
+ throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (state=" << state << ")"));
+ }
+ message->getFrames().append(frame);
+}
+
+void MessageBuilder::end()
+{
+ message = 0;
+ state = DORMANT;
+}
+
+void MessageBuilder::start(const SequenceNumber& id)
+{
+ message = intrusive_ptr<Message>(new Message(id));
+ message->setStore(store);
+ state = METHOD;
+}
+
+namespace {
+
+const std::string HEADER_BODY_S = "HEADER";
+const std::string METHOD_BODY_S = "METHOD";
+const std::string CONTENT_BODY_S = "CONTENT";
+const std::string HEARTBEAT_BODY_S = "HEARTBEAT";
+const std::string UNKNOWN = "unknown";
+
+std::string type_str(uint8_t type)
+{
+ switch(type) {
+ case METHOD_BODY: return METHOD_BODY_S;
+ case HEADER_BODY: return HEADER_BODY_S;
+ case CONTENT_BODY: return CONTENT_BODY_S;
+ case HEARTBEAT_BODY: return HEARTBEAT_BODY_S;
+ }
+ return UNKNOWN;
+}
+
+}
+
+void MessageBuilder::checkType(uint8_t expected, uint8_t actual)
+{
+ if (expected != actual) {
+ throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected "
+ << type_str(expected) << " got " << type_str(actual) << ")"));
+ }
+}
diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.h b/qpid/cpp/src/qpid/broker/MessageBuilder.h
new file mode 100644
index 0000000000..b99b8efee6
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageBuilder.h
@@ -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.
+ *
+ */
+#ifndef _MessageBuilder_
+#define _MessageBuilder_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/RefCounted.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+ namespace broker {
+ class Message;
+ class MessageStore;
+
+ class QPID_BROKER_CLASS_EXTERN MessageBuilder : public framing::FrameHandler{
+ public:
+ QPID_BROKER_EXTERN MessageBuilder(MessageStore* const store);
+ QPID_BROKER_EXTERN void handle(framing::AMQFrame& frame);
+ boost::intrusive_ptr<Message> getMessage() { return message; }
+ QPID_BROKER_EXTERN void start(const framing::SequenceNumber& id);
+ void end();
+ private:
+ enum State {DORMANT, METHOD, HEADER, CONTENT};
+ State state;
+ boost::intrusive_ptr<Message> message;
+ MessageStore* const store;
+
+ void checkType(uint8_t expected, uint8_t actual);
+ };
+ }
+}
+
+
+#endif
+
diff --git a/qpid/cpp/src/qpid/broker/MessageDeque.cpp b/qpid/cpp/src/qpid/broker/MessageDeque.cpp
new file mode 100644
index 0000000000..24b8f6f895
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageDeque.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/MessageDeque.h"
+#include "qpid/broker/QueuedMessage.h"
+
+namespace qpid {
+namespace broker {
+
+size_t MessageDeque::size()
+{
+ return messages.size();
+}
+
+bool MessageDeque::empty()
+{
+ return messages.empty();
+}
+
+void MessageDeque::reinsert(const QueuedMessage& message)
+{
+ messages.insert(lower_bound(messages.begin(), messages.end(), message), message);
+}
+
+MessageDeque::Deque::iterator MessageDeque::seek(const framing::SequenceNumber& position)
+{
+ if (!messages.empty()) {
+ QueuedMessage comp;
+ comp.position = position;
+ unsigned long diff = position.getValue() - messages.front().position.getValue();
+ long maxEnd = diff < messages.size()? diff : messages.size();
+ return lower_bound(messages.begin(),messages.begin()+maxEnd,comp);
+ } else {
+ return messages.end();
+ }
+}
+
+bool MessageDeque::find(const framing::SequenceNumber& position, QueuedMessage& message, bool remove)
+{
+ Deque::iterator i = seek(position);
+ if (i != messages.end() && i->position == position) {
+ message = *i;
+ if (remove) messages.erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool MessageDeque::remove(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ return find(position, message, true);
+}
+
+bool MessageDeque::find(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ return find(position, message, false);
+}
+
+bool MessageDeque::next(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ if (messages.empty()) {
+ return false;
+ } else if (position < front().position) {
+ message = front();
+ return true;
+ } else {
+ Deque::iterator i = seek(position+1);
+ if (i != messages.end()) {
+ message = *i;
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+QueuedMessage& MessageDeque::front()
+{
+ return messages.front();
+}
+
+void MessageDeque::pop()
+{
+ if (!messages.empty()) {
+ messages.pop_front();
+ }
+}
+
+bool MessageDeque::pop(QueuedMessage& out)
+{
+ if (messages.empty()) {
+ return false;
+ } else {
+ out = front();
+ messages.pop_front();
+ return true;
+ }
+}
+
+bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/)
+{
+ messages.push_back(added);
+ return false;//adding a message never causes one to be removed for deque
+}
+
+void MessageDeque::foreach(Functor f)
+{
+ std::for_each(messages.begin(), messages.end(), f);
+}
+
+void MessageDeque::removeIf(Predicate p)
+{
+ for (Deque::iterator i = messages.begin(); i != messages.end();) {
+ if (p(*i)) {
+ i = messages.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/MessageDeque.h b/qpid/cpp/src/qpid/broker/MessageDeque.h
new file mode 100644
index 0000000000..0e1aef2986
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageDeque.h
@@ -0,0 +1,62 @@
+#ifndef QPID_BROKER_MESSAGEDEQUE_H
+#define QPID_BROKER_MESSAGEDEQUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Messages.h"
+#include "qpid/broker/QueuedMessage.h"
+#include <deque>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Provides the standard FIFO queue behaviour.
+ */
+class MessageDeque : public Messages
+{
+ public:
+ size_t size();
+ bool empty();
+
+ void reinsert(const QueuedMessage&);
+ bool remove(const framing::SequenceNumber&, QueuedMessage&);
+ bool find(const framing::SequenceNumber&, QueuedMessage&);
+ bool next(const framing::SequenceNumber&, QueuedMessage&);
+
+ QueuedMessage& front();
+ void pop();
+ bool pop(QueuedMessage&);
+ bool push(const QueuedMessage& added, QueuedMessage& removed);
+
+ void foreach(Functor);
+ void removeIf(Predicate);
+
+ private:
+ typedef std::deque<QueuedMessage> Deque;
+ Deque messages;
+
+ Deque::iterator seek(const framing::SequenceNumber&);
+ bool find(const framing::SequenceNumber&, QueuedMessage&, bool remove);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_MESSAGEDEQUE_H*/
diff --git a/qpid/cpp/src/qpid/broker/MessageMap.cpp b/qpid/cpp/src/qpid/broker/MessageMap.cpp
new file mode 100644
index 0000000000..39e23df533
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageMap.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/MessageMap.h"
+#include "qpid/broker/QueuedMessage.h"
+
+namespace qpid {
+namespace broker {
+namespace {
+const std::string EMPTY;
+}
+
+std::string MessageMap::getKey(const QueuedMessage& message)
+{
+ const framing::FieldTable* ft = message.payload->getApplicationHeaders();
+ if (ft) return ft->getAsString(key);
+ else return EMPTY;
+}
+
+size_t MessageMap::size()
+{
+ return messages.size();
+}
+
+bool MessageMap::empty()
+{
+ return messages.empty();
+}
+
+void MessageMap::reinsert(const QueuedMessage& message)
+{
+ std::string key = getKey(message);
+ Index::iterator i = index.find(key);
+ if (i == index.end()) {
+ index[key] = message;
+ messages[message.position] = message;
+ } //else message has already been replaced
+}
+
+bool MessageMap::remove(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ Ordering::iterator i = messages.find(position);
+ if (i != messages.end()) {
+ message = i->second;
+ erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ Ordering::iterator i = messages.find(position);
+ if (i != messages.end()) {
+ message = i->second;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool MessageMap::next(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ if (!messages.empty() && position < front().position) {
+ message = front();
+ return true;
+ } else {
+ Ordering::iterator i = messages.lower_bound(position+1);
+ if (i != messages.end()) {
+ message = i->second;
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+QueuedMessage& MessageMap::front()
+{
+ return messages.begin()->second;
+}
+
+void MessageMap::pop()
+{
+ QueuedMessage dummy;
+ pop(dummy);
+}
+
+bool MessageMap::pop(QueuedMessage& out)
+{
+ Ordering::iterator i = messages.begin();
+ if (i != messages.end()) {
+ out = i->second;
+ erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+const QueuedMessage& MessageMap::replace(const QueuedMessage& original, const QueuedMessage& update)
+{
+ messages.erase(original.position);
+ messages[update.position] = update;
+ return update;
+}
+
+bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed)
+{
+ std::pair<Index::iterator, bool> result = index.insert(Index::value_type(getKey(added), added));
+ if (result.second) {
+ //there was no previous message for this key; nothing needs to
+ //be removed, just add the message into its correct position
+ messages[added.position] = added;
+ return false;
+ } else {
+ //there is already a message with that key which needs to be replaced
+ removed = result.first->second;
+ result.first->second = replace(result.first->second, added);
+ return true;
+ }
+}
+
+void MessageMap::foreach(Functor f)
+{
+ for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) {
+ f(i->second);
+ }
+}
+
+void MessageMap::removeIf(Predicate p)
+{
+ for (Ordering::iterator i = messages.begin(); i != messages.end(); i++) {
+ if (p(i->second)) {
+ erase(i);
+ }
+ }
+}
+
+void MessageMap::erase(Ordering::iterator i)
+{
+ index.erase(getKey(i->second));
+ messages.erase(i);
+}
+
+MessageMap::MessageMap(const std::string& k) : key(k) {}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/MessageMap.h b/qpid/cpp/src/qpid/broker/MessageMap.h
new file mode 100644
index 0000000000..1128a1d54a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageMap.h
@@ -0,0 +1,72 @@
+#ifndef QPID_BROKER_MESSAGEMAP_H
+#define QPID_BROKER_MESSAGEMAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Messages.h"
+#include "qpid/framing/SequenceNumber.h"
+#include <map>
+#include <string>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Provides a last value queue behaviour, whereby a messages replace
+ * any previous message with the same value for a defined property
+ * (i.e. the key).
+ */
+class MessageMap : public Messages
+{
+ public:
+ MessageMap(const std::string& key);
+ virtual ~MessageMap() {}
+
+ size_t size();
+ bool empty();
+
+ void reinsert(const QueuedMessage&);
+ virtual bool remove(const framing::SequenceNumber&, QueuedMessage&);
+ bool find(const framing::SequenceNumber&, QueuedMessage&);
+ virtual bool next(const framing::SequenceNumber&, QueuedMessage&);
+
+ QueuedMessage& front();
+ void pop();
+ bool pop(QueuedMessage&);
+ virtual bool push(const QueuedMessage& added, QueuedMessage& removed);
+
+ void foreach(Functor);
+ virtual void removeIf(Predicate);
+
+ protected:
+ typedef std::map<std::string, QueuedMessage> Index;
+ typedef std::map<framing::SequenceNumber, QueuedMessage> Ordering;
+ const std::string key;
+ Index index;
+ Ordering messages;
+
+ std::string getKey(const QueuedMessage&);
+ virtual const QueuedMessage& replace(const QueuedMessage&, const QueuedMessage&);
+ void erase(Ordering::iterator);
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_MESSAGEMAP_H*/
diff --git a/qpid/cpp/src/qpid/broker/MessageStore.h b/qpid/cpp/src/qpid/broker/MessageStore.h
new file mode 100644
index 0000000000..ab0225ef6b
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageStore.h
@@ -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.
+ *
+ */
+#ifndef _MessageStore_
+#define _MessageStore_
+
+#include "qpid/broker/PersistableExchange.h"
+#include "qpid/broker/PersistableMessage.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/PersistableConfig.h"
+#include "qpid/broker/RecoveryManager.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/framing/FieldTable.h"
+
+#include <qpid/Options.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * An abstraction of the persistent storage for messages. (In
+ * all methods, any pointers/references to queues or messages
+ * are valid only for the duration of the call).
+ */
+class MessageStore : public TransactionalStore, public Recoverable {
+ public:
+
+ /**
+ * If called after initialization but before recovery, will discard the database
+ * content and reinitialize as though it were a new installation. If the parameter
+ * saveStoreContent is true, the content of the store will be saved in such a way
+ * that the truncate can be reversed. This is used when cluster nodes recover and
+ * must get their content from a cluster sync rather than directly from the store.
+ *
+ * @param saveStoreContent If true, will move content of the store to a backup
+ * location where they may be restored later if needed. It is
+ * not necessary to save more than one prior version of the
+ * store.
+ */
+ virtual void truncateInit(const bool saveStoreContent = false) = 0;
+
+ /**
+ * Record the existence of a durable queue
+ */
+ virtual void create(PersistableQueue& queue,
+ const framing::FieldTable& args) = 0;
+ /**
+ * Destroy a durable queue
+ */
+ virtual void destroy(PersistableQueue& queue) = 0;
+
+ /**
+ * Record the existence of a durable exchange
+ */
+ virtual void create(const PersistableExchange& exchange,
+ const framing::FieldTable& args) = 0;
+ /**
+ * Destroy a durable exchange
+ */
+ virtual void destroy(const PersistableExchange& exchange) = 0;
+
+ /**
+ * Record a binding
+ */
+ virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ const std::string& key, const framing::FieldTable& args) = 0;
+
+ /**
+ * Forget a binding
+ */
+ virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ const std::string& key, const framing::FieldTable& args) = 0;
+
+ /**
+ * Record generic durable configuration
+ */
+ virtual void create(const PersistableConfig& config) = 0;
+
+ /**
+ * Destroy generic durable configuration
+ */
+ virtual void destroy(const PersistableConfig& config) = 0;
+
+ /**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+ virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg) = 0;
+
+ /**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+ virtual void destroy(PersistableMessage& msg) = 0;
+
+ /**
+ * Appends content to a previously staged message
+ */
+ virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data) = 0;
+
+ /**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+ virtual void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data, uint64_t offset, uint32_t length) = 0;
+
+ /**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void enqueue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue) = 0;
+
+ /**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to dequeue
+ * @param queue the name of the queue from which it is to be dequeued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void dequeue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue) = 0;
+
+ /**
+ * Flushes all async messages to disk for the specified queue
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param queue the name of the queue from which it is to be dequeued
+ */
+ virtual void flush(const qpid::broker::PersistableQueue& queue)=0;
+
+ /**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk
+ *
+ * @param queue the name of the queue to check for outstanding AIO
+ */
+ virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue) = 0;
+
+
+ virtual ~MessageStore(){}
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp
new file mode 100644
index 0000000000..cd9fd4c933
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/MessageStoreModule.h"
+#include "qpid/broker/NullMessageStore.h"
+#include <iostream>
+
+// This transfer protects against the unloading of the store lib prior to the handling of the exception
+#define TRANSFER_EXCEPTION(fn) try { fn; } catch (std::exception& e) { throw Exception(e.what()); }
+
+using boost::intrusive_ptr;
+using qpid::framing::FieldTable;
+using std::string;
+
+namespace qpid {
+namespace broker {
+
+MessageStoreModule::MessageStoreModule(boost::shared_ptr<MessageStore>& _store)
+ : store(_store) {}
+
+MessageStoreModule::~MessageStoreModule()
+{
+}
+
+bool MessageStoreModule::init(const Options*) { return true; }
+
+void MessageStoreModule::truncateInit(const bool pushDownStoreFiles)
+{
+ TRANSFER_EXCEPTION(store->truncateInit(pushDownStoreFiles));
+}
+
+void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args)
+{
+ TRANSFER_EXCEPTION(store->create(queue, args));
+}
+
+void MessageStoreModule::destroy(PersistableQueue& queue)
+{
+ TRANSFER_EXCEPTION(store->destroy(queue));
+}
+
+void MessageStoreModule::create(const PersistableExchange& exchange, const FieldTable& args)
+{
+ TRANSFER_EXCEPTION(store->create(exchange, args));
+}
+
+void MessageStoreModule::destroy(const PersistableExchange& exchange)
+{
+ TRANSFER_EXCEPTION(store->destroy(exchange));
+}
+
+void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q,
+ const std::string& k, const framing::FieldTable& a)
+{
+ TRANSFER_EXCEPTION(store->bind(e, q, k, a));
+}
+
+void MessageStoreModule::unbind(const PersistableExchange& e, const PersistableQueue& q,
+ const std::string& k, const framing::FieldTable& a)
+{
+ TRANSFER_EXCEPTION(store->unbind(e, q, k, a));
+}
+
+void MessageStoreModule::create(const PersistableConfig& config)
+{
+ TRANSFER_EXCEPTION(store->create(config));
+}
+
+void MessageStoreModule::destroy(const PersistableConfig& config)
+{
+ TRANSFER_EXCEPTION(store->destroy(config));
+}
+
+void MessageStoreModule::recover(RecoveryManager& registry)
+{
+ TRANSFER_EXCEPTION(store->recover(registry));
+}
+
+void MessageStoreModule::stage(const intrusive_ptr<PersistableMessage>& msg)
+{
+ TRANSFER_EXCEPTION(store->stage(msg));
+}
+
+void MessageStoreModule::destroy(PersistableMessage& msg)
+{
+ TRANSFER_EXCEPTION(store->destroy(msg));
+}
+
+void MessageStoreModule::appendContent(const intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data)
+{
+ TRANSFER_EXCEPTION(store->appendContent(msg, data));
+}
+
+void MessageStoreModule::loadContent(
+ const qpid::broker::PersistableQueue& queue,
+ const intrusive_ptr<const PersistableMessage>& msg,
+ string& data, uint64_t offset, uint32_t length)
+{
+ TRANSFER_EXCEPTION(store->loadContent(queue, msg, data, offset, length));
+}
+
+void MessageStoreModule::enqueue(TransactionContext* ctxt,
+ const intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ TRANSFER_EXCEPTION(store->enqueue(ctxt, msg, queue));
+}
+
+void MessageStoreModule::dequeue(TransactionContext* ctxt,
+ const intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ TRANSFER_EXCEPTION(store->dequeue(ctxt, msg, queue));
+}
+
+void MessageStoreModule::flush(const qpid::broker::PersistableQueue& queue)
+{
+ TRANSFER_EXCEPTION(store->flush(queue));
+}
+
+uint32_t MessageStoreModule::outstandingQueueAIO(const PersistableQueue& queue)
+{
+ TRANSFER_EXCEPTION(return store->outstandingQueueAIO(queue));
+}
+
+std::auto_ptr<TransactionContext> MessageStoreModule::begin()
+{
+ TRANSFER_EXCEPTION(return store->begin());
+}
+
+std::auto_ptr<TPCTransactionContext> MessageStoreModule::begin(const std::string& xid)
+{
+ TRANSFER_EXCEPTION(return store->begin(xid));
+}
+
+void MessageStoreModule::prepare(TPCTransactionContext& txn)
+{
+ TRANSFER_EXCEPTION(store->prepare(txn));
+}
+
+void MessageStoreModule::commit(TransactionContext& ctxt)
+{
+ TRANSFER_EXCEPTION(store->commit(ctxt));
+}
+
+void MessageStoreModule::abort(TransactionContext& ctxt)
+{
+ TRANSFER_EXCEPTION(store->abort(ctxt));
+}
+
+void MessageStoreModule::collectPreparedXids(std::set<std::string>& xids)
+{
+ TRANSFER_EXCEPTION(store->collectPreparedXids(xids));
+}
+
+bool MessageStoreModule::isNull() const
+{
+ return NullMessageStore::isNullStore(store.get());
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.h b/qpid/cpp/src/qpid/broker/MessageStoreModule.h
new file mode 100644
index 0000000000..56b5a3c1ae
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.h
@@ -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.
+ *
+ */
+#ifndef _MessageStoreModule_
+#define _MessageStoreModule_
+
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/RecoveryManager.h"
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * A null implementation of the MessageStore interface
+ */
+class MessageStoreModule : public MessageStore
+{
+ boost::shared_ptr<MessageStore> store;
+ public:
+ MessageStoreModule(boost::shared_ptr<MessageStore>& store);
+
+ bool init(const Options* options);
+ void truncateInit(const bool pushDownStoreFiles = false);
+ std::auto_ptr<TransactionContext> begin();
+ std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
+ void prepare(TPCTransactionContext& txn);
+ void commit(TransactionContext& txn);
+ void abort(TransactionContext& txn);
+ void collectPreparedXids(std::set<std::string>& xids);
+
+ void create(PersistableQueue& queue, const framing::FieldTable& args);
+ void destroy(PersistableQueue& queue);
+ void create(const PersistableExchange& exchange, const framing::FieldTable& args);
+ void destroy(const PersistableExchange& exchange);
+ void bind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ const std::string& key, const framing::FieldTable& args);
+ void unbind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ const std::string& key, const framing::FieldTable& args);
+ void create(const PersistableConfig& config);
+ void destroy(const PersistableConfig& config);
+ void recover(RecoveryManager& queues);
+ void stage(const boost::intrusive_ptr<PersistableMessage>& msg);
+ void destroy(PersistableMessage& msg);
+ void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, const std::string& data);
+ void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data,
+ uint64_t offset, uint32_t length);
+
+ void enqueue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+ void dequeue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+ uint32_t outstandingQueueAIO(const PersistableQueue& queue);
+ void flush(const qpid::broker::PersistableQueue& queue);
+ bool isNull() const;
+
+ ~MessageStoreModule();
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Messages.h b/qpid/cpp/src/qpid/broker/Messages.h
new file mode 100644
index 0000000000..0d75417640
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Messages.h
@@ -0,0 +1,117 @@
+#ifndef QPID_BROKER_MESSAGES_H
+#define QPID_BROKER_MESSAGES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace framing {
+class SequenceNumber;
+}
+namespace broker {
+struct QueuedMessage;
+
+/**
+ * This interface abstracts out the access to the messages held for
+ * delivery by a Queue instance.
+ */
+class Messages
+{
+ public:
+ typedef boost::function1<void, QueuedMessage&> Functor;
+ typedef boost::function1<bool, QueuedMessage&> Predicate;
+
+ virtual ~Messages() {}
+ /**
+ * @return the number of messages available for delivery.
+ */
+ virtual size_t size() = 0;
+ /**
+ * @return true if there are no messages for delivery, false otherwise
+ */
+ virtual bool empty() = 0;
+
+ /**
+ * Re-inserts a message back into its original position - used
+ * when requeing released messages.
+ */
+ virtual void reinsert(const QueuedMessage&) = 0;
+ /**
+ * Remove the message at the specified position, returning true if
+ * found, false otherwise. The removed message is passed back via
+ * the second parameter.
+ */
+ virtual bool remove(const framing::SequenceNumber&, QueuedMessage&) = 0;
+ /**
+ * Find the message at the specified position, returning true if
+ * found, false otherwise. The matched message is passed back via
+ * the second parameter.
+ */
+ virtual bool find(const framing::SequenceNumber&, QueuedMessage&) = 0;
+ /**
+ * Return the next message to be given to a browsing subscrption
+ * that has reached the specified poisition. The next messages is
+ * passed back via the second parameter.
+ *
+ * @return true if there is another message, false otherwise.
+ */
+ virtual bool next(const framing::SequenceNumber&, QueuedMessage&) = 0;
+
+ /**
+ * Note: Caller is responsible for ensuring that there is a front
+ * (e.g. empty() returns false)
+ *
+ * @return the next message to be delivered
+ */
+ virtual QueuedMessage& front() = 0;
+ /**
+ * Removes the front message
+ */
+ virtual void pop() = 0;
+ /**
+ * @return true if there is a mesage to be delivered - in which
+ * case that message will be returned via the parameter and
+ * removed - otherwise false.
+ */
+ virtual bool pop(QueuedMessage&) = 0;
+ /**
+ * Pushes a message to the back of the 'queue'. For some types of
+ * queue this may cause another message to be removed; if that is
+ * the case the method will return true and the removed message
+ * will be passed out via the second parameter.
+ */
+ virtual bool push(const QueuedMessage& added, QueuedMessage& removed) = 0;
+
+ /**
+ * Apply the functor to each message held
+ */
+ virtual void foreach(Functor) = 0;
+ /**
+ * Remove every message held that for which the specified
+ * predicate returns true
+ */
+ virtual void removeIf(Predicate) = 0;
+ private:
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_MESSAGES_H*/
diff --git a/qpid/cpp/src/qpid/broker/NameGenerator.cpp b/qpid/cpp/src/qpid/broker/NameGenerator.cpp
new file mode 100644
index 0000000000..e7f193d546
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/NameGenerator.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/NameGenerator.h"
+#include <sstream>
+
+using namespace qpid::broker;
+
+NameGenerator::NameGenerator(const std::string& _base) : base(_base), counter(1) {}
+
+std::string NameGenerator::generate(){
+ std::stringstream ss;
+ ss << base << counter++;
+ return ss.str();
+}
diff --git a/qpid/cpp/src/qpid/broker/NameGenerator.h b/qpid/cpp/src/qpid/broker/NameGenerator.h
new file mode 100644
index 0000000000..6ea25c9797
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/NameGenerator.h
@@ -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.
+ *
+ */
+#ifndef _NameGenerator_
+#define _NameGenerator_
+
+#include <string>
+
+namespace qpid {
+ namespace broker {
+ class NameGenerator{
+ const std::string base;
+ unsigned int counter;
+ public:
+ NameGenerator(const std::string& base);
+ std::string generate();
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.cpp b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp
new file mode 100644
index 0000000000..43f600eaf1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/MessageStoreModule.h"
+#include "qpid/broker/RecoveryManager.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/reply_exceptions.h"
+
+#include <iostream>
+
+using boost::intrusive_ptr;
+
+namespace qpid{
+namespace broker{
+
+const std::string nullxid = "";
+
+class SimpleDummyCtxt : public TransactionContext {};
+
+class DummyCtxt : public TPCTransactionContext
+{
+ const std::string xid;
+public:
+ DummyCtxt(const std::string& _xid) : xid(_xid) {}
+ static std::string getXid(TransactionContext& ctxt)
+ {
+ DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt));
+ return c ? c->xid : nullxid;
+ }
+};
+
+NullMessageStore::NullMessageStore() : nextPersistenceId(1) {}
+
+bool NullMessageStore::init(const Options* /*options*/) {return true;}
+
+void NullMessageStore::truncateInit(const bool /*pushDownStoreFiles*/) {}
+
+void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/)
+{
+ queue.setPersistenceId(nextPersistenceId++);
+}
+
+void NullMessageStore::destroy(PersistableQueue&) {}
+
+void NullMessageStore::create(const PersistableExchange& exchange, const framing::FieldTable& /*args*/)
+{
+ exchange.setPersistenceId(nextPersistenceId++);
+}
+
+void NullMessageStore::destroy(const PersistableExchange& ) {}
+
+void NullMessageStore::bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){}
+
+void NullMessageStore::unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){}
+
+void NullMessageStore::create(const PersistableConfig& config)
+{
+ config.setPersistenceId(nextPersistenceId++);
+}
+
+void NullMessageStore::destroy(const PersistableConfig&) {}
+
+void NullMessageStore::recover(RecoveryManager&) {}
+
+void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&) {}
+
+void NullMessageStore::destroy(PersistableMessage&) {}
+
+void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const std::string&) {}
+
+void NullMessageStore::loadContent(const qpid::broker::PersistableQueue&,
+ const intrusive_ptr<const PersistableMessage>&,
+ std::string&, uint64_t, uint32_t)
+{
+ throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled");
+}
+
+void NullMessageStore::enqueue(TransactionContext*,
+ const intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue&)
+{
+ msg->enqueueComplete();
+}
+
+void NullMessageStore::dequeue(TransactionContext*,
+ const intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue&)
+{
+ msg->dequeueComplete();
+}
+
+void NullMessageStore::flush(const qpid::broker::PersistableQueue&) {}
+
+uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) {
+ return 0;
+}
+
+std::auto_ptr<TransactionContext> NullMessageStore::begin()
+{
+ return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt());
+}
+
+std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string& xid)
+{
+ return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid));
+}
+
+void NullMessageStore::prepare(TPCTransactionContext& ctxt)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
+ prepared.insert(DummyCtxt::getXid(ctxt));
+}
+
+void NullMessageStore::commit(TransactionContext& ctxt)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
+ prepared.erase(DummyCtxt::getXid(ctxt));
+}
+
+void NullMessageStore::abort(TransactionContext& ctxt)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
+ prepared.erase(DummyCtxt::getXid(ctxt));
+}
+
+void NullMessageStore::collectPreparedXids(std::set<std::string>& out)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
+ out.insert(prepared.begin(), prepared.end());
+}
+
+bool NullMessageStore::isNull() const
+{
+ return true;
+}
+
+bool NullMessageStore::isNullStore(const MessageStore* store)
+{
+ const MessageStoreModule* wrapper = dynamic_cast<const MessageStoreModule*>(store);
+ if (wrapper) {
+ return wrapper->isNull();
+ } else {
+ const NullMessageStore* test = dynamic_cast<const NullMessageStore*>(store);
+ return test && test->isNull();
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.h b/qpid/cpp/src/qpid/broker/NullMessageStore.h
new file mode 100644
index 0000000000..c6f402662e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/NullMessageStore.h
@@ -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.
+ *
+ */
+#ifndef _NullMessageStore_
+#define _NullMessageStore_
+
+#include <set>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * A null implementation of the MessageStore interface
+ */
+class QPID_BROKER_CLASS_EXTERN NullMessageStore : public MessageStore
+{
+ std::set<std::string> prepared;
+ uint64_t nextPersistenceId;
+ qpid::sys::Mutex lock;
+ public:
+ QPID_BROKER_EXTERN NullMessageStore();
+
+ QPID_BROKER_EXTERN virtual bool init(const Options* options);
+ QPID_BROKER_EXTERN virtual void truncateInit(const bool pushDownStoreFiles = false);
+ QPID_BROKER_EXTERN virtual std::auto_ptr<TransactionContext> begin();
+ QPID_BROKER_EXTERN virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
+ QPID_BROKER_EXTERN virtual void prepare(TPCTransactionContext& txn);
+ QPID_BROKER_EXTERN virtual void commit(TransactionContext& txn);
+ QPID_BROKER_EXTERN virtual void abort(TransactionContext& txn);
+ QPID_BROKER_EXTERN virtual void collectPreparedXids(std::set<std::string>& xids);
+
+ QPID_BROKER_EXTERN virtual void create(PersistableQueue& queue,
+ const framing::FieldTable& args);
+ QPID_BROKER_EXTERN virtual void destroy(PersistableQueue& queue);
+ QPID_BROKER_EXTERN virtual void create(const PersistableExchange& exchange,
+ const framing::FieldTable& args);
+ QPID_BROKER_EXTERN virtual void destroy(const PersistableExchange& exchange);
+
+ QPID_BROKER_EXTERN virtual void bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args);
+ QPID_BROKER_EXTERN virtual void unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args);
+ QPID_BROKER_EXTERN virtual void create(const PersistableConfig& config);
+ QPID_BROKER_EXTERN virtual void destroy(const PersistableConfig& config);
+ QPID_BROKER_EXTERN virtual void recover(RecoveryManager& queues);
+ QPID_BROKER_EXTERN virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg);
+ QPID_BROKER_EXTERN virtual void destroy(PersistableMessage& msg);
+ QPID_BROKER_EXTERN virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data);
+ QPID_BROKER_EXTERN virtual void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+ QPID_BROKER_EXTERN virtual void enqueue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+ QPID_BROKER_EXTERN virtual void dequeue(TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+ QPID_BROKER_EXTERN virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue);
+ QPID_BROKER_EXTERN virtual void flush(const qpid::broker::PersistableQueue& queue);
+ ~NullMessageStore(){}
+
+ QPID_BROKER_EXTERN virtual bool isNull() const;
+ static bool isNullStore(const MessageStore*);
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/OwnershipToken.h b/qpid/cpp/src/qpid/broker/OwnershipToken.h
new file mode 100644
index 0000000000..effd2f5b3c
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/OwnershipToken.h
@@ -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.
+ *
+ */
+#ifndef _OwnershipToken_
+#define _OwnershipToken_
+
+namespace qpid {
+namespace broker {
+
+class ConnectionToken;
+
+class OwnershipToken{
+public:
+ virtual bool isLocal(const ConnectionToken* t) const = 0;
+ virtual ~OwnershipToken(){}
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Persistable.h b/qpid/cpp/src/qpid/broker/Persistable.h
new file mode 100644
index 0000000000..36499c7a1a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Persistable.h
@@ -0,0 +1,63 @@
+#ifndef _broker_Persistable_h
+#define _broker_Persistable_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Base class for all persistable objects
+ */
+class Persistable : public RefCounted
+{
+public:
+ /**
+ * Allows the store to attach its own identifier to this object
+ */
+ virtual void setPersistenceId(uint64_t id) const = 0;
+ /**
+ * Returns any identifier the store may have attached to this
+ * object
+ */
+ virtual uint64_t getPersistenceId() const = 0;
+ /**
+ * Encodes the persistable state of this object into the supplied
+ * buffer
+ */
+ virtual void encode(framing::Buffer& buffer) const = 0;
+ /**
+ * @returns the size of the buffer needed to encode this object
+ */
+ virtual uint32_t encodedSize() const = 0;
+
+ virtual ~Persistable() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/PersistableConfig.h b/qpid/cpp/src/qpid/broker/PersistableConfig.h
new file mode 100644
index 0000000000..8ddb84d129
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PersistableConfig.h
@@ -0,0 +1,45 @@
+#ifndef _broker_PersistableConfig_h
+#define _broker_PersistableConfig_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include "qpid/broker/Persistable.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface used by general-purpose persistable configuration for
+ * the message store.
+ */
+class PersistableConfig : public Persistable
+{
+public:
+ virtual const std::string& getName() const = 0;
+ virtual ~PersistableConfig() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/PersistableExchange.h b/qpid/cpp/src/qpid/broker/PersistableExchange.h
new file mode 100644
index 0000000000..e1a0853247
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PersistableExchange.h
@@ -0,0 +1,45 @@
+#ifndef _broker_PersistableExchange_h
+#define _broker_PersistableExchange_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include "qpid/broker/Persistable.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface exchanges must expose to the MessageStore in order to be
+ * persistable.
+ */
+class PersistableExchange : public Persistable
+{
+public:
+ virtual const std::string& getName() const = 0;
+ virtual ~PersistableExchange() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp
new file mode 100644
index 0000000000..7ba28eb293
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp
@@ -0,0 +1,161 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include "qpid/broker/PersistableMessage.h"
+#include "qpid/broker/MessageStore.h"
+#include <iostream>
+
+using namespace qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+class MessageStore;
+
+PersistableMessage::~PersistableMessage() {}
+
+PersistableMessage::PersistableMessage() :
+ asyncDequeueCounter(0),
+ store(0)
+{}
+
+void PersistableMessage::flush()
+{
+ syncList copy;
+ {
+ sys::ScopedLock<sys::Mutex> l(storeLock);
+ if (store) {
+ copy = synclist;
+ } else {
+ return;//early exit as nothing to do
+ }
+ }
+ for (syncList::iterator i = copy.begin(); i != copy.end(); ++i) {
+ PersistableQueue::shared_ptr q(i->lock());
+ if (q) {
+ q->flush();
+ }
+ }
+}
+
+void PersistableMessage::setContentReleased()
+{
+ contentReleaseState.released = true;
+}
+
+bool PersistableMessage::isContentReleased() const
+{
+ return contentReleaseState.released;
+}
+
+
+bool PersistableMessage::isStoredOnQueue(PersistableQueue::shared_ptr queue){
+ if (store && (queue->getPersistenceId()!=0)) {
+ for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) {
+ PersistableQueue::shared_ptr q(i->lock());
+ if (q && q->getPersistenceId() == queue->getPersistenceId()) return true;
+ }
+ }
+ return false;
+}
+
+
+void PersistableMessage::addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store) {
+ if (_store){
+ sys::ScopedLock<sys::Mutex> l(storeLock);
+ store = _store;
+ boost::weak_ptr<PersistableQueue> q(queue);
+ synclist.push_back(q);
+ }
+}
+
+void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) {
+ addToSyncList(queue, _store);
+ enqueueStart();
+}
+
+bool PersistableMessage::isDequeueComplete() {
+ sys::ScopedLock<sys::Mutex> l(asyncDequeueLock);
+ return asyncDequeueCounter == 0;
+}
+
+void PersistableMessage::dequeueComplete() {
+ bool notify = false;
+ {
+ sys::ScopedLock<sys::Mutex> l(asyncDequeueLock);
+ if (asyncDequeueCounter > 0) {
+ if (--asyncDequeueCounter == 0) {
+ notify = true;
+ }
+ }
+ }
+ if (notify) allDequeuesComplete();
+}
+
+void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) {
+ if (_store){
+ sys::ScopedLock<sys::Mutex> l(storeLock);
+ store = _store;
+ boost::weak_ptr<PersistableQueue> q(queue);
+ synclist.push_back(q);
+ }
+ dequeueAsync();
+}
+
+void PersistableMessage::dequeueAsync() {
+ sys::ScopedLock<sys::Mutex> l(asyncDequeueLock);
+ asyncDequeueCounter++;
+}
+
+PersistableMessage::ContentReleaseState::ContentReleaseState() : blocked(false), requested(false), released(false) {}
+
+void PersistableMessage::setStore(MessageStore* s)
+{
+ store = s;
+}
+
+void PersistableMessage::requestContentRelease()
+{
+ contentReleaseState.requested = true;
+}
+void PersistableMessage::blockContentRelease()
+{
+ contentReleaseState.blocked = true;
+}
+bool PersistableMessage::checkContentReleasable()
+{
+ return contentReleaseState.requested && !contentReleaseState.blocked;
+}
+
+bool PersistableMessage::isContentReleaseBlocked()
+{
+ return contentReleaseState.blocked;
+}
+
+bool PersistableMessage::isContentReleaseRequested()
+{
+ return contentReleaseState.requested;
+}
+
+}}
+
+
diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h
new file mode 100644
index 0000000000..d29c2c45b4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h
@@ -0,0 +1,143 @@
+#ifndef _broker_PersistableMessage_h
+#define _broker_PersistableMessage_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Persistable.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/AsyncCompletion.h"
+
+namespace qpid {
+namespace broker {
+
+class MessageStore;
+
+/**
+ * Base class for persistable messages.
+ */
+class PersistableMessage : public Persistable
+{
+ typedef std::list< boost::weak_ptr<PersistableQueue> > syncList;
+ sys::Mutex asyncDequeueLock;
+ sys::Mutex storeLock;
+
+ /**
+ * "Ingress" messages == messages sent _to_ the broker.
+ * Tracks the number of outstanding asynchronous operations that must
+ * complete before an inbound message can be considered fully received by the
+ * broker. E.g. all enqueues have completed, the message has been written
+ * to store, credit has been replenished, etc. Once all outstanding
+ * operations have completed, the transfer of this message from the client
+ * may be considered complete.
+ */
+ AsyncCompletion ingressCompletion;
+
+ /**
+ * Tracks the number of outstanding asynchronous dequeue
+ * operations. When the message is dequeued asynchronously the
+ * count is incremented; when that dequeue completes it is
+ * decremented. Thus when it is 0, there are no outstanding
+ * dequeues.
+ */
+ int asyncDequeueCounter;
+
+ void dequeueAsync();
+
+ syncList synclist;
+ struct ContentReleaseState
+ {
+ bool blocked;
+ bool requested;
+ bool released;
+
+ ContentReleaseState();
+ };
+ ContentReleaseState contentReleaseState;
+
+ protected:
+ /** Called when all dequeues are complete for this message. */
+ virtual void allDequeuesComplete() = 0;
+
+ void setContentReleased();
+
+ MessageStore* store;
+
+
+ public:
+ typedef boost::shared_ptr<PersistableMessage> shared_ptr;
+
+ /**
+ * @returns the size of the headers when encoded
+ */
+ virtual uint32_t encodedHeaderSize() const = 0;
+
+ virtual ~PersistableMessage();
+
+ PersistableMessage();
+
+ void flush();
+
+ QPID_BROKER_EXTERN bool isContentReleased() const;
+
+ QPID_BROKER_EXTERN void setStore(MessageStore*);
+ void requestContentRelease();
+ void blockContentRelease();
+ bool checkContentReleasable();
+ bool isContentReleaseBlocked();
+ bool isContentReleaseRequested();
+
+ virtual QPID_BROKER_EXTERN bool isPersistent() const = 0;
+
+ /** track the progress of a message received by the broker - see ingressCompletion above */
+ QPID_BROKER_INLINE_EXTERN bool isIngressComplete() { return ingressCompletion.isDone(); }
+ QPID_BROKER_INLINE_EXTERN AsyncCompletion& getIngressCompletion() { return ingressCompletion; }
+
+ QPID_BROKER_INLINE_EXTERN void enqueueStart() { ingressCompletion.startCompleter(); }
+ QPID_BROKER_INLINE_EXTERN void enqueueComplete() { ingressCompletion.finishCompleter(); }
+
+ QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue,
+ MessageStore* _store);
+
+
+ QPID_BROKER_EXTERN bool isDequeueComplete();
+
+ QPID_BROKER_EXTERN void dequeueComplete();
+
+ QPID_BROKER_EXTERN void dequeueAsync(PersistableQueue::shared_ptr queue,
+ MessageStore* _store);
+
+ bool isStoredOnQueue(PersistableQueue::shared_ptr queue);
+
+ void addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store);
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/PersistableQueue.h b/qpid/cpp/src/qpid/broker/PersistableQueue.h
new file mode 100644
index 0000000000..655d26bc74
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PersistableQueue.h
@@ -0,0 +1,78 @@
+#ifndef _broker_PersistableQueue_h
+#define _broker_PersistableQueue_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include "qpid/broker/Persistable.h"
+#include "qpid/management/Manageable.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+
+/**
+* Empty class to be used by any module that wanted to set an external per queue store into
+* persistableQueue
+*/
+
+class ExternalQueueStore : public management::Manageable
+{
+public:
+ virtual ~ExternalQueueStore() {};
+
+};
+
+
+/**
+ * The interface queues must expose to the MessageStore in order to be
+ * persistable.
+ */
+class PersistableQueue : public Persistable
+{
+public:
+ typedef boost::shared_ptr<PersistableQueue> shared_ptr;
+
+ virtual const std::string& getName() const = 0;
+ virtual ~PersistableQueue() {
+ if (externalQueueStore)
+ delete externalQueueStore;
+ };
+
+ virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0;
+ virtual void flush() = 0;
+
+ inline ExternalQueueStore* getExternalQueueStore() const {return externalQueueStore;};
+
+ PersistableQueue():externalQueueStore(NULL){
+ };
+
+protected:
+ ExternalQueueStore* externalQueueStore;
+
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/PriorityQueue.cpp b/qpid/cpp/src/qpid/broker/PriorityQueue.cpp
new file mode 100644
index 0000000000..e07e73d323
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PriorityQueue.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/PriorityQueue.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/framing/reply_exceptions.h"
+#include <cmath>
+
+namespace qpid {
+namespace broker {
+
+PriorityQueue::PriorityQueue(int l) :
+ levels(l),
+ messages(levels, Deque()),
+ frontLevel(0), haveFront(false), cached(false) {}
+
+size_t PriorityQueue::size()
+{
+ size_t total(0);
+ for (int i = 0; i < levels; ++i) {
+ total += messages[i].size();
+ }
+ return total;
+}
+
+bool PriorityQueue::empty()
+{
+ for (int i = 0; i < levels; ++i) {
+ if (!messages[i].empty()) return false;
+ }
+ return true;
+}
+
+void PriorityQueue::reinsert(const QueuedMessage& message)
+{
+ uint p = getPriorityLevel(message);
+ messages[p].insert(lower_bound(messages[p].begin(), messages[p].end(), message), message);
+ clearCache();
+}
+
+bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message, bool remove)
+{
+ QueuedMessage comp;
+ comp.position = position;
+ for (int i = 0; i < levels; ++i) {
+ if (!messages[i].empty()) {
+ unsigned long diff = position.getValue() - messages[i].front().position.getValue();
+ long maxEnd = diff < messages[i].size() ? diff : messages[i].size();
+ Deque::iterator l = lower_bound(messages[i].begin(),messages[i].begin()+maxEnd,comp);
+ if (l != messages[i].end() && l->position == position) {
+ message = *l;
+ if (remove) {
+ messages[i].erase(l);
+ clearCache();
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool PriorityQueue::remove(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ return find(position, message, true);
+}
+
+bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ return find(position, message, false);
+}
+
+bool PriorityQueue::next(const framing::SequenceNumber& position, QueuedMessage& message)
+{
+ QueuedMessage match;
+ match.position = position+1;
+ Deque::iterator lowest;
+ bool found = false;
+ for (int i = 0; i < levels; ++i) {
+ Deque::iterator m = lower_bound(messages[i].begin(), messages[i].end(), match);
+ if (m != messages[i].end()) {
+ if (m->position == match.position) {
+ message = *m;
+ return true;
+ } else if (!found || m->position < lowest->position) {
+ lowest = m;
+ found = true;
+ }
+ }
+ }
+ if (found) {
+ message = *lowest;
+ }
+ return found;
+}
+
+QueuedMessage& PriorityQueue::front()
+{
+ if (checkFront()) {
+ return messages[frontLevel].front();
+ } else {
+ throw qpid::framing::InternalErrorException(QPID_MSG("No message available"));
+ }
+}
+
+bool PriorityQueue::pop(QueuedMessage& message)
+{
+ if (checkFront()) {
+ message = messages[frontLevel].front();
+ messages[frontLevel].pop_front();
+ clearCache();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void PriorityQueue::pop()
+{
+ QueuedMessage dummy;
+ pop(dummy);
+}
+
+bool PriorityQueue::push(const QueuedMessage& added, QueuedMessage& /*not needed*/)
+{
+ messages[getPriorityLevel(added)].push_back(added);
+ clearCache();
+ return false;//adding a message never causes one to be removed for deque
+}
+
+void PriorityQueue::foreach(Functor f)
+{
+ for (int i = 0; i < levels; ++i) {
+ std::for_each(messages[i].begin(), messages[i].end(), f);
+ }
+}
+
+void PriorityQueue::removeIf(Predicate p)
+{
+ for (int priority = 0; priority < levels; ++priority) {
+ for (Deque::iterator i = messages[priority].begin(); i != messages[priority].end();) {
+ if (p(*i)) {
+ i = messages[priority].erase(i);
+ clearCache();
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+uint PriorityQueue::getPriorityLevel(const QueuedMessage& m) const
+{
+ uint priority = m.payload->getPriority();
+ //Use AMQP 0-10 approach to mapping priorities to a fixed level
+ //(see rule priority-level-implementation)
+ const uint firstLevel = 5 - uint(std::min(5.0, std::ceil((double) levels/2.0)));
+ if (priority <= firstLevel) return 0;
+ return std::min(priority - firstLevel, (uint)levels-1);
+}
+
+void PriorityQueue::clearCache()
+{
+ cached = false;
+}
+
+bool PriorityQueue::findFrontLevel(uint& l, PriorityLevels& m)
+{
+ for (int p = levels-1; p >= 0; --p) {
+ if (!m[p].empty()) {
+ l = p;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PriorityQueue::checkFront()
+{
+ if (!cached) {
+ haveFront = findFrontLevel(frontLevel, messages);
+ cached = true;
+ }
+ return haveFront;
+}
+
+uint PriorityQueue::getPriority(const QueuedMessage& message)
+{
+ const PriorityQueue* queue = dynamic_cast<const PriorityQueue*>(&(message.queue->getMessages()));
+ if (queue) return queue->getPriorityLevel(message);
+ else return 0;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/PriorityQueue.h b/qpid/cpp/src/qpid/broker/PriorityQueue.h
new file mode 100644
index 0000000000..4bf9d26a9d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/PriorityQueue.h
@@ -0,0 +1,78 @@
+#ifndef QPID_BROKER_PRIORITYQUEUE_H
+#define QPID_BROKER_PRIORITYQUEUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Messages.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <deque>
+#include <vector>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Basic priority queue with a configurable number of recognised
+ * priority levels. This is implemented as a separate deque per
+ * priority level. Browsing is FIFO not priority order.
+ */
+class PriorityQueue : public Messages
+{
+ public:
+ PriorityQueue(int levels);
+ virtual ~PriorityQueue() {}
+ size_t size();
+ bool empty();
+
+ void reinsert(const QueuedMessage&);
+ bool remove(const framing::SequenceNumber&, QueuedMessage&);
+ bool find(const framing::SequenceNumber&, QueuedMessage&);
+ bool next(const framing::SequenceNumber&, QueuedMessage&);
+
+ QueuedMessage& front();
+ void pop();
+ bool pop(QueuedMessage&);
+ bool push(const QueuedMessage& added, QueuedMessage& removed);
+
+ void foreach(Functor);
+ void removeIf(Predicate);
+ static uint getPriority(const QueuedMessage&);
+ protected:
+ typedef std::deque<QueuedMessage> Deque;
+ typedef std::vector<Deque> PriorityLevels;
+ virtual bool findFrontLevel(uint& p, PriorityLevels&);
+
+ const int levels;
+ private:
+ PriorityLevels messages;
+ uint frontLevel;
+ bool haveFront;
+ bool cached;
+
+ bool find(const framing::SequenceNumber&, QueuedMessage&, bool remove);
+ uint getPriorityLevel(const QueuedMessage&) const;
+ void clearCache();
+ bool checkFront();
+};
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_PRIORITYQUEUE_H*/
diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp
new file mode 100644
index 0000000000..789ad581f5
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Queue.cpp
@@ -0,0 +1,1225 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Fairshare.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/LegacyLVQ.h"
+#include "qpid/broker/MessageDeque.h"
+#include "qpid/broker/MessageMap.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/ThresholdAlerts.h"
+
+#include "qpid/StringUtils.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/ClusterSafe.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h"
+#include "qmf/org/apache/qpid/broker/ArgsQueueReroute.h"
+
+#include <iostream>
+#include <algorithm>
+#include <functional>
+
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+using namespace qpid::framing;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+using std::for_each;
+using std::mem_fun;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+
+namespace
+{
+const std::string qpidMaxSize("qpid.max_size");
+const std::string qpidMaxCount("qpid.max_count");
+const std::string qpidNoLocal("no-local");
+const std::string qpidTraceIdentity("qpid.trace.id");
+const std::string qpidTraceExclude("qpid.trace.exclude");
+const std::string qpidLastValueQueueKey("qpid.last_value_queue_key");
+const std::string qpidLastValueQueue("qpid.last_value_queue");
+const std::string qpidLastValueQueueNoBrowse("qpid.last_value_queue_no_browse");
+const std::string qpidPersistLastNode("qpid.persist_last_node");
+const std::string qpidVQMatchProperty("qpid.LVQ_key");
+const std::string qpidQueueEventGeneration("qpid.queue_event_generation");
+const std::string qpidAutoDeleteTimeout("qpid.auto_delete_timeout");
+//following feature is not ready for general use as it doesn't handle
+//the case where a message is enqueued on more than one queue well enough:
+const std::string qpidInsertSequenceNumbers("qpid.insert_sequence_numbers");
+
+const int ENQUEUE_ONLY=1;
+const int ENQUEUE_AND_DEQUEUE=2;
+}
+
+Queue::Queue(const string& _name, bool _autodelete,
+ MessageStore* const _store,
+ const OwnershipToken* const _owner,
+ Manageable* parent,
+ Broker* b) :
+
+ name(_name),
+ autodelete(_autodelete),
+ store(_store),
+ owner(_owner),
+ consumerCount(0),
+ exclusive(0),
+ noLocal(false),
+ persistLastNode(false),
+ inLastNodeFailure(false),
+ messages(new MessageDeque()),
+ persistenceId(0),
+ policyExceeded(false),
+ mgmtObject(0),
+ eventMode(0),
+ insertSeqNo(0),
+ broker(b),
+ deleted(false),
+ barrier(*this),
+ autoDeleteTimeout(0)
+{
+ if (parent != 0 && broker != 0) {
+ ManagementAgent* agent = broker->getManagementAgent();
+
+ if (agent != 0) {
+ mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete, _owner != 0);
+ agent->addObject(mgmtObject, 0, store != 0);
+ }
+ }
+}
+
+Queue::~Queue()
+{
+ if (mgmtObject != 0)
+ mgmtObject->resourceDestroy();
+}
+
+bool isLocalTo(const OwnershipToken* token, boost::intrusive_ptr<Message>& msg)
+{
+ return token && token->isLocal(msg->getPublisher());
+}
+
+bool Queue::isLocal(boost::intrusive_ptr<Message>& msg)
+{
+ //message is considered local if it was published on the same
+ //connection as that of the session which declared this queue
+ //exclusive (owner) or which has an exclusive subscription
+ //(exclusive)
+ return noLocal && (isLocalTo(owner, msg) || isLocalTo(exclusive, msg));
+}
+
+bool Queue::isExcluded(boost::intrusive_ptr<Message>& msg)
+{
+ return traceExclude.size() && msg->isExcluded(traceExclude);
+}
+
+void Queue::deliver(boost::intrusive_ptr<Message> msg){
+ // Check for deferred delivery in a cluster.
+ if (broker && broker->deferDelivery(name, msg))
+ return;
+ if (msg->isImmediate() && getConsumerCount() == 0) {
+ if (alternateExchange) {
+ DeliverableMessage deliverable(msg);
+ alternateExchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders());
+ }
+ } else if (isLocal(msg)) {
+ //drop message
+ QPID_LOG(info, "Dropping 'local' message from " << getName());
+ } else if (isExcluded(msg)) {
+ //drop message
+ QPID_LOG(info, "Dropping excluded message from " << getName());
+ } else {
+ enqueue(0, msg);
+ push(msg);
+ QPID_LOG(debug, "Message " << msg << " enqueued on " << name);
+ }
+}
+
+void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg)
+{
+ if (policy.get()) policy->recoverEnqueued(msg);
+}
+
+void Queue::recover(boost::intrusive_ptr<Message>& msg){
+ if (policy.get()) policy->recoverEnqueued(msg);
+
+ push(msg, true);
+ if (store){
+ // setup synclist for recovered messages, so they don't get re-stored on lastNodeFailure
+ msg->addToSyncList(shared_from_this(), store);
+ }
+
+ if (store && (!msg->isContentLoaded() || msg->checkContentReleasable())) {
+ //content has not been loaded, need to ensure that lazy loading mode is set:
+ //TODO: find a nicer way to do this
+ msg->releaseContent(store);
+ // NOTE: The log message in this section are used for flow-to-disk testing (which checks the log for the
+ // presence of this message). Do not change this without also checking these tests.
+ QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" <<
+ std::hex << msg->getPersistenceId() << std::dec << ": Content released after recovery");
+ }
+}
+
+void Queue::process(boost::intrusive_ptr<Message>& msg){
+ push(msg);
+ if (mgmtObject != 0){
+ mgmtObject->inc_msgTxnEnqueues ();
+ mgmtObject->inc_byteTxnEnqueues (msg->contentSize ());
+ }
+}
+
+void Queue::requeue(const QueuedMessage& msg){
+ assertClusterSafe();
+ QueueListeners::NotificationSet copy;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (!isEnqueued(msg)) return;
+ messages->reinsert(msg);
+ listeners.populate(copy);
+
+ // for persistLastNode - don't force a message twice to disk, but force it if no force before
+ if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) {
+ msg.payload->forcePersistent();
+ if (msg.payload->isForcedPersistent() ){
+ boost::intrusive_ptr<Message> payload = msg.payload;
+ enqueue(0, payload);
+ }
+ }
+ }
+ copy.notify();
+}
+
+bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message)
+{
+ Mutex::ScopedLock locker(messageLock);
+ assertClusterSafe();
+ QPID_LOG(debug, "Attempting to acquire message at " << position);
+ if (messages->remove(position, message)) {
+ QPID_LOG(debug, "Acquired message at " << position << " from " << name);
+ return true;
+ } else {
+ QPID_LOG(debug, "Could not acquire message at " << position << " from " << name << "; no message at that position");
+ return false;
+ }
+}
+
+bool Queue::acquire(const QueuedMessage& msg) {
+ QueuedMessage copy = msg;
+ return acquireMessageAt(msg.position, copy);
+}
+
+void Queue::notifyListener()
+{
+ assertClusterSafe();
+ QueueListeners::NotificationSet set;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (messages->size()) {
+ listeners.populate(set);
+ }
+ }
+ set.notify();
+}
+
+bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+{
+ checkNotDeleted();
+ if (c->preAcquires()) {
+ switch (consumeNextMessage(m, c)) {
+ case CONSUMED:
+ return true;
+ case CANT_CONSUME:
+ notifyListener();//let someone else try
+ case NO_MESSAGES:
+ default:
+ return false;
+ }
+ } else {
+ return browseNextMessage(m, c);
+ }
+}
+
+Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+{
+ while (true) {
+ Mutex::ScopedLock locker(messageLock);
+ if (messages->empty()) {
+ QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'");
+ listeners.addListener(c);
+ return NO_MESSAGES;
+ } else {
+ QueuedMessage msg = messages->front();
+ if (msg.payload->hasExpired()) {
+ QPID_LOG(debug, "Message expired from queue '" << name << "'");
+ popAndDequeue();
+ continue;
+ }
+
+ if (c->filter(msg.payload)) {
+ if (c->accept(msg.payload)) {
+ m = msg;
+ pop();
+ return CONSUMED;
+ } else {
+ //message(s) are available but consumer hasn't got enough credit
+ QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
+ return CANT_CONSUME;
+ }
+ } else {
+ //consumer will never want this message
+ QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
+ return CANT_CONSUME;
+ }
+ }
+ }
+}
+
+
+bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+{
+ QueuedMessage msg(this);
+ while (seek(msg, c)) {
+ if (c->filter(msg.payload) && !msg.payload->hasExpired()) {
+ if (c->accept(msg.payload)) {
+ //consumer wants the message
+ c->position = msg.position;
+ m = msg;
+ return true;
+ } else {
+ //browser hasn't got enough credit for the message
+ QPID_LOG(debug, "Browser can't currently accept message from '" << name << "'");
+ return false;
+ }
+ } else {
+ //consumer will never want this message, continue seeking
+ c->position = msg.position;
+ QPID_LOG(debug, "Browser skipping message from '" << name << "'");
+ }
+ }
+ return false;
+}
+
+void Queue::removeListener(Consumer::shared_ptr c)
+{
+ QueueListeners::NotificationSet set;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ listeners.removeListener(c);
+ if (messages->size()) {
+ listeners.populate(set);
+ }
+ }
+ set.notify();
+}
+
+bool Queue::dispatch(Consumer::shared_ptr c)
+{
+ QueuedMessage msg(this);
+ if (getNextMessage(msg, c)) {
+ c->deliver(msg);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Find the next message
+bool Queue::seek(QueuedMessage& msg, Consumer::shared_ptr c) {
+ Mutex::ScopedLock locker(messageLock);
+ if (messages->next(c->position, msg)) {
+ return true;
+ } else {
+ listeners.addListener(c);
+ return false;
+ }
+}
+
+QueuedMessage Queue::find(SequenceNumber pos) const {
+
+ Mutex::ScopedLock locker(messageLock);
+ QueuedMessage msg;
+ messages->find(pos, msg);
+ return msg;
+}
+
+void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){
+ assertClusterSafe();
+ Mutex::ScopedLock locker(consumerLock);
+ if(exclusive) {
+ throw ResourceLockedException(
+ QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed."));
+ } else if(requestExclusive) {
+ if(consumerCount) {
+ throw ResourceLockedException(
+ QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied."));
+ } else {
+ exclusive = c->getSession();
+ }
+ }
+ consumerCount++;
+ if (mgmtObject != 0)
+ mgmtObject->inc_consumerCount ();
+ //reset auto deletion timer if necessary
+ if (autoDeleteTimeout && autoDeleteTask) {
+ autoDeleteTask->cancel();
+ }
+}
+
+void Queue::cancel(Consumer::shared_ptr c){
+ removeListener(c);
+ Mutex::ScopedLock locker(consumerLock);
+ consumerCount--;
+ if(exclusive) exclusive = 0;
+ if (mgmtObject != 0)
+ mgmtObject->dec_consumerCount ();
+}
+
+QueuedMessage Queue::get(){
+ Mutex::ScopedLock locker(messageLock);
+ QueuedMessage msg(this);
+ messages->pop(msg);
+ return msg;
+}
+
+bool collect_if_expired(std::deque<QueuedMessage>& expired, QueuedMessage& message)
+{
+ if (message.payload->hasExpired()) {
+ expired.push_back(message);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void Queue::purgeExpired()
+{
+ //As expired messages are discarded during dequeue also, only
+ //bother explicitly expiring if the rate of dequeues since last
+ //attempt is less than one per second.
+
+ if (dequeueTracker.sampleRatePerSecond() < 1) {
+ std::deque<QueuedMessage> expired;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf(boost::bind(&collect_if_expired, boost::ref(expired), _1));
+ }
+ for_each(expired.begin(), expired.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
+ }
+}
+
+/**
+ * purge - for purging all or some messages on a queue
+ * depending on the purge_request
+ *
+ * purge_request == 0 then purge all messages
+ * == N then purge N messages from queue
+ * Sometimes purge_request == 1 to unblock the top of queue
+ *
+ * The dest exchange may be supplied to re-route messages through the exchange.
+ * It is safe to re-route messages such that they arrive back on the same queue,
+ * even if the queue is ordered by priority.
+ */
+uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> dest)
+{
+ Mutex::ScopedLock locker(messageLock);
+ uint32_t purge_count = purge_request; // only comes into play if >0
+ std::deque<DeliverableMessage> rerouteQueue;
+
+ uint32_t count = 0;
+ // Either purge them all or just the some (purge_count) while the queue isn't empty.
+ while((!purge_request || purge_count--) && !messages->empty()) {
+ if (dest.get()) {
+ //
+ // If there is a destination exchange, stage the messages onto a reroute queue
+ // so they don't wind up getting purged more than once.
+ //
+ DeliverableMessage msg(messages->front().payload);
+ rerouteQueue.push_back(msg);
+ }
+ popAndDequeue();
+ count++;
+ }
+
+ //
+ // Re-route purged messages into the destination exchange. Note that there's no need
+ // to test dest.get() here because if it is NULL, the rerouteQueue will be empty.
+ //
+ while (!rerouteQueue.empty()) {
+ DeliverableMessage msg(rerouteQueue.front());
+ rerouteQueue.pop_front();
+ dest->routeWithAlternate(msg);
+ }
+
+ return count;
+}
+
+uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty) {
+ Mutex::ScopedLock locker(messageLock);
+ uint32_t move_count = qty; // only comes into play if qty >0
+ uint32_t count = 0; // count how many were moved for returning
+
+ while((!qty || move_count--) && !messages->empty()) {
+ QueuedMessage qmsg = messages->front();
+ boost::intrusive_ptr<Message> msg = qmsg.payload;
+ destq->deliver(msg); // deliver message to the destination queue
+ pop();
+ dequeue(0, qmsg);
+ count++;
+ }
+ return count;
+}
+
+void Queue::pop()
+{
+ assertClusterSafe();
+ messages->pop();
+ ++dequeueTracker;
+}
+
+void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
+ assertClusterSafe();
+ QueueListeners::NotificationSet copy;
+ QueuedMessage removed;
+ bool dequeueRequired = false;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ QueuedMessage qm(this, msg, ++sequence);
+ if (insertSeqNo) msg->getOrInsertHeaders().setInt64(seqNoKey, sequence);
+
+ dequeueRequired = messages->push(qm, removed);
+ listeners.populate(copy);
+ enqueued(qm);
+ }
+ copy.notify();
+ if (dequeueRequired) {
+ if (isRecovery) {
+ //can't issue new requests for the store until
+ //recovery is complete
+ pendingDequeues.push_back(removed);
+ } else {
+ dequeue(0, removed);
+ }
+ }
+}
+
+void isEnqueueComplete(uint32_t* result, const QueuedMessage& message)
+{
+ if (message.payload->isIngressComplete()) (*result)++;
+}
+
+/** function only provided for unit tests, or code not in critical message path */
+uint32_t Queue::getEnqueueCompleteMessageCount() const
+{
+ Mutex::ScopedLock locker(messageLock);
+ uint32_t count = 0;
+ messages->foreach(boost::bind(&isEnqueueComplete, &count, _1));
+ return count;
+}
+
+uint32_t Queue::getMessageCount() const
+{
+ Mutex::ScopedLock locker(messageLock);
+ return messages->size();
+}
+
+uint32_t Queue::getConsumerCount() const
+{
+ Mutex::ScopedLock locker(consumerLock);
+ return consumerCount;
+}
+
+bool Queue::canAutoDelete() const
+{
+ Mutex::ScopedLock locker(consumerLock);
+ return autodelete && !consumerCount && !owner;
+}
+
+void Queue::clearLastNodeFailure()
+{
+ inLastNodeFailure = false;
+}
+
+void Queue::forcePersistent(QueuedMessage& message)
+{
+ if(!message.payload->isStoredOnQueue(shared_from_this())) {
+ message.payload->forcePersistent();
+ if (message.payload->isForcedPersistent() ){
+ enqueue(0, message.payload);
+ }
+ }
+}
+
+void Queue::setLastNodeFailure()
+{
+ if (persistLastNode){
+ Mutex::ScopedLock locker(messageLock);
+ try {
+ messages->foreach(boost::bind(&Queue::forcePersistent, this, _1));
+ } catch (const std::exception& e) {
+ // Could not go into last node standing (for example journal not large enough)
+ QPID_LOG(error, "Unable to fail to last node standing for queue: " << name << " : " << e.what());
+ }
+ inLastNodeFailure = true;
+ }
+}
+
+
+// return true if store exists,
+bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck)
+{
+ ScopedUse u(barrier);
+ if (!u.acquired) return false;
+
+ if (policy.get() && !suppressPolicyCheck) {
+ std::deque<QueuedMessage> dequeues;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ policy->tryEnqueue(msg);
+ policy->getPendingDequeues(dequeues);
+ }
+ //depending on policy, may have some dequeues that need to performed without holding the lock
+ for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
+ }
+
+ if (inLastNodeFailure && persistLastNode){
+ msg->forcePersistent();
+ }
+
+ if (traceId.size()) {
+ //copy on write: take deep copy of message before modifying it
+ //as the frames may already be available for delivery on other
+ //threads
+ boost::intrusive_ptr<Message> copy(new Message(*msg));
+ msg = copy;
+ msg->addTraceId(traceId);
+ }
+
+ if ((msg->isPersistent() || msg->checkContentReleasable()) && store) {
+ // mark the message as being enqueued - the store MUST CALL msg->enqueueComplete()
+ // when it considers the message stored.
+ msg->enqueueAsync(shared_from_this(), store);
+ boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg);
+ store->enqueue(ctxt, pmsg, *this);
+ return true;
+ }
+ if (!store) {
+ //Messages enqueued on a transient queue should be prevented
+ //from having their content released as it may not be
+ //recoverable by these queue for delivery
+ msg->blockContentRelease();
+ }
+ return false;
+}
+
+void Queue::enqueueAborted(boost::intrusive_ptr<Message> msg)
+{
+ Mutex::ScopedLock locker(messageLock);
+ if (policy.get()) policy->enqueueAborted(msg);
+}
+
+// return true if store exists,
+bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
+{
+ ScopedUse u(barrier);
+ if (!u.acquired) return false;
+
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (!isEnqueued(msg)) return false;
+ if (!ctxt) {
+ dequeued(msg);
+ }
+ }
+ // This check prevents messages which have been forced persistent on one queue from dequeuing
+ // from another on which no forcing has taken place and thus causing a store error.
+ bool fp = msg.payload->isForcedPersistent();
+ if (!fp || (fp && msg.payload->isStoredOnQueue(shared_from_this()))) {
+ if ((msg.payload->isPersistent() || msg.payload->checkContentReleasable()) && store) {
+ msg.payload->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue
+ boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg.payload);
+ store->dequeue(ctxt, pmsg, *this);
+ return true;
+ }
+ }
+ return false;
+}
+
+void Queue::dequeueCommitted(const QueuedMessage& msg)
+{
+ Mutex::ScopedLock locker(messageLock);
+ dequeued(msg);
+ if (mgmtObject != 0) {
+ mgmtObject->inc_msgTxnDequeues();
+ mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize());
+ }
+}
+
+/**
+ * Removes a message from the in-memory delivery queue as well
+ * dequeing it from the logical (and persistent if applicable) queue
+ */
+void Queue::popAndDequeue()
+{
+ QueuedMessage msg = messages->front();
+ pop();
+ dequeue(0, msg);
+}
+
+/**
+ * Updates policy and management when a message has been dequeued,
+ * expects messageLock to be held
+ */
+void Queue::dequeued(const QueuedMessage& msg)
+{
+ if (policy.get()) policy->dequeued(msg);
+ mgntDeqStats(msg.payload);
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->dequeued(msg);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of dequeue for queue " << getName() << ": " << e.what());
+ }
+ }
+}
+
+
+void Queue::create(const FieldTable& _settings)
+{
+ settings = _settings;
+ if (store) {
+ store->create(*this, _settings);
+ }
+ configureImpl(_settings);
+}
+
+
+int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::string& key)
+{
+ qpid::framing::FieldTable::ValuePtr v = settings.get(key);
+ if (!v) {
+ return 0;
+ } else if (v->convertsTo<int>()) {
+ return v->get<int>();
+ } else if (v->convertsTo<std::string>()){
+ std::string s = v->get<std::string>();
+ try {
+ return boost::lexical_cast<int>(s);
+ } catch(const boost::bad_lexical_cast&) {
+ QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << s);
+ return 0;
+ }
+ } else {
+ QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << *v);
+ return 0;
+ }
+}
+
+void Queue::configure(const FieldTable& _settings)
+{
+ settings = _settings;
+ configureImpl(settings);
+}
+
+void Queue::configureImpl(const FieldTable& _settings)
+{
+ eventMode = _settings.getAsInt(qpidQueueEventGeneration);
+ if (eventMode && broker) {
+ broker->getQueueEvents().observe(*this, eventMode == ENQUEUE_ONLY);
+ }
+
+ if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK &&
+ (!store || NullMessageStore::isNullStore(store) || (broker && !(broker->getQueueEvents().isSync())) )) {
+ if ( NullMessageStore::isNullStore(store)) {
+ QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName());
+ } else if (broker && !(broker->getQueueEvents().isSync()) ) {
+ QPID_LOG(warning, "Flow to disk not valid with async Queue Events:" << getName());
+ }
+ FieldTable copy(_settings);
+ copy.erase(QueuePolicy::typeKey);
+ setPolicy(QueuePolicy::createQueuePolicy(getName(), copy));
+ } else {
+ setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings));
+ }
+ if (broker && broker->getManagementAgent()) {
+ ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings, broker->getOptions().queueThresholdEventRatio);
+ }
+
+ //set this regardless of owner to allow use of no-local with exclusive consumers also
+ noLocal = _settings.get(qpidNoLocal);
+ QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal);
+
+ std::string lvqKey = _settings.getAsString(qpidLastValueQueueKey);
+ if (lvqKey.size()) {
+ QPID_LOG(debug, "Configured queue " << getName() << " as Last Value Queue with key " << lvqKey);
+ messages = std::auto_ptr<Messages>(new MessageMap(lvqKey));
+ } else if (_settings.get(qpidLastValueQueueNoBrowse)) {
+ QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue with 'no-browse' on");
+ messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, true, broker);
+ } else if (_settings.get(qpidLastValueQueue)) {
+ QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue");
+ messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, false, broker);
+ } else {
+ std::auto_ptr<Messages> m = Fairshare::create(_settings);
+ if (m.get()) {
+ messages = m;
+ QPID_LOG(debug, "Configured queue " << getName() << " as priority queue.");
+ }
+ }
+
+ persistLastNode= _settings.get(qpidPersistLastNode);
+ if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node for: " << getName());
+
+ traceId = _settings.getAsString(qpidTraceIdentity);
+ std::string excludeList = _settings.getAsString(qpidTraceExclude);
+ if (excludeList.size()) {
+ split(traceExclude, excludeList, ", ");
+ }
+ QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId
+ << "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements");
+
+ FieldTable::ValuePtr p =_settings.get(qpidInsertSequenceNumbers);
+ if (p && p->convertsTo<std::string>()) insertSequenceNumbers(p->get<std::string>());
+
+ autoDeleteTimeout = getIntegerSetting(_settings, qpidAutoDeleteTimeout);
+ if (autoDeleteTimeout)
+ QPID_LOG(debug, "Configured queue " << getName() << " with qpid.auto_delete_timeout=" << autoDeleteTimeout);
+
+ if (mgmtObject != 0) {
+ mgmtObject->set_arguments(ManagementAgent::toMap(_settings));
+ }
+
+ QueueFlowLimit::observe(*this, _settings);
+}
+
+void Queue::destroyed()
+{
+ unbind(broker->getExchanges());
+ if (alternateExchange.get()) {
+ Mutex::ScopedLock locker(messageLock);
+ while(!messages->empty()){
+ DeliverableMessage msg(messages->front().payload);
+ alternateExchange->routeWithAlternate(msg);
+ popAndDequeue();
+ }
+ alternateExchange->decAlternateUsers();
+ }
+
+ if (store) {
+ barrier.destroy();
+ store->flush(*this);
+ store->destroy(*this);
+ store = 0;//ensure we make no more calls to the store for this queue
+ }
+ if (autoDeleteTask) autoDeleteTask = boost::intrusive_ptr<TimerTask>();
+ notifyDeleted();
+}
+
+void Queue::notifyDeleted()
+{
+ QueueListeners::ListenerSet set;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ listeners.snapshot(set);
+ deleted = true;
+ }
+ set.notifyAll();
+}
+
+void Queue::bound(const string& exchange, const string& key,
+ const FieldTable& args)
+{
+ bindings.add(exchange, key, args);
+}
+
+void Queue::unbind(ExchangeRegistry& exchanges)
+{
+ bindings.unbind(exchanges, shared_from_this());
+}
+
+void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy)
+{
+ policy = _policy;
+}
+
+const QueuePolicy* Queue::getPolicy()
+{
+ return policy.get();
+}
+
+uint64_t Queue::getPersistenceId() const
+{
+ return persistenceId;
+}
+
+void Queue::setPersistenceId(uint64_t _persistenceId) const
+{
+ if (mgmtObject != 0 && persistenceId == 0 && externalQueueStore)
+ {
+ ManagementObject* childObj = externalQueueStore->GetManagementObject();
+ if (childObj != 0)
+ childObj->setReference(mgmtObject->getObjectId());
+ }
+ persistenceId = _persistenceId;
+}
+
+void Queue::encode(Buffer& buffer) const
+{
+ buffer.putShortString(name);
+ buffer.put(settings);
+ if (policy.get()) {
+ buffer.put(*policy);
+ }
+ buffer.putShortString(alternateExchange.get() ? alternateExchange->getName() : std::string(""));
+}
+
+uint32_t Queue::encodedSize() const
+{
+ return name.size() + 1/*short string size octet*/
+ + (alternateExchange.get() ? alternateExchange->getName().size() : 0) + 1 /* short string */
+ + settings.encodedSize()
+ + (policy.get() ? (*policy).encodedSize() : 0);
+}
+
+Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer )
+{
+ string name;
+ buffer.getShortString(name);
+ FieldTable settings;
+ buffer.get(settings);
+ boost::shared_ptr<Exchange> alternate;
+ std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true, false, 0, alternate, settings, true);
+ if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) {
+ buffer.get ( *(result.first->policy) );
+ }
+ if (buffer.available()) {
+ string altExch;
+ buffer.getShortString(altExch);
+ result.first->alternateExchangeName.assign(altExch);
+ }
+
+ return result.first;
+}
+
+
+void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange)
+{
+ alternateExchange = exchange;
+ if (mgmtObject) {
+ if (exchange.get() != 0)
+ mgmtObject->set_altExchange(exchange->GetManagementObject()->getObjectId());
+ else
+ mgmtObject->clr_altExchange();
+ }
+}
+
+boost::shared_ptr<Exchange> Queue::getAlternateExchange()
+{
+ return alternateExchange;
+}
+
+void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue)
+{
+ if (broker.getQueues().destroyIf(queue->getName(),
+ boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) {
+ QPID_LOG(debug, "Auto-deleting " << queue->getName());
+ queue->destroyed();
+ }
+}
+
+struct AutoDeleteTask : qpid::sys::TimerTask
+{
+ Broker& broker;
+ Queue::shared_ptr queue;
+
+ AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime)
+ : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion"), broker(b), queue(q) {}
+
+ void fire()
+ {
+ //need to detect case where queue was used after the task was
+ //created, but then became unused again before the task fired;
+ //in this case ignore this request as there will have already
+ //been a later task added
+ tryAutoDeleteImpl(broker, queue);
+ }
+};
+
+void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue)
+{
+ if (queue->autoDeleteTimeout && queue->canAutoDelete()) {
+ AbsTime time(now(), Duration(queue->autoDeleteTimeout * TIME_SEC));
+ queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, time));
+ broker.getClusterTimer().add(queue->autoDeleteTask);
+ QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated");
+ } else {
+ tryAutoDeleteImpl(broker, queue);
+ }
+}
+
+bool Queue::isExclusiveOwner(const OwnershipToken* const o) const
+{
+ Mutex::ScopedLock locker(ownershipLock);
+ return o == owner;
+}
+
+void Queue::releaseExclusiveOwnership()
+{
+ Mutex::ScopedLock locker(ownershipLock);
+ owner = 0;
+}
+
+bool Queue::setExclusiveOwner(const OwnershipToken* const o)
+{
+ //reset auto deletion timer if necessary
+ if (autoDeleteTimeout && autoDeleteTask) {
+ autoDeleteTask->cancel();
+ }
+ Mutex::ScopedLock locker(ownershipLock);
+ if (owner) {
+ return false;
+ } else {
+ owner = o;
+ return true;
+ }
+}
+
+bool Queue::hasExclusiveOwner() const
+{
+ Mutex::ScopedLock locker(ownershipLock);
+ return owner != 0;
+}
+
+bool Queue::hasExclusiveConsumer() const
+{
+ return exclusive;
+}
+
+void Queue::setExternalQueueStore(ExternalQueueStore* inst) {
+ if (externalQueueStore!=inst && externalQueueStore)
+ delete externalQueueStore;
+ externalQueueStore = inst;
+
+ if (inst) {
+ ManagementObject* childObj = inst->GetManagementObject();
+ if (childObj != 0 && mgmtObject != 0)
+ childObj->setReference(mgmtObject->getObjectId());
+ }
+}
+
+ManagementObject* Queue::GetManagementObject (void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string& etext)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]");
+
+ switch (methodId) {
+ case _qmf::Queue::METHOD_PURGE :
+ {
+ _qmf::ArgsQueuePurge& purgeArgs = (_qmf::ArgsQueuePurge&) args;
+ purge(purgeArgs.i_request);
+ status = Manageable::STATUS_OK;
+ }
+ break;
+
+ case _qmf::Queue::METHOD_REROUTE :
+ {
+ _qmf::ArgsQueueReroute& rerouteArgs = (_qmf::ArgsQueueReroute&) args;
+ boost::shared_ptr<Exchange> dest;
+ if (rerouteArgs.i_useAltExchange)
+ dest = alternateExchange;
+ else {
+ try {
+ dest = broker->getExchanges().get(rerouteArgs.i_exchange);
+ } catch(const std::exception&) {
+ status = Manageable::STATUS_PARAMETER_INVALID;
+ etext = "Exchange not found";
+ break;
+ }
+ }
+
+ purge(rerouteArgs.i_request, dest);
+ status = Manageable::STATUS_OK;
+ }
+ break;
+ }
+
+ return status;
+}
+
+void Queue::setPosition(SequenceNumber n) {
+ Mutex::ScopedLock locker(messageLock);
+ sequence = n;
+}
+
+SequenceNumber Queue::getPosition() {
+ return sequence;
+}
+
+int Queue::getEventMode() { return eventMode; }
+
+void Queue::recoveryComplete(ExchangeRegistry& exchanges)
+{
+ // set the alternate exchange
+ if (!alternateExchangeName.empty()) {
+ try {
+ Exchange::shared_ptr ae = exchanges.get(alternateExchangeName);
+ setAlternateExchange(ae);
+ } catch (const NotFoundException&) {
+ QPID_LOG(warning, "Could not set alternate exchange \"" << alternateExchangeName << "\" on queue \"" << name << "\": exchange does not exist.");
+ }
+ }
+ //process any pending dequeues
+ for_each(pendingDequeues.begin(), pendingDequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
+ pendingDequeues.clear();
+}
+
+void Queue::insertSequenceNumbers(const std::string& key)
+{
+ seqNoKey = key;
+ insertSeqNo = !seqNoKey.empty();
+ QPID_LOG(debug, "Inserting sequence numbers as " << key);
+}
+
+void Queue::enqueued(const QueuedMessage& m)
+{
+ for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) {
+ try {
+ (*i)->enqueued(m);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of enqueue for queue " << getName() << ": " << e.what());
+ }
+ }
+ if (policy.get()) {
+ policy->enqueued(m);
+ }
+ mgntEnqStats(m.payload);
+}
+
+void Queue::updateEnqueued(const QueuedMessage& m)
+{
+ if (m.payload) {
+ boost::intrusive_ptr<Message> payload = m.payload;
+ enqueue ( 0, payload, true );
+ if (policy.get()) {
+ policy->recoverEnqueued(payload);
+ }
+ enqueued(m);
+ } else {
+ QPID_LOG(warning, "Queue informed of enqueued message that has no payload");
+ }
+}
+
+bool Queue::isEnqueued(const QueuedMessage& msg)
+{
+ return !policy.get() || policy->isEnqueued(msg);
+}
+
+QueueListeners& Queue::getListeners() { return listeners; }
+Messages& Queue::getMessages() { return *messages; }
+const Messages& Queue::getMessages() const { return *messages; }
+
+void Queue::checkNotDeleted()
+{
+ if (deleted) {
+ throw ResourceDeletedException(QPID_MSG("Queue " << getName() << " has been deleted."));
+ }
+}
+
+void Queue::addObserver(boost::shared_ptr<QueueObserver> observer)
+{
+ observers.insert(observer);
+}
+
+void Queue::flush()
+{
+ ScopedUse u(barrier);
+ if (u.acquired && store) store->flush(*this);
+}
+
+
+bool Queue::bind(boost::shared_ptr<Exchange> exchange, const std::string& key,
+ const qpid::framing::FieldTable& arguments)
+{
+ if (exchange->bind(shared_from_this(), key, &arguments)) {
+ bound(exchange->getName(), key, arguments);
+ if (exchange->isDurable() && isDurable()) {
+ store->bind(*exchange, *this, key, arguments);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+const Broker* Queue::getBroker()
+{
+ return broker;
+}
+
+
+Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {}
+
+bool Queue::UsageBarrier::acquire()
+{
+ Monitor::ScopedLock l(parent.messageLock);
+ if (parent.deleted) {
+ return false;
+ } else {
+ ++count;
+ return true;
+ }
+}
+
+void Queue::UsageBarrier::release()
+{
+ Monitor::ScopedLock l(parent.messageLock);
+ if (--count == 0) parent.messageLock.notifyAll();
+}
+
+void Queue::UsageBarrier::destroy()
+{
+ Monitor::ScopedLock l(parent.messageLock);
+ parent.deleted = true;
+ while (count) parent.messageLock.wait();
+}
diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h
new file mode 100644
index 0000000000..c4f1bcc07e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Queue.h
@@ -0,0 +1,390 @@
+#ifndef _broker_Queue_h
+#define _broker_Queue_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/OwnershipToken.h"
+#include "qpid/broker/Consumer.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Messages.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueBindings.h"
+#include "qpid/broker/QueueListeners.h"
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/broker/RateTracker.h"
+
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Queue.h"
+#include "qpid/framing/amqp_types.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <list>
+#include <vector>
+#include <memory>
+#include <deque>
+#include <set>
+#include <algorithm>
+
+namespace qpid {
+namespace broker {
+class Broker;
+class MessageStore;
+class QueueEvents;
+class QueueRegistry;
+class TransactionContext;
+class Exchange;
+
+/**
+ * The brokers representation of an amqp queue. Messages are
+ * delivered to a queue from where they can be dispatched to
+ * registered consumers or be stored until dequeued or until one
+ * or more consumers registers.
+ */
+class Queue : public boost::enable_shared_from_this<Queue>,
+ public PersistableQueue, public management::Manageable {
+
+ struct UsageBarrier
+ {
+ Queue& parent;
+ uint count;
+
+ UsageBarrier(Queue&);
+ bool acquire();
+ void release();
+ void destroy();
+ };
+
+ struct ScopedUse
+ {
+ UsageBarrier& barrier;
+ const bool acquired;
+ ScopedUse(UsageBarrier& b) : barrier(b), acquired(barrier.acquire()) {}
+ ~ScopedUse() { if (acquired) barrier.release(); }
+ };
+
+ typedef std::set< boost::shared_ptr<QueueObserver> > Observers;
+ enum ConsumeCode {NO_MESSAGES=0, CANT_CONSUME=1, CONSUMED=2};
+
+
+ const std::string name;
+ const bool autodelete;
+ MessageStore* store;
+ const OwnershipToken* owner;
+ uint32_t consumerCount;
+ OwnershipToken* exclusive;
+ bool noLocal;
+ bool persistLastNode;
+ bool inLastNodeFailure;
+ std::string traceId;
+ std::vector<std::string> traceExclude;
+ QueueListeners listeners;
+ std::auto_ptr<Messages> messages;
+ std::deque<QueuedMessage> pendingDequeues;//used to avoid dequeuing during recovery
+ mutable qpid::sys::Mutex consumerLock;
+ mutable qpid::sys::Monitor messageLock;
+ mutable qpid::sys::Mutex ownershipLock;
+ mutable uint64_t persistenceId;
+ framing::FieldTable settings;
+ std::auto_ptr<QueuePolicy> policy;
+ bool policyExceeded;
+ QueueBindings bindings;
+ std::string alternateExchangeName;
+ boost::shared_ptr<Exchange> alternateExchange;
+ framing::SequenceNumber sequence;
+ qmf::org::apache::qpid::broker::Queue* mgmtObject;
+ RateTracker dequeueTracker;
+ int eventMode;
+ Observers observers;
+ bool insertSeqNo;
+ std::string seqNoKey;
+ Broker* broker;
+ bool deleted;
+ UsageBarrier barrier;
+ int autoDeleteTimeout;
+ boost::intrusive_ptr<qpid::sys::TimerTask> autoDeleteTask;
+
+ void push(boost::intrusive_ptr<Message>& msg, bool isRecovery=false);
+ void setPolicy(std::auto_ptr<QueuePolicy> policy);
+ bool seek(QueuedMessage& msg, Consumer::shared_ptr position);
+ bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
+ ConsumeCode consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
+ bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
+ void notifyListener();
+
+ void removeListener(Consumer::shared_ptr);
+
+ bool isExcluded(boost::intrusive_ptr<Message>& msg);
+
+ void enqueued(const QueuedMessage& msg);
+ void dequeued(const QueuedMessage& msg);
+ void pop();
+ void popAndDequeue();
+ QueuedMessage getFront();
+ void forcePersistent(QueuedMessage& msg);
+ int getEventMode();
+ void configureImpl(const qpid::framing::FieldTable& settings);
+
+ inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg)
+ {
+ if (mgmtObject != 0) {
+ mgmtObject->inc_msgTotalEnqueues ();
+ mgmtObject->inc_byteTotalEnqueues (msg->contentSize ());
+ if (msg->isPersistent ()) {
+ mgmtObject->inc_msgPersistEnqueues ();
+ mgmtObject->inc_bytePersistEnqueues (msg->contentSize ());
+ }
+ }
+ }
+ inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg)
+ {
+ if (mgmtObject != 0){
+ mgmtObject->inc_msgTotalDequeues ();
+ mgmtObject->inc_byteTotalDequeues (msg->contentSize());
+ if (msg->isPersistent ()){
+ mgmtObject->inc_msgPersistDequeues ();
+ mgmtObject->inc_bytePersistDequeues (msg->contentSize());
+ }
+ }
+ }
+
+ void checkNotDeleted();
+ void notifyDeleted();
+
+ public:
+
+ typedef boost::shared_ptr<Queue> shared_ptr;
+
+ typedef std::vector<shared_ptr> vector;
+
+ QPID_BROKER_EXTERN Queue(const std::string& name,
+ bool autodelete = false,
+ MessageStore* const store = 0,
+ const OwnershipToken* const owner = 0,
+ management::Manageable* parent = 0,
+ Broker* broker = 0);
+ QPID_BROKER_EXTERN ~Queue();
+
+ QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr);
+
+ /**
+ * Used to configure a new queue and create a persistent record
+ * for it in store if required.
+ */
+ QPID_BROKER_EXTERN void create(const qpid::framing::FieldTable& settings);
+
+ /**
+ * Used to reconfigure a recovered queue (does not create
+ * persistent record in store).
+ */
+ QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings);
+ void destroyed();
+ QPID_BROKER_EXTERN void bound(const std::string& exchange,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+ //TODO: get unbind out of the public interface; only there for purposes of one unit test
+ QPID_BROKER_EXTERN void unbind(ExchangeRegistry& exchanges);
+ /**
+ * Bind self to specified exchange, and record that binding for unbinding on delete.
+ */
+ bool bind(boost::shared_ptr<Exchange> exchange, const std::string& key,
+ const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable());
+
+ QPID_BROKER_EXTERN bool acquire(const QueuedMessage& msg);
+ QPID_BROKER_EXTERN bool acquireMessageAt(const qpid::framing::SequenceNumber& position, QueuedMessage& message);
+
+ /**
+ * Delivers a message to the queue. Will record it as
+ * enqueued if persistent then process it.
+ */
+ QPID_BROKER_EXTERN void deliver(boost::intrusive_ptr<Message> msg);
+ /**
+ * Dispatches the messages immediately to a consumer if
+ * one is available or stores it for later if not.
+ */
+ QPID_BROKER_EXTERN void process(boost::intrusive_ptr<Message>& msg);
+ /**
+ * Returns a message to the in-memory queue (due to lack
+ * of acknowledegement from a receiver). If a consumer is
+ * available it will be dispatched immediately, else it
+ * will be returned to the front of the queue.
+ */
+ QPID_BROKER_EXTERN void requeue(const QueuedMessage& msg);
+ /**
+ * Used during recovery to add stored messages back to the queue
+ */
+ QPID_BROKER_EXTERN void recover(boost::intrusive_ptr<Message>& msg);
+
+ QPID_BROKER_EXTERN void consume(Consumer::shared_ptr c,
+ bool exclusive = false);
+ QPID_BROKER_EXTERN void cancel(Consumer::shared_ptr c);
+
+ uint32_t purge(const uint32_t purge_request=0, boost::shared_ptr<Exchange> dest=boost::shared_ptr<Exchange>()); //defaults to all messages
+ QPID_BROKER_EXTERN void purgeExpired();
+
+ //move qty # of messages to destination Queue destq
+ uint32_t move(const Queue::shared_ptr destq, uint32_t qty);
+
+ QPID_BROKER_EXTERN uint32_t getMessageCount() const;
+ QPID_BROKER_EXTERN uint32_t getEnqueueCompleteMessageCount() const;
+ QPID_BROKER_EXTERN uint32_t getConsumerCount() const;
+ inline const std::string& getName() const { return name; }
+ bool isExclusiveOwner(const OwnershipToken* const o) const;
+ void releaseExclusiveOwnership();
+ bool setExclusiveOwner(const OwnershipToken* const o);
+ bool hasExclusiveConsumer() const;
+ bool hasExclusiveOwner() const;
+ inline bool isDurable() const { return store != 0; }
+ inline const framing::FieldTable& getSettings() const { return settings; }
+ inline bool isAutoDelete() const { return autodelete; }
+ bool canAutoDelete() const;
+ const QueueBindings& getBindings() const { return bindings; }
+
+ /**
+ * used to take messages from in memory and flush down to disk.
+ */
+ QPID_BROKER_EXTERN void setLastNodeFailure();
+ QPID_BROKER_EXTERN void clearLastNodeFailure();
+
+ bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck = false);
+ void enqueueAborted(boost::intrusive_ptr<Message> msg);
+ /**
+ * dequeue from store (only done once messages is acknowledged)
+ */
+ QPID_BROKER_EXTERN bool dequeue(TransactionContext* ctxt, const QueuedMessage &msg);
+ /**
+ * Inform the queue that a previous transactional dequeue
+ * committed.
+ */
+ void dequeueCommitted(const QueuedMessage& msg);
+
+ /**
+ * Inform queue of messages that were enqueued, have since
+ * been acquired but not yet accepted or released (and
+ * thus are still logically on the queue) - used in
+ * clustered broker.
+ */
+ void updateEnqueued(const QueuedMessage& msg);
+
+ /**
+ * Test whether the specified message (identified by its
+ * sequence/position), is still enqueued (note this
+ * doesn't mean it is available for delivery as it may
+ * have been delievered to a subscriber who has not yet
+ * accepted it).
+ */
+ bool isEnqueued(const QueuedMessage& msg);
+
+ /**
+ * Gets the next available message
+ */
+ QPID_BROKER_EXTERN QueuedMessage get();
+
+ /** Get the message at position pos */
+ QPID_BROKER_EXTERN QueuedMessage find(framing::SequenceNumber pos) const;
+
+ const QueuePolicy* getPolicy();
+
+ void setAlternateExchange(boost::shared_ptr<Exchange> exchange);
+ boost::shared_ptr<Exchange> getAlternateExchange();
+ bool isLocal(boost::intrusive_ptr<Message>& msg);
+
+ //PersistableQueue support:
+ uint64_t getPersistenceId() const;
+ void setPersistenceId(uint64_t persistenceId) const;
+ void encode(framing::Buffer& buffer) const;
+ uint32_t encodedSize() const;
+
+ /**
+ * Restores a queue from encoded data (used in recovery)
+ *
+ * Note: restored queue will be neither auto-deleted or have an
+ * exclusive owner
+ */
+ static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer);
+ static void tryAutoDelete(Broker& broker, Queue::shared_ptr);
+
+ virtual void setExternalQueueStore(ExternalQueueStore* inst);
+
+ // Manageable entry points
+ management::ManagementObject* GetManagementObject (void) const;
+ management::Manageable::status_t
+ ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+
+ /** Apply f to each Message on the queue. */
+ template <class F> void eachMessage(F f) {
+ sys::Mutex::ScopedLock l(messageLock);
+ messages->foreach(f);
+ }
+
+ /** Apply f to each QueueBinding on the queue */
+ template <class F> void eachBinding(F f) {
+ bindings.eachBinding(f);
+ }
+
+ /** Apply f to each Observer on the queue */
+ template <class F> void eachObserver(F f) {
+ std::for_each<Observers::iterator, F>(observers.begin(), observers.end(), f);
+ }
+
+ /** Set the position sequence number for the next message on the queue.
+ * Must be >= the current sequence number.
+ * Used by cluster to replicate queues.
+ */
+ QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos);
+ /** return current position sequence number for the next message on the queue.
+ */
+ QPID_BROKER_EXTERN framing::SequenceNumber getPosition();
+ void addObserver(boost::shared_ptr<QueueObserver>);
+ QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key);
+ /**
+ * Notify queue that recovery has completed.
+ */
+ void recoveryComplete(ExchangeRegistry& exchanges);
+
+ // For cluster update
+ QueueListeners& getListeners();
+ Messages& getMessages();
+ const Messages& getMessages() const;
+
+ /**
+ * Reserve space in policy for an enqueued message that
+ * has been recovered in the prepared state (dtx only)
+ */
+ void recoverPrepared(boost::intrusive_ptr<Message>& msg);
+
+ void flush();
+
+ const Broker* getBroker();
+};
+}
+}
+
+
+#endif /*!_broker_Queue_h*/
diff --git a/qpid/cpp/src/qpid/broker/QueueBindings.cpp b/qpid/cpp/src/qpid/broker/QueueBindings.cpp
new file mode 100644
index 0000000000..60d315acfe
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueBindings.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueBindings.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/framing/reply_exceptions.h"
+
+using qpid::framing::FieldTable;
+using qpid::framing::NotFoundException;
+using std::string;
+using namespace qpid::broker;
+
+void QueueBindings::add(const string& exchange, const string& key, const FieldTable& args)
+{
+ bindings.push_back(QueueBinding(exchange, key, args));
+}
+
+void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue)
+{
+ for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) {
+ try {
+ exchanges.get(i->exchange)->unbind(queue, i->key, &(i->args));
+ } catch (const NotFoundException&) {}
+ }
+}
+
+QueueBinding::QueueBinding(const string& _exchange, const string& _key, const FieldTable& _args)
+ : exchange(_exchange), key(_key), args(_args)
+{}
diff --git a/qpid/cpp/src/qpid/broker/QueueBindings.h b/qpid/cpp/src/qpid/broker/QueueBindings.h
new file mode 100644
index 0000000000..1b90ba5540
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueBindings.h
@@ -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.
+ *
+ */
+#ifndef _QueueBindings_
+#define _QueueBindings_
+
+#include "qpid/framing/FieldTable.h"
+#include <boost/ptr_container/ptr_list.hpp>
+#include <boost/shared_ptr.hpp>
+#include <algorithm>
+
+namespace qpid {
+namespace broker {
+
+class ExchangeRegistry;
+class Queue;
+
+struct QueueBinding{
+ std::string exchange;
+ std::string key;
+ qpid::framing::FieldTable args;
+ QueueBinding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args);
+};
+
+class QueueBindings
+{
+ public:
+
+ /** Apply f to each QueueBinding. */
+ template <class F> void eachBinding(F f) const { std::for_each(bindings.begin(), bindings.end(), f); }
+
+ void add(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args);
+ void unbind(ExchangeRegistry& exchanges, boost::shared_ptr<Queue> queue);
+
+ private:
+ typedef std::vector<QueueBinding> Bindings;
+ Bindings bindings;
+};
+
+
+}} // namespace qpid::broker
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/QueueCleaner.cpp b/qpid/cpp/src/qpid/broker/QueueCleaner.cpp
new file mode 100644
index 0000000000..3499ea8a4d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueCleaner.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueCleaner.h"
+
+#include "qpid/broker/Broker.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace broker {
+
+QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer& t) : queues(q), timer(t) {}
+
+QueueCleaner::~QueueCleaner()
+{
+ if (task) task->cancel();
+}
+
+void QueueCleaner::start(qpid::sys::Duration p)
+{
+ task = new Task(*this, p);
+ timer.add(task);
+}
+
+QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), parent(p) {}
+
+void QueueCleaner::Task::fire()
+{
+ parent.fired();
+}
+
+namespace {
+struct CollectQueues
+{
+ std::vector<Queue::shared_ptr>* queues;
+ CollectQueues(std::vector<Queue::shared_ptr>* q) : queues(q) {}
+ void operator()(Queue::shared_ptr q)
+ {
+ queues->push_back(q);
+ }
+};
+}
+
+void QueueCleaner::fired()
+{
+ //collect copy of list of queues to avoid holding registry lock while we perform purge
+ std::vector<Queue::shared_ptr> copy;
+ CollectQueues collect(&copy);
+ queues.eachQueue(collect);
+ std::for_each(copy.begin(), copy.end(), boost::bind(&Queue::purgeExpired, _1));
+ task->setupNextFire();
+ timer.add(task);
+}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/QueueCleaner.h b/qpid/cpp/src/qpid/broker/QueueCleaner.h
new file mode 100644
index 0000000000..11c2d180ac
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueCleaner.h
@@ -0,0 +1,59 @@
+#ifndef QPID_BROKER_QUEUECLEANER_H
+#define QPID_BROKER_QUEUECLEANER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/sys/Timer.h"
+
+namespace qpid {
+namespace broker {
+
+class QueueRegistry;
+/**
+ * TimerTask to purge expired messages from queues
+ */
+class QueueCleaner
+{
+ public:
+ QPID_BROKER_EXTERN QueueCleaner(QueueRegistry& queues, sys::Timer& timer);
+ QPID_BROKER_EXTERN ~QueueCleaner();
+ QPID_BROKER_EXTERN void start(qpid::sys::Duration period);
+ private:
+ class Task : public sys::TimerTask
+ {
+ public:
+ Task(QueueCleaner& parent, qpid::sys::Duration duration);
+ void fire();
+ private:
+ QueueCleaner& parent;
+ };
+
+ boost::intrusive_ptr<sys::TimerTask> task;
+ QueueRegistry& queues;
+ sys::Timer& timer;
+
+ void fired();
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_QUEUECLEANER_H*/
diff --git a/qpid/cpp/src/qpid/broker/QueueEvents.cpp b/qpid/cpp/src/qpid/broker/QueueEvents.cpp
new file mode 100644
index 0000000000..2c540ff1ad
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueEvents.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+
+QueueEvents::QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync) :
+ eventQueue(boost::bind(&QueueEvents::handle, this, _1), poller), enabled(true), sync(isSync)
+{
+ if (!sync) eventQueue.start();
+}
+
+QueueEvents::~QueueEvents()
+{
+ if (!sync) eventQueue.stop();
+}
+
+void QueueEvents::enqueued(const QueuedMessage& m)
+{
+ if (enabled) {
+ Event enq(ENQUEUE, m);
+ if (sync) {
+ for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++)
+ j->second(enq);
+ } else {
+ eventQueue.push(enq);
+ }
+ }
+}
+
+void QueueEvents::dequeued(const QueuedMessage& m)
+{
+ if (enabled) {
+ Event deq(DEQUEUE, m);
+ if (sync) {
+ for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++)
+ j->second(deq);
+ } else {
+ eventQueue.push(Event(DEQUEUE, m));
+ }
+ }
+}
+
+void QueueEvents::registerListener(const std::string& id, const EventListener& listener)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (listeners.find(id) == listeners.end()) {
+ listeners[id] = listener;
+ } else {
+ throw Exception(QPID_MSG("Event listener already registered for '" << id << "'"));
+ }
+}
+
+void QueueEvents::unregisterListener(const std::string& id)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (listeners.find(id) == listeners.end()) {
+ throw Exception(QPID_MSG("No event listener registered for '" << id << "'"));
+ } else {
+ listeners.erase(id);
+ }
+}
+
+QueueEvents::EventQueue::Batch::const_iterator
+QueueEvents::handle(const EventQueue::Batch& events) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (EventQueue::Batch::const_iterator i = events.begin(); i != events.end(); ++i) {
+ for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) {
+ j->second(*i);
+ }
+ }
+ return events.end();
+}
+
+void QueueEvents::shutdown()
+{
+ if (!sync && !eventQueue.empty() && !listeners.empty()) eventQueue.shutdown();
+}
+
+void QueueEvents::enable()
+{
+ enabled = true;
+ QPID_LOG(debug, "Queue events enabled");
+}
+
+void QueueEvents::disable()
+{
+ enabled = false;
+ QPID_LOG(debug, "Queue events disabled");
+}
+
+bool QueueEvents::isSync()
+{
+ return sync;
+}
+
+class EventGenerator : public QueueObserver
+{
+ public:
+ EventGenerator(QueueEvents& mgr, bool enqOnly) : manager(mgr), enqueueOnly(enqOnly) {}
+ void enqueued(const QueuedMessage& m)
+ {
+ manager.enqueued(m);
+ }
+ void dequeued(const QueuedMessage& m)
+ {
+ if (!enqueueOnly) manager.dequeued(m);
+ }
+ private:
+ QueueEvents& manager;
+ const bool enqueueOnly;
+};
+
+void QueueEvents::observe(Queue& queue, bool enqueueOnly)
+{
+ boost::shared_ptr<QueueObserver> observer(new EventGenerator(*this, enqueueOnly));
+ queue.addObserver(observer);
+}
+
+
+QueueEvents::Event::Event(EventType t, const QueuedMessage& m) : type(t), msg(m) {}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/QueueEvents.h b/qpid/cpp/src/qpid/broker/QueueEvents.h
new file mode 100644
index 0000000000..fcddfe9092
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueEvents.h
@@ -0,0 +1,85 @@
+#ifndef QPID_BROKER_QUEUEEVENTS_H
+#define QPID_BROKER_QUEUEEVENTS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/PollableQueue.h"
+#include <map>
+#include <string>
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Event manager for queue events. Allows queues to indicate when
+ * events have occured; allows listeners to register for notification
+ * of this. The notification happens asynchronously, in a separate
+ * thread.
+ */
+class QueueEvents
+{
+ public:
+ enum EventType {ENQUEUE, DEQUEUE};
+
+ struct Event
+ {
+ EventType type;
+ QueuedMessage msg;
+
+ QPID_BROKER_EXTERN Event(EventType, const QueuedMessage&);
+ };
+
+ typedef boost::function<void (Event)> EventListener;
+
+ QPID_BROKER_EXTERN QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync = false);
+ QPID_BROKER_EXTERN ~QueueEvents();
+ QPID_BROKER_EXTERN void enqueued(const QueuedMessage&);
+ QPID_BROKER_EXTERN void dequeued(const QueuedMessage&);
+ QPID_BROKER_EXTERN void registerListener(const std::string& id,
+ const EventListener&);
+ QPID_BROKER_EXTERN void unregisterListener(const std::string& id);
+ void enable();
+ void disable();
+ void observe(Queue&, bool enqueueOnly);
+ //process all outstanding events
+ QPID_BROKER_EXTERN void shutdown();
+ QPID_BROKER_EXTERN bool isSync();
+ private:
+ typedef qpid::sys::PollableQueue<Event> EventQueue;
+ typedef std::map<std::string, EventListener> Listeners;
+
+ EventQueue eventQueue;
+ Listeners listeners;
+ volatile bool enabled;
+ qpid::sys::Mutex lock;//protect listeners from concurrent access
+ bool sync;
+
+ EventQueue::Batch::const_iterator handle(const EventQueue::Batch& e);
+
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_QUEUEEVENTS_H*/
diff --git a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp
new file mode 100644
index 0000000000..b2e2e54bdf
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp
@@ -0,0 +1,407 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/sys/ClusterSafe.h"
+
+#include "qmf/org/apache/qpid/broker/Queue.h"
+
+#include <sstream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace {
+ /** ensure that the configured flow control stop and resume values are
+ * valid with respect to the maximum queue capacity, and each other
+ */
+ template <typename T>
+ void validateFlowConfig(T max, T& stop, T& resume, const std::string& type, const std::string& queue)
+ {
+ if (resume > stop) {
+ throw InvalidArgumentException(QPID_MSG("Queue \"" << queue << "\": qpid.flow_resume_" << type
+ << "=" << resume
+ << " must be less than qpid.flow_stop_" << type
+ << "=" << stop));
+ }
+ if (resume == 0) resume = stop;
+ if (max != 0 && (max < stop)) {
+ throw InvalidArgumentException(QPID_MSG("Queue \"" << queue << "\": qpid.flow_stop_" << type
+ << "=" << stop
+ << " must be less than qpid.max_" << type
+ << "=" << max));
+ }
+ }
+
+ /** extract a capacity value as passed in an argument map
+ */
+ uint64_t getCapacity(const FieldTable& settings, const std::string& key, uint64_t defaultValue)
+ {
+ FieldTable::ValuePtr v = settings.get(key);
+
+ int64_t result = 0;
+
+ if (!v) return defaultValue;
+ if (v->getType() == 0x23) {
+ QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>());
+ } else if (v->getType() == 0x33) {
+ QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>());
+ } else if (v->convertsTo<int64_t>()) {
+ result = v->get<int64_t>();
+ QPID_LOG(debug, "Got integer value for " << key << ": " << result);
+ if (result >= 0) return result;
+ } else if (v->convertsTo<string>()) {
+ string s(v->get<string>());
+ QPID_LOG(debug, "Got string value for " << key << ": " << s);
+ std::istringstream convert(s);
+ if (convert >> result && result >= 0) return result;
+ }
+
+ QPID_LOG(warning, "Cannot convert " << key << " to unsigned integer, using default (" << defaultValue << ")");
+ return defaultValue;
+ }
+}
+
+
+
+QueueFlowLimit::QueueFlowLimit(Queue *_queue,
+ uint32_t _flowStopCount, uint32_t _flowResumeCount,
+ uint64_t _flowStopSize, uint64_t _flowResumeSize)
+ : StatefulQueueObserver(std::string("QueueFlowLimit")), queue(_queue), queueName("<unknown>"),
+ flowStopCount(_flowStopCount), flowResumeCount(_flowResumeCount),
+ flowStopSize(_flowStopSize), flowResumeSize(_flowResumeSize),
+ flowStopped(false), count(0), size(0), queueMgmtObj(0), broker(0)
+{
+ uint32_t maxCount(0);
+ uint64_t maxSize(0);
+
+ if (queue) {
+ queueName = _queue->getName();
+ if (queue->getPolicy()) {
+ maxSize = _queue->getPolicy()->getMaxSize();
+ maxCount = _queue->getPolicy()->getMaxCount();
+ }
+ broker = queue->getBroker();
+ queueMgmtObj = dynamic_cast<_qmfBroker::Queue*> (queue->GetManagementObject());
+ if (queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(isFlowControlActive());
+ }
+ }
+ validateFlowConfig( maxCount, flowStopCount, flowResumeCount, "count", queueName );
+ validateFlowConfig( maxSize, flowStopSize, flowResumeSize, "size", queueName );
+ QPID_LOG(info, "Queue \"" << queueName << "\": Flow limit created: flowStopCount=" << flowStopCount
+ << ", flowResumeCount=" << flowResumeCount
+ << ", flowStopSize=" << flowStopSize << ", flowResumeSize=" << flowResumeSize );
+}
+
+
+QueueFlowLimit::~QueueFlowLimit()
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ if (!index.empty()) {
+ // we're gone - release all pending msgs
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin();
+ itr != index.end(); ++itr)
+ if (itr->second)
+ try {
+ itr->second->getIngressCompletion().finishCompleter();
+ } catch (...) {} // ignore - not safe for a destructor to throw.
+ index.clear();
+ }
+}
+
+
+void QueueFlowLimit::enqueued(const QueuedMessage& msg)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+
+ ++count;
+ size += msg.payload->contentSize();
+
+ if (!flowStopped) {
+ if (flowStopCount && count > flowStopCount) {
+ flowStopped = true;
+ QPID_LOG(info, "Queue \"" << queueName << "\": has reached " << flowStopCount << " enqueued messages. Producer flow control activated." );
+ } else if (flowStopSize && size > flowStopSize) {
+ flowStopped = true;
+ QPID_LOG(info, "Queue \"" << queueName << "\": has reached " << flowStopSize << " enqueued bytes. Producer flow control activated." );
+ }
+ if (flowStopped && queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(true);
+ queueMgmtObj->inc_flowStoppedCount();
+ }
+ }
+
+ if (flowStopped || !index.empty()) {
+ // ignore flow control if we are populating the queue due to cluster replication:
+ if (broker && broker->isClusterUpdatee()) {
+ QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.position);
+ return;
+ }
+ QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.position);
+ msg.payload->getIngressCompletion().startCompleter(); // don't complete until flow resumes
+ bool unique;
+ unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(msg.position, msg.payload)).second;
+ assert(unique);
+ }
+}
+
+
+
+void QueueFlowLimit::dequeued(const QueuedMessage& msg)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+
+ if (count > 0) {
+ --count;
+ } else {
+ throw Exception(QPID_MSG("Flow limit count underflow on dequeue. Queue=" << queueName));
+ }
+
+ uint64_t _size = msg.payload->contentSize();
+ if (_size <= size) {
+ size -= _size;
+ } else {
+ throw Exception(QPID_MSG("Flow limit size underflow on dequeue. Queue=" << queueName));
+ }
+
+ if (flowStopped &&
+ (flowResumeSize == 0 || size < flowResumeSize) &&
+ (flowResumeCount == 0 || count < flowResumeCount)) {
+ flowStopped = false;
+ if (queueMgmtObj)
+ queueMgmtObj->set_flowStopped(false);
+ QPID_LOG(info, "Queue \"" << queueName << "\": has drained below the flow control resume level. Producer flow control deactivated." );
+ }
+
+ if (!index.empty()) {
+ if (!flowStopped) {
+ // flow enabled - release all pending msgs
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin();
+ itr != index.end(); ++itr)
+ if (itr->second)
+ itr->second->getIngressCompletion().finishCompleter();
+ index.clear();
+ } else {
+ // even if flow controlled, we must release this msg as it is being dequeued
+ std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.find(msg.position);
+ if (itr != index.end()) { // this msg is flow controlled, release it:
+ msg.payload->getIngressCompletion().finishCompleter();
+ index.erase(itr);
+ }
+ }
+ }
+}
+
+
+void QueueFlowLimit::encode(Buffer& buffer) const
+{
+ buffer.putLong(flowStopCount);
+ buffer.putLong(flowResumeCount);
+ buffer.putLongLong(flowStopSize);
+ buffer.putLongLong(flowResumeSize);
+ buffer.putLong(count);
+ buffer.putLongLong(size);
+}
+
+
+void QueueFlowLimit::decode ( Buffer& buffer )
+{
+ flowStopCount = buffer.getLong();
+ flowResumeCount = buffer.getLong();
+ flowStopSize = buffer.getLongLong();
+ flowResumeSize = buffer.getLongLong();
+ count = buffer.getLong();
+ size = buffer.getLongLong();
+}
+
+
+uint32_t QueueFlowLimit::encodedSize() const {
+ return sizeof(uint32_t) + // flowStopCount
+ sizeof(uint32_t) + // flowResumecount
+ sizeof(uint64_t) + // flowStopSize
+ sizeof(uint64_t) + // flowResumeSize
+ sizeof(uint32_t) + // count
+ sizeof(uint64_t); // size
+}
+
+
+const std::string QueueFlowLimit::flowStopCountKey("qpid.flow_stop_count");
+const std::string QueueFlowLimit::flowResumeCountKey("qpid.flow_resume_count");
+const std::string QueueFlowLimit::flowStopSizeKey("qpid.flow_stop_size");
+const std::string QueueFlowLimit::flowResumeSizeKey("qpid.flow_resume_size");
+uint64_t QueueFlowLimit::defaultMaxSize;
+uint QueueFlowLimit::defaultFlowStopRatio;
+uint QueueFlowLimit::defaultFlowResumeRatio;
+
+
+void QueueFlowLimit::setDefaults(uint64_t maxQueueSize, uint flowStopRatio, uint flowResumeRatio)
+{
+ defaultMaxSize = maxQueueSize;
+ defaultFlowStopRatio = flowStopRatio;
+ defaultFlowResumeRatio = flowResumeRatio;
+
+ /** @todo KAG: Verify valid range on Broker::Options instead of here */
+ if (flowStopRatio > 100 || flowResumeRatio > 100)
+ throw InvalidArgumentException(QPID_MSG("Default queue flow ratios must be between 0 and 100, inclusive:"
+ << " flowStopRatio=" << flowStopRatio
+ << " flowResumeRatio=" << flowResumeRatio));
+ if (flowResumeRatio > flowStopRatio)
+ throw InvalidArgumentException(QPID_MSG("Default queue flow stop ratio must be >= flow resume ratio:"
+ << " flowStopRatio=" << flowStopRatio
+ << " flowResumeRatio=" << flowResumeRatio));
+}
+
+
+void QueueFlowLimit::observe(Queue& queue, const qpid::framing::FieldTable& settings)
+{
+ QueueFlowLimit *ptr = createLimit( &queue, settings );
+ if (ptr) {
+ boost::shared_ptr<QueueFlowLimit> observer(ptr);
+ queue.addObserver(observer);
+ }
+}
+
+/** returns ptr to a QueueFlowLimit, else 0 if no limit */
+QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const qpid::framing::FieldTable& settings)
+{
+ std::string type(QueuePolicy::getType(settings));
+
+ if (type == QueuePolicy::RING || type == QueuePolicy::RING_STRICT) {
+ // The size of a RING queue is limited by design - no need for flow control.
+ return 0;
+ }
+
+ if (settings.get(flowStopCountKey) || settings.get(flowStopSizeKey) ||
+ settings.get(flowResumeCountKey) || settings.get(flowResumeSizeKey)) {
+ // user provided (some) flow settings manually...
+ uint32_t flowStopCount = getCapacity(settings, flowStopCountKey, 0);
+ uint32_t flowResumeCount = getCapacity(settings, flowResumeCountKey, 0);
+ uint64_t flowStopSize = getCapacity(settings, flowStopSizeKey, 0);
+ uint64_t flowResumeSize = getCapacity(settings, flowResumeSizeKey, 0);
+ if (flowStopCount == 0 && flowStopSize == 0) { // disable flow control
+ return 0;
+ }
+ return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+
+ if (defaultFlowStopRatio) { // broker has a default ratio setup...
+ uint64_t maxByteCount = getCapacity(settings, QueuePolicy::maxSizeKey, defaultMaxSize);
+ uint64_t flowStopSize = (uint64_t)(maxByteCount * (defaultFlowStopRatio/100.0) + 0.5);
+ uint64_t flowResumeSize = (uint64_t)(maxByteCount * (defaultFlowResumeRatio/100.0));
+ uint32_t maxMsgCount = getCapacity(settings, QueuePolicy::maxCountKey, 0); // no size by default
+ uint32_t flowStopCount = (uint32_t)(maxMsgCount * (defaultFlowStopRatio/100.0) + 0.5);
+ uint32_t flowResumeCount = (uint32_t)(maxMsgCount * (defaultFlowResumeRatio/100.0));
+
+ return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+ return 0;
+}
+
+/* Cluster replication */
+
+namespace {
+ /** pack a set of sequence number ranges into a framing::Array */
+ void buildSeqRangeArray(qpid::framing::Array *seqs,
+ const qpid::framing::SequenceNumber& first,
+ const qpid::framing::SequenceNumber& last)
+ {
+ seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(first)));
+ seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(last)));
+ }
+}
+
+/** Runs on UPDATER to snapshot current state */
+void QueueFlowLimit::getState(qpid::framing::FieldTable& state ) const
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ state.clear();
+
+ framing::SequenceSet ss;
+ if (!index.empty()) {
+ /* replicate the set of messages pending flow control */
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::const_iterator itr = index.begin();
+ itr != index.end(); ++itr) {
+ ss.add(itr->first);
+ }
+ framing::Array seqs(TYPE_CODE_UINT32);
+ typedef boost::function<void(framing::SequenceNumber, framing::SequenceNumber)> arrayBuilder;
+ ss.for_each((arrayBuilder)boost::bind(&buildSeqRangeArray, &seqs, _1, _2));
+ state.setArray("pendingMsgSeqs", seqs);
+ }
+ QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicating pending msgs, range=" << ss);
+}
+
+
+/** called on UPDATEE to set state from snapshot */
+void QueueFlowLimit::setState(const qpid::framing::FieldTable& state)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ index.clear();
+
+ framing::SequenceSet fcmsg;
+ framing::Array seqArray(TYPE_CODE_UINT32);
+ if (state.getArray("pendingMsgSeqs", seqArray)) {
+ assert((seqArray.count() & 0x01) == 0); // must be even since they are sequence ranges
+ framing::Array::const_iterator i = seqArray.begin();
+ while (i != seqArray.end()) {
+ framing::SequenceNumber first((*i)->getIntegerValue<uint32_t, 4>());
+ ++i;
+ framing::SequenceNumber last((*i)->getIntegerValue<uint32_t, 4>());
+ ++i;
+ fcmsg.add(first, last);
+ for (SequenceNumber seq = first; seq <= last; ++seq) {
+ QueuedMessage msg(queue->find(seq)); // fyi: msg.payload may be null if msg is delivered & unacked
+ bool unique;
+ unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(seq, msg.payload)).second;
+ assert(unique);
+ }
+ }
+ }
+
+ flowStopped = index.size() != 0;
+ if (queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(isFlowControlActive());
+ }
+ QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicated the pending msgs, range=" << fcmsg)
+}
+
+
+namespace qpid {
+ namespace broker {
+
+std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f)
+{
+ out << "; flowStopCount=" << f.flowStopCount << ", flowResumeCount=" << f.flowResumeCount;
+ out << "; flowStopSize=" << f.flowStopSize << ", flowResumeSize=" << f.flowResumeSize;
+ return out;
+}
+
+ }
+}
+
diff --git a/qpid/cpp/src/qpid/broker/QueueFlowLimit.h b/qpid/cpp/src/qpid/broker/QueueFlowLimit.h
new file mode 100644
index 0000000000..c02e479976
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueFlowLimit.h
@@ -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.
+ *
+ */
+#ifndef _QueueFlowLimit_
+#define _QueueFlowLimit_
+
+#include <list>
+#include <set>
+#include <iostream>
+#include <memory>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qmf {
+namespace org {
+namespace apache {
+namespace qpid {
+namespace broker {
+ class Queue;
+}}}}}
+namespace _qmfBroker = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+/**
+ * Producer flow control: when level is > flowStop*, flow control is ON.
+ * then level is < flowResume*, flow control is OFF. If == 0, flow control
+ * is not used. If both byte and msg count thresholds are set, then
+ * passing _either_ level may turn flow control ON, but _both_ must be
+ * below level before flow control will be turned OFF.
+ */
+ class QueueFlowLimit : public StatefulQueueObserver
+{
+ static uint64_t defaultMaxSize;
+ static uint defaultFlowStopRatio;
+ static uint defaultFlowResumeRatio;
+
+ Queue *queue;
+ std::string queueName;
+
+ uint32_t flowStopCount;
+ uint32_t flowResumeCount;
+ uint64_t flowStopSize;
+ uint64_t flowResumeSize;
+ bool flowStopped; // true = producers held in flow control
+
+ // current queue utilization
+ uint32_t count;
+ uint64_t size;
+
+ public:
+ static QPID_BROKER_EXTERN const std::string flowStopCountKey;
+ static QPID_BROKER_EXTERN const std::string flowResumeCountKey;
+ static QPID_BROKER_EXTERN const std::string flowStopSizeKey;
+ static QPID_BROKER_EXTERN const std::string flowResumeSizeKey;
+
+ QPID_BROKER_EXTERN virtual ~QueueFlowLimit();
+
+ /** the queue has added QueuedMessage. Returns true if flow state changes */
+ QPID_BROKER_EXTERN void enqueued(const QueuedMessage&);
+ /** the queue has removed QueuedMessage. Returns true if flow state changes */
+ QPID_BROKER_EXTERN void dequeued(const QueuedMessage&);
+
+ /** for clustering: */
+ QPID_BROKER_EXTERN void getState(qpid::framing::FieldTable&) const;
+ QPID_BROKER_EXTERN void setState(const qpid::framing::FieldTable&);
+
+ uint32_t getFlowStopCount() const { return flowStopCount; }
+ uint32_t getFlowResumeCount() const { return flowResumeCount; }
+ uint64_t getFlowStopSize() const { return flowStopSize; }
+ uint64_t getFlowResumeSize() const { return flowResumeSize; }
+
+ uint32_t getFlowCount() const { return count; }
+ uint64_t getFlowSize() const { return size; }
+ bool isFlowControlActive() const { return flowStopped; }
+ bool monitorFlowControl() const { return flowStopCount || flowStopSize; }
+
+ void encode(framing::Buffer& buffer) const;
+ void decode(framing::Buffer& buffer);
+ uint32_t encodedSize() const;
+
+ static QPID_BROKER_EXTERN void observe(Queue& queue, const qpid::framing::FieldTable& settings);
+ static QPID_BROKER_EXTERN void setDefaults(uint64_t defaultMaxSize, uint defaultFlowStopRatio, uint defaultFlowResumeRatio);
+
+ friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueueFlowLimit&);
+
+ protected:
+ // msgs waiting for flow to become available.
+ std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> > index;
+ mutable qpid::sys::Mutex indexLock;
+
+ _qmfBroker::Queue *queueMgmtObj;
+
+ const Broker *broker;
+
+ QPID_BROKER_EXTERN QueueFlowLimit(Queue *queue,
+ uint32_t flowStopCount, uint32_t flowResumeCount,
+ uint64_t flowStopSize, uint64_t flowResumeSize);
+ static QPID_BROKER_EXTERN QueueFlowLimit *createLimit(Queue *queue, const qpid::framing::FieldTable& settings);
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/QueueListeners.cpp b/qpid/cpp/src/qpid/broker/QueueListeners.cpp
new file mode 100644
index 0000000000..591f4443bb
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueListeners.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/QueueListeners.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace broker {
+
+void QueueListeners::addListener(Consumer::shared_ptr c)
+{
+ if (!c->inListeners) {
+ if (c->acquires) {
+ add(consumers, c);
+ } else {
+ add(browsers, c);
+ }
+ c->inListeners = true;
+ }
+}
+
+void QueueListeners::removeListener(Consumer::shared_ptr c)
+{
+ if (c->inListeners) {
+ if (c->acquires) {
+ remove(consumers, c);
+ } else {
+ remove(browsers, c);
+ }
+ c->inListeners = false;
+ }
+}
+
+void QueueListeners::populate(NotificationSet& set)
+{
+ if (consumers.size()) {
+ set.consumer = consumers.front();
+ consumers.pop_front();
+ set.consumer->inListeners = false;
+ } else {
+ // Don't swap the deques, hang on to the memory allocated.
+ set.browsers = browsers;
+ browsers.clear();
+ for (Listeners::iterator i = set.browsers.begin(); i != set.browsers.end(); i++)
+ (*i)->inListeners = false;
+ }
+}
+
+void QueueListeners::add(Listeners& listeners, Consumer::shared_ptr c)
+{
+ listeners.push_back(c);
+}
+
+void QueueListeners::remove(Listeners& listeners, Consumer::shared_ptr c)
+{
+ Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c);
+ if (i != listeners.end()) listeners.erase(i);
+}
+
+void QueueListeners::NotificationSet::notify()
+{
+ if (consumer) consumer->notify();
+ else std::for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify));
+}
+
+bool QueueListeners::contains(Consumer::shared_ptr c) const {
+ return c->inListeners;
+}
+
+void QueueListeners::ListenerSet::notifyAll()
+{
+ std::for_each(listeners.begin(), listeners.end(), boost::mem_fn(&Consumer::notify));
+}
+
+void QueueListeners::snapshot(ListenerSet& set)
+{
+ set.listeners.insert(set.listeners.end(), consumers.begin(), consumers.end());
+ set.listeners.insert(set.listeners.end(), browsers.begin(), browsers.end());
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/QueueListeners.h b/qpid/cpp/src/qpid/broker/QueueListeners.h
new file mode 100644
index 0000000000..0659499253
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueListeners.h
@@ -0,0 +1,86 @@
+#ifndef QPID_BROKER_QUEUELISTENERS_H
+#define QPID_BROKER_QUEUELISTENERS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Consumer.h"
+#include <deque>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Track and notify components that wish to be notified of messages
+ * that become available on a queue.
+ *
+ * None of the methods defined here are protected by locking. However
+ * the populate method allows a 'snapshot' to be taken of the
+ * listeners to be notified. NotificationSet::notify() may then be
+ * called outside of any lock that protects the QueueListeners
+ * instance from concurrent access.
+ */
+class QueueListeners
+{
+ public:
+ typedef std::deque<Consumer::shared_ptr> Listeners;
+
+ class NotificationSet
+ {
+ public:
+ void notify();
+ private:
+ Listeners browsers;
+ Consumer::shared_ptr consumer;
+ friend class QueueListeners;
+ };
+
+ class ListenerSet
+ {
+ public:
+ void notifyAll();
+ private:
+ Listeners listeners;
+ friend class QueueListeners;
+ };
+
+ void addListener(Consumer::shared_ptr);
+ void removeListener(Consumer::shared_ptr);
+ void populate(NotificationSet&);
+ void snapshot(ListenerSet&);
+ bool contains(Consumer::shared_ptr c) const;
+ void notifyAll();
+
+ template <class F> void eachListener(F f) {
+ std::for_each(browsers.begin(), browsers.end(), f);
+ std::for_each(consumers.begin(), consumers.end(), f);
+ }
+
+ private:
+ Listeners consumers;
+ Listeners browsers;
+
+ void add(Listeners&, Consumer::shared_ptr);
+ void remove(Listeners&, Consumer::shared_ptr);
+
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_QUEUELISTENERS_H*/
diff --git a/qpid/cpp/src/qpid/broker/QueueObserver.h b/qpid/cpp/src/qpid/broker/QueueObserver.h
new file mode 100644
index 0000000000..3ca01c051e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueObserver.h
@@ -0,0 +1,42 @@
+#ifndef QPID_BROKER_QUEUEOBSERVER_H
+#define QPID_BROKER_QUEUEOBSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace broker {
+
+struct QueuedMessage;
+/**
+ * Interface for notifying classes who want to act as 'observers' of a
+ * queue of particular events.
+ */
+class QueueObserver
+{
+ public:
+ virtual ~QueueObserver() {}
+ virtual void enqueued(const QueuedMessage&) = 0;
+ virtual void dequeued(const QueuedMessage&) = 0;
+ private:
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_QUEUEOBSERVER_H*/
diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp
new file mode 100644
index 0000000000..a93a6332fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp
@@ -0,0 +1,360 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/PriorityQueue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include <sstream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+QueuePolicy::QueuePolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) :
+ maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false), name(_name) {
+ QPID_LOG(info, "Queue \"" << name << "\": Policy created: type=" << type << "; maxCount=" << maxCount << "; maxSize=" << maxSize);
+}
+
+void QueuePolicy::enqueued(uint64_t _size)
+{
+ if (maxCount) ++count;
+ if (maxSize) size += _size;
+}
+
+void QueuePolicy::dequeued(uint64_t _size)
+{
+ if (maxCount) {
+ if (count > 0) {
+ --count;
+ } else {
+ throw Exception(QPID_MSG("Attempted count underflow on dequeue(" << _size << "): " << *this));
+ }
+ }
+ if (maxSize) {
+ if (_size > size) {
+ throw Exception(QPID_MSG("Attempted size underflow on dequeue(" << _size << "): " << *this));
+ } else {
+ size -= _size;
+ }
+ }
+}
+
+bool QueuePolicy::checkLimit(boost::intrusive_ptr<Message> m)
+{
+ bool sizeExceeded = maxSize && (size + m->contentSize()) > maxSize;
+ bool countExceeded = maxCount && (count + 1) > maxCount;
+ bool exceeded = sizeExceeded || countExceeded;
+ if (exceeded) {
+ if (!policyExceeded) {
+ policyExceeded = true;
+ if (sizeExceeded) QPID_LOG(info, "Queue cumulative message size exceeded policy for " << name);
+ if (countExceeded) QPID_LOG(info, "Queue message count exceeded policy for " << name);
+ }
+ } else {
+ if (policyExceeded) {
+ policyExceeded = false;
+ QPID_LOG(info, "Queue cumulative message size and message count within policy for " << name);
+ }
+ }
+ return !exceeded;
+}
+
+void QueuePolicy::tryEnqueue(boost::intrusive_ptr<Message> m)
+{
+ if (checkLimit(m)) {
+ enqueued(m->contentSize());
+ } else {
+ throw ResourceLimitExceededException(QPID_MSG("Policy exceeded on " << name << ", policy: " << *this));
+ }
+}
+
+void QueuePolicy::recoverEnqueued(boost::intrusive_ptr<Message> m)
+{
+ tryEnqueue(m);
+}
+
+void QueuePolicy::enqueueAborted(boost::intrusive_ptr<Message> m)
+{
+ dequeued(m->contentSize());
+}
+
+void QueuePolicy::enqueued(const QueuedMessage&) {}
+
+void QueuePolicy::dequeued(const QueuedMessage& m)
+{
+ dequeued(m.payload->contentSize());
+}
+
+bool QueuePolicy::isEnqueued(const QueuedMessage&)
+{
+ return true;
+}
+
+void QueuePolicy::update(FieldTable& settings)
+{
+ if (maxCount) settings.setInt(maxCountKey, maxCount);
+ if (maxSize) settings.setInt(maxSizeKey, maxSize);
+ settings.setString(typeKey, type);
+}
+
+uint32_t QueuePolicy::getCapacity(const FieldTable& settings, const std::string& key, uint32_t defaultValue)
+{
+ FieldTable::ValuePtr v = settings.get(key);
+
+ int32_t result = 0;
+
+ if (!v) return defaultValue;
+ if (v->getType() == 0x23) {
+ QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>());
+ } else if (v->getType() == 0x33) {
+ QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>());
+ } else if (v->convertsTo<int>()) {
+ result = v->get<int>();
+ QPID_LOG(debug, "Got integer value for " << key << ": " << result);
+ if (result >= 0) return result;
+ } else if (v->convertsTo<string>()) {
+ string s(v->get<string>());
+ QPID_LOG(debug, "Got string value for " << key << ": " << s);
+ std::istringstream convert(s);
+ if (convert >> result && result >= 0 && convert.eof()) return result;
+ }
+
+ throw IllegalArgumentException(QPID_MSG("Cannot convert " << key << " to unsigned integer: " << *v));
+}
+
+std::string QueuePolicy::getType(const FieldTable& settings)
+{
+ FieldTable::ValuePtr v = settings.get(typeKey);
+ if (v && v->convertsTo<std::string>()) {
+ std::string t = v->get<std::string>();
+ std::transform(t.begin(), t.end(), t.begin(), tolower);
+ if (t == REJECT || t == FLOW_TO_DISK || t == RING || t == RING_STRICT) return t;
+ }
+ return REJECT;
+}
+
+void QueuePolicy::setDefaultMaxSize(uint64_t s)
+{
+ defaultMaxSize = s;
+}
+
+void QueuePolicy::getPendingDequeues(Messages&) {}
+
+
+
+
+void QueuePolicy::encode(Buffer& buffer) const
+{
+ buffer.putLong(maxCount);
+ buffer.putLongLong(maxSize);
+ buffer.putLong(count);
+ buffer.putLongLong(size);
+}
+
+void QueuePolicy::decode ( Buffer& buffer )
+{
+ maxCount = buffer.getLong();
+ maxSize = buffer.getLongLong();
+ count = buffer.getLong();
+ size = buffer.getLongLong();
+}
+
+
+uint32_t QueuePolicy::encodedSize() const {
+ return sizeof(uint32_t) + // maxCount
+ sizeof(uint64_t) + // maxSize
+ sizeof(uint32_t) + // count
+ sizeof(uint64_t); // size
+}
+
+
+
+const std::string QueuePolicy::maxCountKey("qpid.max_count");
+const std::string QueuePolicy::maxSizeKey("qpid.max_size");
+const std::string QueuePolicy::typeKey("qpid.policy_type");
+const std::string QueuePolicy::REJECT("reject");
+const std::string QueuePolicy::FLOW_TO_DISK("flow_to_disk");
+const std::string QueuePolicy::RING("ring");
+const std::string QueuePolicy::RING_STRICT("ring_strict");
+uint64_t QueuePolicy::defaultMaxSize(0);
+
+FlowToDiskPolicy::FlowToDiskPolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize) :
+ QueuePolicy(_name, _maxCount, _maxSize, FLOW_TO_DISK) {}
+
+bool FlowToDiskPolicy::checkLimit(boost::intrusive_ptr<Message> m)
+{
+ if (!QueuePolicy::checkLimit(m)) m->requestContentRelease();
+ return true;
+}
+
+RingQueuePolicy::RingQueuePolicy(const std::string& _name,
+ uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) :
+ QueuePolicy(_name, _maxCount, _maxSize, _type), strict(_type == RING_STRICT) {}
+
+bool before(const QueuedMessage& a, const QueuedMessage& b)
+{
+ int priorityA = PriorityQueue::getPriority(a);
+ int priorityB = PriorityQueue::getPriority(b);
+ if (priorityA == priorityB) return a.position < b.position;
+ else return priorityA < priorityB;
+}
+
+void RingQueuePolicy::enqueued(const QueuedMessage& m)
+{
+ //need to insert in correct location based on position
+ queue.insert(lower_bound(queue.begin(), queue.end(), m, before), m);
+}
+
+void RingQueuePolicy::dequeued(const QueuedMessage& m)
+{
+ //find and remove m from queue
+ if (find(m, pendingDequeues, true) || find(m, queue, true)) {
+ //now update count and size
+ QueuePolicy::dequeued(m);
+ }
+}
+
+bool RingQueuePolicy::isEnqueued(const QueuedMessage& m)
+{
+ //for non-strict ring policy, a message can be replaced (and
+ //therefore dequeued) before it is accepted or released by
+ //subscriber; need to detect this
+ return find(m, pendingDequeues, false) || find(m, queue, false);
+}
+
+bool RingQueuePolicy::checkLimit(boost::intrusive_ptr<Message> m)
+{
+
+ // If the message is bigger than the queue size, give up
+ if (getMaxSize() && m->contentSize() > getMaxSize()) {
+ QPID_LOG(debug, "Message too large for ring queue " << name
+ << " [" << *this << "] "
+ << ": message size = " << m->contentSize() << " bytes"
+ << ": max queue size = " << getMaxSize() << " bytes");
+ return false;
+ }
+
+ // if within limits, ok to accept
+ if (QueuePolicy::checkLimit(m)) return true;
+
+ // At this point, we've exceeded maxSize, maxCount, or both.
+ //
+ // If we've exceeded maxCount, we've exceeded it by 1, so
+ // replacing the first message is sufficient. If we've exceeded
+ // maxSize, we need to pop enough messages to get the space we
+ // need.
+
+ unsigned int haveSpace = getMaxSize() - getCurrentQueueSize();
+
+ do {
+ QueuedMessage oldest = queue.front();
+
+ if (oldest.queue->acquire(oldest) || !strict) {
+ queue.pop_front();
+ pendingDequeues.push_back(oldest);
+ QPID_LOG(debug, "Ring policy triggered in " << name
+ << ": removed message " << oldest.position << " to make way for new message");
+
+ haveSpace += oldest.payload->contentSize();
+
+ } else {
+ //in strict mode, if oldest message has been delivered (hence
+ //cannot be acquired) but not yet acked, it should not be
+ //removed and the attempted enqueue should fail
+ QPID_LOG(debug, "Ring policy could not be triggered in " << name
+ << ": oldest message (seq-no=" << oldest.position << ") has been delivered but not yet acknowledged or requeued");
+ return false;
+ }
+ } while (haveSpace < m->contentSize());
+
+
+ return true;
+}
+
+void RingQueuePolicy::getPendingDequeues(Messages& result)
+{
+ result = pendingDequeues;
+}
+
+bool RingQueuePolicy::find(const QueuedMessage& m, Messages& q, bool remove)
+{
+ for (Messages::iterator i = q.begin(); i != q.end(); i++) {
+ if (i->payload == m.payload) {
+ if (remove) q.erase(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type)
+{
+ return createQueuePolicy("<unspecified>", maxCount, maxSize, type);
+}
+
+std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const qpid::framing::FieldTable& settings)
+{
+ return createQueuePolicy("<unspecified>", settings);
+}
+
+std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings)
+{
+ uint32_t maxCount = getCapacity(settings, maxCountKey, 0);
+ uint32_t maxSize = getCapacity(settings, maxSizeKey, defaultMaxSize);
+ if (maxCount || maxSize) {
+ return createQueuePolicy(name, maxCount, maxSize, getType(settings));
+ } else {
+ return std::auto_ptr<QueuePolicy>();
+ }
+}
+
+std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name,
+ uint32_t maxCount, uint64_t maxSize, const std::string& type)
+{
+ if (type == RING || type == RING_STRICT) {
+ return std::auto_ptr<QueuePolicy>(new RingQueuePolicy(name, maxCount, maxSize, type));
+ } else if (type == FLOW_TO_DISK) {
+ return std::auto_ptr<QueuePolicy>(new FlowToDiskPolicy(name, maxCount, maxSize));
+ } else {
+ return std::auto_ptr<QueuePolicy>(new QueuePolicy(name, maxCount, maxSize, type));
+ }
+
+}
+
+namespace qpid {
+ namespace broker {
+
+std::ostream& operator<<(std::ostream& out, const QueuePolicy& p)
+{
+ if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size;
+ else out << "size: unlimited";
+ out << "; ";
+ if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count;
+ else out << "count: unlimited";
+ out << "; type=" << p.type;
+ return out;
+}
+
+ }
+}
+
diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.h b/qpid/cpp/src/qpid/broker/QueuePolicy.h
new file mode 100644
index 0000000000..3cdd63784d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueuePolicy.h
@@ -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.
+ *
+ */
+#ifndef _QueuePolicy_
+#define _QueuePolicy_
+
+#include <deque>
+#include <iostream>
+#include <memory>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace broker {
+
+class QueuePolicy
+{
+ static uint64_t defaultMaxSize;
+
+ uint32_t maxCount;
+ uint64_t maxSize;
+ const std::string type;
+ uint32_t count;
+ uint64_t size;
+ bool policyExceeded;
+
+ static uint32_t getCapacity(const qpid::framing::FieldTable& settings, const std::string& key, uint32_t defaultValue);
+
+ protected:
+ uint64_t getCurrentQueueSize() const { return size; }
+
+ public:
+ typedef std::deque<QueuedMessage> Messages;
+ static QPID_BROKER_EXTERN const std::string maxCountKey;
+ static QPID_BROKER_EXTERN const std::string maxSizeKey;
+ static QPID_BROKER_EXTERN const std::string typeKey;
+ static QPID_BROKER_EXTERN const std::string REJECT;
+ static QPID_BROKER_EXTERN const std::string FLOW_TO_DISK;
+ static QPID_BROKER_EXTERN const std::string RING;
+ static QPID_BROKER_EXTERN const std::string RING_STRICT;
+
+ virtual ~QueuePolicy() {}
+ QPID_BROKER_EXTERN void tryEnqueue(boost::intrusive_ptr<Message> msg);
+ QPID_BROKER_EXTERN void recoverEnqueued(boost::intrusive_ptr<Message> msg);
+ QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr<Message> msg);
+ virtual void enqueued(const QueuedMessage&);
+ virtual void dequeued(const QueuedMessage&);
+ virtual bool isEnqueued(const QueuedMessage&);
+ QPID_BROKER_EXTERN void update(qpid::framing::FieldTable& settings);
+ uint32_t getMaxCount() const { return maxCount; }
+ uint64_t getMaxSize() const { return maxSize; }
+ void encode(framing::Buffer& buffer) const;
+ void decode ( framing::Buffer& buffer );
+ uint32_t encodedSize() const;
+ virtual void getPendingDequeues(Messages& result);
+
+ static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings);
+ static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT);
+ static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const qpid::framing::FieldTable& settings);
+ static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT);
+ static std::string getType(const qpid::framing::FieldTable& settings);
+ static void setDefaultMaxSize(uint64_t);
+ friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&,
+ const QueuePolicy&);
+ protected:
+ const std::string name;
+
+ QueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT);
+
+ virtual bool checkLimit(boost::intrusive_ptr<Message> msg);
+ void enqueued(uint64_t size);
+ void dequeued(uint64_t size);
+};
+
+
+class FlowToDiskPolicy : public QueuePolicy
+{
+ public:
+ FlowToDiskPolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize);
+ bool checkLimit(boost::intrusive_ptr<Message> msg);
+};
+
+class RingQueuePolicy : public QueuePolicy
+{
+ public:
+ RingQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = RING);
+ void enqueued(const QueuedMessage&);
+ void dequeued(const QueuedMessage&);
+ bool isEnqueued(const QueuedMessage&);
+ bool checkLimit(boost::intrusive_ptr<Message> msg);
+ void getPendingDequeues(Messages& result);
+ private:
+ Messages pendingDequeues;
+ Messages queue;
+ const bool strict;
+
+ bool find(const QueuedMessage&, Messages&, bool remove);
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp
new file mode 100644
index 0000000000..135a3543d9
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/log/Statement.h"
+#include <sstream>
+#include <assert.h>
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+using std::string;
+
+QueueRegistry::QueueRegistry(Broker* b) :
+ counter(1), store(0), events(0), parent(0), lastNode(false), broker(b) {}
+
+QueueRegistry::~QueueRegistry(){}
+
+std::pair<Queue::shared_ptr, bool>
+QueueRegistry::declare(const string& declareName, bool durable,
+ bool autoDelete, const OwnershipToken* owner,
+ boost::shared_ptr<Exchange> alternate,
+ const qpid::framing::FieldTable& arguments,
+ bool recovering/*true if this declare is a
+ result of recovering queue
+ definition from persistente
+ record*/)
+{
+ RWlock::ScopedWlock locker(lock);
+ string name = declareName.empty() ? generateName() : declareName;
+ assert(!name.empty());
+ QueueMap::iterator i = queues.find(name);
+
+ if (i == queues.end()) {
+ Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker));
+ if (alternate) {
+ queue->setAlternateExchange(alternate);//need to do this *before* create
+ alternate->incAlternateUsers();
+ }
+ if (!recovering) {
+ //apply settings & create persistent record if required
+ queue->create(arguments);
+ } else {
+ //i.e. recovering a queue for which we already have a persistent record
+ queue->configure(arguments);
+ }
+ queues[name] = queue;
+ if (lastNode) queue->setLastNodeFailure();
+
+ return std::pair<Queue::shared_ptr, bool>(queue, true);
+ } else {
+ return std::pair<Queue::shared_ptr, bool>(i->second, false);
+ }
+}
+
+void QueueRegistry::destroyLH (const string& name){
+ queues.erase(name);
+}
+
+void QueueRegistry::destroy (const string& name){
+ RWlock::ScopedWlock locker(lock);
+ destroyLH (name);
+}
+
+Queue::shared_ptr QueueRegistry::find(const string& name){
+ RWlock::ScopedRlock locker(lock);
+ QueueMap::iterator i = queues.find(name);
+
+ if (i == queues.end()) {
+ return Queue::shared_ptr();
+ } else {
+ return i->second;
+ }
+}
+
+string QueueRegistry::generateName(){
+ string name;
+ do {
+ std::stringstream ss;
+ ss << "tmp_" << counter++;
+ name = ss.str();
+ // Thread safety: Private function, only called with lock held
+ // so this is OK.
+ } while(queues.find(name) != queues.end());
+ return name;
+}
+
+void QueueRegistry::setStore (MessageStore* _store)
+{
+ store = _store;
+}
+
+MessageStore* QueueRegistry::getStore() const {
+ return store;
+}
+
+void QueueRegistry::updateQueueClusterState(bool _lastNode)
+{
+ RWlock::ScopedRlock locker(lock);
+ for (QueueMap::iterator i = queues.begin(); i != queues.end(); i++) {
+ if (_lastNode){
+ i->second->setLastNodeFailure();
+ } else {
+ i->second->clearLastNodeFailure();
+ }
+ }
+ lastNode = _lastNode;
+}
diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.h b/qpid/cpp/src/qpid/broker/QueueRegistry.h
new file mode 100644
index 0000000000..8a32a64f05
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueueRegistry.h
@@ -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.
+ *
+ */
+#ifndef _QueueRegistry_
+#define _QueueRegistry_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/framing/FieldTable.h"
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <algorithm>
+#include <map>
+
+namespace qpid {
+namespace broker {
+
+class Queue;
+class QueueEvents;
+class Exchange;
+class OwnershipToken;
+class Broker;
+class MessageStore;
+
+/**
+ * A registry of queues indexed by queue name.
+ *
+ * Queues are reference counted using shared_ptr to ensure that they
+ * are deleted when and only when they are no longer in use.
+ *
+ */
+class QueueRegistry {
+ public:
+ QPID_BROKER_EXTERN QueueRegistry(Broker* b = 0);
+ QPID_BROKER_EXTERN ~QueueRegistry();
+
+ /**
+ * Declare a queue.
+ *
+ * @return The queue and a boolean flag which is true if the queue
+ * was created by this declare call false if it already existed.
+ */
+ QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> declare(
+ const std::string& name,
+ bool durable = false,
+ bool autodelete = false,
+ const OwnershipToken* owner = 0,
+ boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(),
+ const qpid::framing::FieldTable& args = framing::FieldTable(),
+ bool recovering = false);
+
+ /**
+ * Destroy the named queue.
+ *
+ * Note: if the queue is in use it is not actually destroyed until
+ * all shared_ptrs to it are destroyed. During that time it is
+ * possible that a new queue with the same name may be
+ * created. This should not create any problems as the new and
+ * old queues exist independently. The registry has
+ * forgotten the old queue so there can be no confusion for
+ * subsequent calls to find or declare with the same name.
+ *
+ */
+ QPID_BROKER_EXTERN void destroy(const std::string& name);
+ template <class Test> bool destroyIf(const std::string& name, Test test)
+ {
+ qpid::sys::RWlock::ScopedWlock locker(lock);
+ if (test()) {
+ destroyLH (name);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Find the named queue. Return 0 if not found.
+ */
+ QPID_BROKER_EXTERN boost::shared_ptr<Queue> find(const std::string& name);
+
+ /**
+ * Generate unique queue name.
+ */
+ std::string generateName();
+
+ /**
+ * Set the store to use. May only be called once.
+ */
+ void setStore (MessageStore*);
+
+ /**
+ * Return the message store used.
+ */
+ MessageStore* getStore() const;
+
+ /**
+ * Register the manageable parent for declared queues
+ */
+ void setParent (management::Manageable* _parent) { parent = _parent; }
+
+ /** Call f for each queue in the registry. */
+ template <class F> void eachQueue(F f) const {
+ qpid::sys::RWlock::ScopedRlock l(lock);
+ for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i)
+ f(i->second);
+ }
+
+ /**
+ * Change queue mode when cluster size drops to 1 node, expands again
+ * in practice allows flow queue to disk when last name to be exectuted
+ */
+ void updateQueueClusterState(bool lastNode);
+
+private:
+ typedef std::map<std::string, boost::shared_ptr<Queue> > QueueMap;
+ QueueMap queues;
+ mutable qpid::sys::RWlock lock;
+ int counter;
+ MessageStore* store;
+ QueueEvents* events;
+ management::Manageable* parent;
+ bool lastNode; //used to set mode on queue declare
+ Broker* broker;
+
+ //destroy impl that assumes lock is already held:
+ void destroyLH (const std::string& name);
+};
+
+
+}} // namespace qpid::broker
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/QueuedMessage.h b/qpid/cpp/src/qpid/broker/QueuedMessage.h
new file mode 100644
index 0000000000..35e48b11f3
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/QueuedMessage.h
@@ -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.
+ *
+ */
+#ifndef _QueuedMessage_
+#define _QueuedMessage_
+
+#include "qpid/broker/Message.h"
+
+namespace qpid {
+namespace broker {
+
+class Queue;
+
+struct QueuedMessage
+{
+ boost::intrusive_ptr<Message> payload;
+ framing::SequenceNumber position;
+ Queue* queue;
+
+ QueuedMessage() : queue(0) {}
+ QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) :
+ payload(msg), position(sn), queue(q) {}
+ QueuedMessage(Queue* q) : queue(q) {}
+
+};
+ inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) { return a.position < b.position; }
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RateFlowcontrol.h b/qpid/cpp/src/qpid/broker/RateFlowcontrol.h
new file mode 100644
index 0000000000..99f9d2c0c4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RateFlowcontrol.h
@@ -0,0 +1,105 @@
+#ifndef broker_RateFlowcontrol_h
+#define broker_RateFlowcontrol_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+#include "qpid/sys/IntegerTypes.h"
+
+#include <algorithm>
+
+namespace qpid {
+namespace broker {
+
+// Class to keep track of issuing flow control to make sure that the peer doesn't exceed
+// a given message rate
+//
+// Create the object with the target rate
+// Then call sendCredit() whenever credit is issued to the peer
+// Call receivedMessage() whenever a message is received, it returns the credit to issue.
+//
+// sentCredit() be sensibly called with a 0 parameter to indicate
+// that we sent credit but treat it as if the value was 0 (we may do this at the start of the connection
+// to allow our peer to send messages)
+//
+// receivedMessage() can be called with 0 to indicate that we've not received a message, but
+// tell me what credit I can send.
+class RateFlowcontrol {
+ uint32_t rate; // messages per second
+ uint32_t maxCredit; // max credit issued to client (issued at start)
+ uint32_t requestedCredit;
+ qpid::sys::AbsTime creditSent;
+
+public:
+ RateFlowcontrol(uint32_t r) :
+ rate(r),
+ maxCredit(0),
+ requestedCredit(0),
+ creditSent(qpid::sys::FAR_FUTURE)
+ {}
+
+ uint32_t getRate() const {
+ return rate;
+ }
+ void sentCredit(const qpid::sys::AbsTime& t, uint32_t credit);
+ uint32_t receivedMessage(const qpid::sys::AbsTime& t, uint32_t msgs);
+ uint32_t availableCredit(const qpid::sys::AbsTime& t);
+ bool flowStopped() const;
+};
+
+inline void RateFlowcontrol::sentCredit(const qpid::sys::AbsTime& t, uint32_t credit) {
+ // If the client isn't currently requesting credit (ie it's not sent us anything yet) then
+ // this credit goes to the max credit held by the client (it can't go to reduce credit
+ // less than 0)
+ int32_t nextRequestedCredit = requestedCredit - credit;
+ if ( nextRequestedCredit<0 ) {
+ requestedCredit = 0;
+ maxCredit -= nextRequestedCredit;
+ } else {
+ requestedCredit = nextRequestedCredit;
+ }
+ creditSent = t;
+}
+
+inline uint32_t RateFlowcontrol::availableCredit(const qpid::sys::AbsTime& t) {
+ qpid::sys::Duration d(creditSent, t);
+ // Could be -ve before first sentCredit
+ int64_t toSend = std::min(rate * d / qpid::sys::TIME_SEC, static_cast<int64_t>(requestedCredit));
+ return toSend > 0 ? toSend : 0;
+}
+
+inline uint32_t RateFlowcontrol::receivedMessage(const qpid::sys::AbsTime& t, uint32_t msgs) {
+ requestedCredit +=msgs;
+ // Don't send credit for every message, only send if more than 0.5s since last credit or
+ // we've got less than .25 of the max left (heuristic)
+ return requestedCredit*4 >= maxCredit*3 || qpid::sys::Duration(creditSent, t) >= 500*qpid::sys::TIME_MSEC
+ ? availableCredit(t)
+ : 0;
+}
+
+inline bool RateFlowcontrol::flowStopped() const {
+ return requestedCredit >= maxCredit;
+}
+
+}}
+
+#endif // broker_RateFlowcontrol_h
diff --git a/qpid/cpp/src/qpid/broker/RateTracker.cpp b/qpid/cpp/src/qpid/broker/RateTracker.cpp
new file mode 100644
index 0000000000..048349b658
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RateTracker.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/RateTracker.h"
+
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::sys::TIME_SEC;
+
+namespace qpid {
+namespace broker {
+
+RateTracker::RateTracker() : currentCount(0), lastCount(0), lastTime(AbsTime::now()) {}
+
+RateTracker& RateTracker::operator++()
+{
+ ++currentCount;
+ return *this;
+}
+
+double RateTracker::sampleRatePerSecond()
+{
+ int32_t increment = currentCount - lastCount;
+ AbsTime now = AbsTime::now();
+ Duration interval(lastTime, now);
+ lastCount = currentCount;
+ lastTime = now;
+ //if sampling at higher frequency than supported, will just return the number of increments
+ if (interval < TIME_SEC) return increment;
+ else if (increment == 0) return 0;
+ else return increment / (interval / TIME_SEC);
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/RateTracker.h b/qpid/cpp/src/qpid/broker/RateTracker.h
new file mode 100644
index 0000000000..0c20b37312
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RateTracker.h
@@ -0,0 +1,57 @@
+#ifndef QPID_BROKER_RATETRACKER_H
+#define QPID_BROKER_RATETRACKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Simple rate tracker: represents some value that can be incremented,
+ * then can periodcially sample the rate of increments.
+ */
+class RateTracker
+{
+ public:
+ RateTracker();
+ /**
+ * Increments the count being tracked. Can be called concurrently
+ * with other calls to this operator as well as with calls to
+ * sampleRatePerSecond().
+ */
+ RateTracker& operator++();
+ /**
+ * Returns the rate of increments per second since last
+ * called. Calls to this method should be serialised, but can be
+ * called concurrently with the increment operator
+ */
+ double sampleRatePerSecond();
+ private:
+ volatile int32_t currentCount;
+ int32_t lastCount;
+ qpid::sys::AbsTime lastTime;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_RATETRACKER_H*/
diff --git a/qpid/cpp/src/qpid/broker/RecoverableConfig.h b/qpid/cpp/src/qpid/broker/RecoverableConfig.h
new file mode 100644
index 0000000000..838a8582dc
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoverableConfig.h
@@ -0,0 +1,45 @@
+#ifndef _broker_RecoverableConfig_h
+#define _broker_RecoverableConfig_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface through which configurations are recovered.
+ */
+class RecoverableConfig
+{
+public:
+ typedef boost::shared_ptr<RecoverableConfig> shared_ptr;
+
+ virtual void setPersistenceId(uint64_t id) = 0;
+ virtual ~RecoverableConfig() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoverableExchange.h b/qpid/cpp/src/qpid/broker/RecoverableExchange.h
new file mode 100644
index 0000000000..ca6cc1541e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoverableExchange.h
@@ -0,0 +1,52 @@
+#ifndef _broker_RecoverableExchange_h
+#define _broker_RecoverableExchange_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include "qpid/framing/FieldTable.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface through which bindings are recovered.
+ */
+class RecoverableExchange
+{
+public:
+ typedef boost::shared_ptr<RecoverableExchange> shared_ptr;
+
+ virtual void setPersistenceId(uint64_t id) = 0;
+ /**
+ * Recover binding. Nb: queue must have been recovered earlier.
+ */
+ virtual void bind(const std::string& queue,
+ const std::string& routingKey,
+ qpid::framing::FieldTable& args) = 0;
+ virtual ~RecoverableExchange() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoverableMessage.h b/qpid/cpp/src/qpid/broker/RecoverableMessage.h
new file mode 100644
index 0000000000..c98857ceb0
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoverableMessage.h
@@ -0,0 +1,59 @@
+#ifndef _broker_RecoverableMessage_h
+#define _broker_RecoverableMessage_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface through which messages are reloaded on recovery.
+ */
+class RecoverableMessage
+{
+public:
+ typedef boost::shared_ptr<RecoverableMessage> shared_ptr;
+ virtual void setPersistenceId(uint64_t id) = 0;
+ virtual void setRedelivered() = 0;
+ /**
+ * Used by store to determine whether to load content on recovery
+ * or let message load its own content as and when it requires it.
+ *
+ * @returns true if the content of the message should be loaded
+ */
+ virtual bool loadContent(uint64_t available) = 0;
+ /**
+ * Loads the content held in the supplied buffer (may do checking
+ * of length as necessary)
+ */
+ virtual void decodeContent(framing::Buffer& buffer) = 0;
+ virtual ~RecoverableMessage() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoverableQueue.h b/qpid/cpp/src/qpid/broker/RecoverableQueue.h
new file mode 100644
index 0000000000..49f05f97a1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoverableQueue.h
@@ -0,0 +1,59 @@
+#ifndef _broker_RecoverableQueue_h
+#define _broker_RecoverableQueue_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/RecoverableMessage.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+class ExternalQueueStore;
+
+/**
+ * The interface through which messages are added back to queues on
+ * recovery.
+ */
+class RecoverableQueue
+{
+public:
+ typedef boost::shared_ptr<RecoverableQueue> shared_ptr;
+
+ virtual void setPersistenceId(uint64_t id) = 0;
+ virtual uint64_t getPersistenceId() const = 0;
+ /**
+ * Used during recovery to add stored messages back to the queue
+ */
+ virtual void recover(RecoverableMessage::shared_ptr msg) = 0;
+ virtual ~RecoverableQueue() {};
+
+ virtual const std::string& getName() const = 0;
+ virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0;
+ virtual ExternalQueueStore* getExternalQueueStore() const = 0;
+
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoverableTransaction.h b/qpid/cpp/src/qpid/broker/RecoverableTransaction.h
new file mode 100644
index 0000000000..1b7d94bd1a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoverableTransaction.h
@@ -0,0 +1,49 @@
+#ifndef _broker_RecoverableTransaction_h
+#define _broker_RecoverableTransaction_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+
+#include "qpid/broker/RecoverableMessage.h"
+#include "qpid/broker/RecoverableQueue.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * The interface through which prepared 2pc transactions are
+ * recovered.
+ */
+class RecoverableTransaction
+{
+public:
+ typedef boost::shared_ptr<RecoverableTransaction> shared_ptr;
+ virtual void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0;
+ virtual void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0;
+ virtual ~RecoverableTransaction() {};
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp b/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp
new file mode 100644
index 0000000000..cd6735328f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/RecoveredDequeue.h"
+
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+
+RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg)
+{
+ queue->recoverPrepared(msg);
+}
+
+bool RecoveredDequeue::prepare(TransactionContext*) throw()
+{
+ //should never be called; transaction has already prepared if an enqueue is recovered
+ return false;
+}
+
+void RecoveredDequeue::commit() throw()
+{
+ queue->enqueueAborted(msg);
+}
+
+void RecoveredDequeue::rollback() throw()
+{
+ queue->process(msg);
+}
+
diff --git a/qpid/cpp/src/qpid/broker/RecoveredDequeue.h b/qpid/cpp/src/qpid/broker/RecoveredDequeue.h
new file mode 100644
index 0000000000..66e66f1d5f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveredDequeue.h
@@ -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.
+ *
+ */
+#ifndef _RecoveredDequeue_
+#define _RecoveredDequeue_
+
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/TxOp.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <list>
+
+namespace qpid {
+ namespace broker {
+ class RecoveredDequeue : public TxOp{
+ boost::shared_ptr<Queue> queue;
+ boost::intrusive_ptr<Message> msg;
+
+ public:
+ RecoveredDequeue(boost::shared_ptr<Queue> queue, boost::intrusive_ptr<Message> msg);
+ virtual bool prepare(TransactionContext* ctxt) throw();
+ virtual void commit() throw();
+ virtual void rollback() throw();
+ virtual ~RecoveredDequeue(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ boost::shared_ptr<Queue> getQueue() const { return queue; }
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp
new file mode 100644
index 0000000000..6d2eaee6c4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/RecoveredEnqueue.h"
+
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+
+RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg)
+{
+ queue->recoverPrepared(msg);
+}
+
+bool RecoveredEnqueue::prepare(TransactionContext*) throw(){
+ //should never be called; transaction has already prepared if an enqueue is recovered
+ return false;
+}
+
+void RecoveredEnqueue::commit() throw(){
+ queue->process(msg);
+}
+
+void RecoveredEnqueue::rollback() throw(){
+ queue->enqueueAborted(msg);
+}
+
diff --git a/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h
new file mode 100644
index 0000000000..5f718001d5
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h
@@ -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.
+ *
+ */
+#ifndef _RecoveredEnqueue_
+#define _RecoveredEnqueue_
+
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/TxOp.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <list>
+
+namespace qpid {
+namespace broker {
+class RecoveredEnqueue : public TxOp{
+ boost::shared_ptr<Queue> queue;
+ boost::intrusive_ptr<Message> msg;
+
+ public:
+ RecoveredEnqueue(boost::shared_ptr<Queue> queue, boost::intrusive_ptr<Message> msg);
+ virtual bool prepare(TransactionContext* ctxt) throw();
+ virtual void commit() throw();
+ virtual void rollback() throw();
+ virtual ~RecoveredEnqueue(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ boost::shared_ptr<Queue> getQueue() const { return queue; }
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+
+};
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoveryManager.h b/qpid/cpp/src/qpid/broker/RecoveryManager.h
new file mode 100644
index 0000000000..2929e92250
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveryManager.h
@@ -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.
+ *
+ */
+#ifndef _RecoveryManager_
+#define _RecoveryManager_
+
+#include "qpid/broker/RecoverableExchange.h"
+#include "qpid/broker/RecoverableQueue.h"
+#include "qpid/broker/RecoverableMessage.h"
+#include "qpid/broker/RecoverableTransaction.h"
+#include "qpid/broker/RecoverableConfig.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/framing/Buffer.h"
+
+namespace qpid {
+namespace broker {
+
+class RecoveryManager{
+ public:
+ virtual ~RecoveryManager(){}
+ virtual RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer) = 0;
+ virtual RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer) = 0;
+ virtual RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer) = 0;
+ virtual RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid,
+ std::auto_ptr<TPCTransactionContext> txn) = 0;
+ virtual RecoverableConfig::shared_ptr recoverConfig(framing::Buffer& buffer) = 0;
+
+ virtual void recoveryComplete() = 0;
+};
+
+class Recoverable {
+ public:
+ virtual ~Recoverable() {}
+
+ /**
+ * Request recovery of queue and message state.
+ */
+ virtual void recover(RecoveryManager& recoverer) = 0;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
new file mode 100644
index 0000000000..d08409695e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
@@ -0,0 +1,278 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/RecoveryManagerImpl.h"
+
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/RecoveredEnqueue.h"
+#include "qpid/broker/RecoveredDequeue.h"
+#include "qpid/framing/reply_exceptions.h"
+
+using boost::dynamic_pointer_cast;
+using boost::intrusive_ptr;
+using std::string;
+
+namespace qpid {
+namespace broker {
+
+RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links,
+ DtxManager& _dtxMgr)
+ : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr) {}
+
+RecoveryManagerImpl::~RecoveryManagerImpl() {}
+
+class RecoverableMessageImpl : public RecoverableMessage
+{
+ intrusive_ptr<Message> msg;
+public:
+ RecoverableMessageImpl(const intrusive_ptr<Message>& _msg);
+ ~RecoverableMessageImpl() {};
+ void setPersistenceId(uint64_t id);
+ void setRedelivered();
+ bool loadContent(uint64_t available);
+ void decodeContent(framing::Buffer& buffer);
+ void recover(Queue::shared_ptr queue);
+ void enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue);
+ void dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue);
+};
+
+class RecoverableQueueImpl : public RecoverableQueue
+{
+ Queue::shared_ptr queue;
+public:
+ RecoverableQueueImpl(const boost::shared_ptr<Queue>& _queue) : queue(_queue) {}
+ ~RecoverableQueueImpl() {};
+ void setPersistenceId(uint64_t id);
+ uint64_t getPersistenceId() const;
+ const std::string& getName() const;
+ void setExternalQueueStore(ExternalQueueStore* inst);
+ ExternalQueueStore* getExternalQueueStore() const;
+ void recover(RecoverableMessage::shared_ptr msg);
+ void enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg);
+ void dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg);
+};
+
+class RecoverableExchangeImpl : public RecoverableExchange
+{
+ Exchange::shared_ptr exchange;
+ QueueRegistry& queues;
+public:
+ RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {}
+ void setPersistenceId(uint64_t id);
+ void bind(const std::string& queue, const std::string& routingKey, qpid::framing::FieldTable& args);
+};
+
+class RecoverableConfigImpl : public RecoverableConfig
+{
+ Link::shared_ptr link;
+ Bridge::shared_ptr bridge;
+public:
+ RecoverableConfigImpl(Link::shared_ptr _link) : link(_link) {}
+ RecoverableConfigImpl(Bridge::shared_ptr _bridge) : bridge(_bridge) {}
+ void setPersistenceId(uint64_t id);
+};
+
+class RecoverableTransactionImpl : public RecoverableTransaction
+{
+ DtxBuffer::shared_ptr buffer;
+public:
+ RecoverableTransactionImpl(DtxBuffer::shared_ptr _buffer) : buffer(_buffer) {}
+ void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message);
+ void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message);
+};
+
+RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Buffer& buffer)
+{
+ Exchange::shared_ptr e = Exchange::decode(exchanges, buffer);
+ if (e) {
+ return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(e, queues));
+ } else {
+ return RecoverableExchange::shared_ptr();
+ }
+}
+
+RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer)
+{
+ Queue::shared_ptr queue = Queue::restore(queues, buffer);
+ try {
+ Exchange::shared_ptr exchange = exchanges.getDefault();
+ if (exchange) {
+ exchange->bind(queue, queue->getName(), 0);
+ queue->bound(exchange->getName(), queue->getName(), framing::FieldTable());
+ }
+ } catch (const framing::NotFoundException& /*e*/) {
+ //assume no default exchange has been declared
+ }
+ return RecoverableQueue::shared_ptr(new RecoverableQueueImpl(queue));
+}
+
+RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer)
+{
+ boost::intrusive_ptr<Message> message(new Message());
+ message->decodeHeader(buffer);
+ return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(message));
+}
+
+RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid,
+ std::auto_ptr<TPCTransactionContext> txn)
+{
+ DtxBuffer::shared_ptr buffer(new DtxBuffer());
+ dtxMgr.recover(xid, txn, buffer);
+ return RecoverableTransaction::shared_ptr(new RecoverableTransactionImpl(buffer));
+}
+
+RecoverableConfig::shared_ptr RecoveryManagerImpl::recoverConfig(framing::Buffer& buffer)
+{
+ string kind;
+
+ buffer.getShortString (kind);
+ if (kind == "link")
+ return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Link::decode (links, buffer)));
+ else if (kind == "bridge")
+ return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Bridge::decode (links, buffer)));
+
+ return RecoverableConfig::shared_ptr(); // TODO: raise an exception instead
+}
+
+void RecoveryManagerImpl::recoveryComplete()
+{
+ //notify all queues and exchanges
+ queues.eachQueue(boost::bind(&Queue::recoveryComplete, _1, boost::ref(exchanges)));
+ exchanges.eachExchange(boost::bind(&Exchange::recoveryComplete, _1, boost::ref(exchanges)));
+}
+
+RecoverableMessageImpl:: RecoverableMessageImpl(const intrusive_ptr<Message>& _msg) : msg(_msg)
+{
+ if (!msg->isPersistent()) {
+ msg->forcePersistent(); // set so that message will get dequeued from store.
+ }
+}
+
+bool RecoverableMessageImpl::loadContent(uint64_t /*available*/)
+{
+ return true;
+}
+
+void RecoverableMessageImpl::decodeContent(framing::Buffer& buffer)
+{
+ msg->decodeContent(buffer);
+}
+
+void RecoverableMessageImpl::recover(Queue::shared_ptr queue)
+{
+ queue->recover(msg);
+}
+
+void RecoverableMessageImpl::setPersistenceId(uint64_t id)
+{
+ msg->setPersistenceId(id);
+}
+
+void RecoverableMessageImpl::setRedelivered()
+{
+ msg->redeliver();
+}
+
+void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg)
+{
+ dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue);
+}
+
+void RecoverableQueueImpl::setPersistenceId(uint64_t id)
+{
+ queue->setPersistenceId(id);
+}
+
+uint64_t RecoverableQueueImpl::getPersistenceId() const
+{
+ return queue->getPersistenceId();
+}
+
+const std::string& RecoverableQueueImpl::getName() const
+{
+ return queue->getName();
+}
+
+void RecoverableQueueImpl::setExternalQueueStore(ExternalQueueStore* inst)
+{
+ queue->setExternalQueueStore(inst);
+}
+
+ExternalQueueStore* RecoverableQueueImpl::getExternalQueueStore() const
+{
+ return queue->getExternalQueueStore();
+}
+
+void RecoverableExchangeImpl::setPersistenceId(uint64_t id)
+{
+ exchange->setPersistenceId(id);
+}
+
+void RecoverableConfigImpl::setPersistenceId(uint64_t id)
+{
+ if (link.get())
+ link->setPersistenceId(id);
+ else if (bridge.get())
+ bridge->setPersistenceId(id);
+}
+
+void RecoverableExchangeImpl::bind(const string& queueName,
+ const string& key,
+ framing::FieldTable& args)
+{
+ Queue::shared_ptr queue = queues.find(queueName);
+ exchange->bind(queue, key, &args);
+ queue->bound(exchange->getName(), key, args);
+}
+
+void RecoverableMessageImpl::dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue)
+{
+ buffer->enlist(TxOp::shared_ptr(new RecoveredDequeue(queue, msg)));
+}
+
+void RecoverableMessageImpl::enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue)
+{
+ buffer->enlist(TxOp::shared_ptr(new RecoveredEnqueue(queue, msg)));
+}
+
+void RecoverableQueueImpl::dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message)
+{
+ dynamic_pointer_cast<RecoverableMessageImpl>(message)->dequeue(buffer, queue);
+}
+
+void RecoverableQueueImpl::enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message)
+{
+ dynamic_pointer_cast<RecoverableMessageImpl>(message)->enqueue(buffer, queue);
+}
+
+void RecoverableTransactionImpl::dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message)
+{
+ dynamic_pointer_cast<RecoverableQueueImpl>(queue)->dequeue(buffer, message);
+}
+
+void RecoverableTransactionImpl::enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message)
+{
+ dynamic_pointer_cast<RecoverableQueueImpl>(queue)->enqueue(buffer, message);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h
new file mode 100644
index 0000000000..1ad7892b13
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h
@@ -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.
+ *
+ */
+#ifndef _RecoveryManagerImpl_
+#define _RecoveryManagerImpl_
+
+#include <list>
+#include "qpid/broker/DtxManager.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/RecoveryManager.h"
+
+namespace qpid {
+namespace broker {
+
+ class RecoveryManagerImpl : public RecoveryManager{
+ QueueRegistry& queues;
+ ExchangeRegistry& exchanges;
+ LinkRegistry& links;
+ DtxManager& dtxMgr;
+ public:
+ RecoveryManagerImpl(QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links,
+ DtxManager& dtxMgr);
+ ~RecoveryManagerImpl();
+
+ RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer);
+ RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer);
+ RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer);
+ RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid,
+ std::auto_ptr<TPCTransactionContext> txn);
+ RecoverableConfig::shared_ptr recoverConfig(framing::Buffer& buffer);
+ void recoveryComplete();
+ };
+
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/RetryList.cpp b/qpid/cpp/src/qpid/broker/RetryList.cpp
new file mode 100644
index 0000000000..b0477dd0f7
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RetryList.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/RetryList.h"
+
+namespace qpid {
+namespace broker {
+
+RetryList::RetryList() : urlIndex(0), addressIndex(0) {}
+
+void RetryList::reset(const std::vector<Url>& u)
+{
+ urls = u;
+ urlIndex = addressIndex = 0;//reset indices
+}
+
+bool RetryList::next(Address& address)
+{
+ while (urlIndex < urls.size()) {
+ if (addressIndex < urls[urlIndex].size()) {
+ address = urls[urlIndex][addressIndex++];
+ return true;
+ }
+ urlIndex++;
+ addressIndex = 0;
+ }
+ urlIndex = addressIndex = 0;//reset indices
+ return false;
+}
+
+std::ostream& operator<<(std::ostream& os, const RetryList& l)
+{
+ for (size_t i = 0; i < l.urls.size(); i++) {
+ os << l.urls[i] << " ";
+ }
+ return os;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/RetryList.h b/qpid/cpp/src/qpid/broker/RetryList.h
new file mode 100644
index 0000000000..242a7d2122
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/RetryList.h
@@ -0,0 +1,54 @@
+#ifndef QPID_BROKER_RETRYLIST_H
+#define QPID_BROKER_RETRYLIST_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/Address.h"
+#include "qpid/Url.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Simple utility for managing a list of urls to try on reconnecting a
+ * link. Currently only supports TCP urls.
+ */
+class RetryList
+{
+ public:
+ QPID_BROKER_EXTERN RetryList();
+ QPID_BROKER_EXTERN void reset(const std::vector<Url>& urls);
+ QPID_BROKER_EXTERN bool next(Address& address);
+ private:
+ std::vector<Url> urls;
+ size_t urlIndex;
+ size_t addressIndex;
+
+ friend std::ostream& operator<<(std::ostream& os, const RetryList& l);
+};
+
+std::ostream& operator<<(std::ostream& os, const RetryList& l);
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_RETRYLIST_H*/
diff --git a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp
new file mode 100644
index 0000000000..acdb4934d4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/SecuritySettings.h"
+#include <boost/format.hpp>
+
+#if HAVE_SASL
+#include <sasl/sasl.h>
+#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
+using qpid::sys::cyrus::CyrusSecurityLayer;
+#endif
+
+using namespace qpid::framing;
+using qpid::sys::SecurityLayer;
+using qpid::sys::SecuritySettings;
+using boost::format;
+using boost::str;
+
+
+namespace qpid {
+namespace broker {
+
+
+
+class NullAuthenticator : public SaslAuthenticator
+{
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection client;
+ std::string realm;
+ const bool encrypt;
+public:
+ NullAuthenticator(Connection& connection, bool encrypt);
+ ~NullAuthenticator();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string&) {}
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+};
+
+#if HAVE_SASL
+
+
+
+class CyrusAuthenticator : public SaslAuthenticator
+{
+ sasl_conn_t *sasl_conn;
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection client;
+ const bool encrypt;
+
+ void processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len);
+ bool getUsername(std::string& uid);
+
+public:
+ CyrusAuthenticator(Connection& connection, bool encrypt);
+ ~CyrusAuthenticator();
+ void init();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string& response);
+ void getError(std::string& error);
+ void getUid(std::string& uid) { getUsername(uid); }
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+};
+
+bool SaslAuthenticator::available(void) {
+ return true;
+}
+
+// Initialize the SASL mechanism; throw if it fails.
+void SaslAuthenticator::init(const std::string& saslName, std::string const & saslConfigPath )
+{
+ // Check if we have a version of SASL that supports sasl_set_path()
+#if (SASL_VERSION_FULL >= ((2<<16)|(1<<8)|22))
+ // If we are not given a sasl path, do nothing and allow the default to be used.
+ if ( ! saslConfigPath.empty() ) {
+ int code = sasl_set_path(SASL_PATH_TYPE_CONFIG,
+ const_cast<char *>(saslConfigPath.c_str()));
+ if(SASL_OK != code)
+ throw Exception(QPID_MSG("SASL: sasl_set_path failed [" << code << "] " ));
+ QPID_LOG(info, "SASL: config path set to " << saslConfigPath );
+ }
+#endif
+
+ int code = sasl_server_init(NULL, saslName.c_str());
+ if (code != SASL_OK) {
+ // TODO: Figure out who owns the char* returned by
+ // sasl_errstring, though it probably does not matter much
+ throw Exception(sasl_errstring(code, NULL, NULL));
+ }
+}
+
+void SaslAuthenticator::fini(void)
+{
+ sasl_done();
+}
+
+#else
+
+typedef NullAuthenticator CyrusAuthenticator;
+
+bool SaslAuthenticator::available(void) {
+ return false;
+}
+
+void SaslAuthenticator::init(const std::string& /*saslName*/, std::string const & /*saslConfigPath*/ )
+{
+ throw Exception("Requested authentication but SASL unavailable");
+}
+
+void SaslAuthenticator::fini(void)
+{
+ return;
+}
+
+#endif
+
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c, bool isShadow )
+{
+ if (c.getBroker().getOptions().auth) {
+ if ( isShadow )
+ return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ else
+ return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ } else {
+ QPID_LOG(debug, "SASL: No Authentication Performed");
+ return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ }
+}
+
+
+NullAuthenticator::NullAuthenticator(Connection& c, bool e) : connection(c), client(c.getOutput()),
+ realm(c.getBroker().getOptions().realm), encrypt(e) {}
+NullAuthenticator::~NullAuthenticator() {}
+
+void NullAuthenticator::getMechanisms(Array& mechanisms)
+{
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS")));
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("PLAIN")));//useful for testing
+}
+
+void NullAuthenticator::start(const string& mechanism, const string& response)
+{
+ if (encrypt) {
+#if HAVE_SASL
+ // encryption required - check to see if we are running over an
+ // encrypted SSL connection.
+ SecuritySettings external = connection.getExternalSecuritySettings();
+ sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf;
+ if (external_ssf < 1) // < 1 == unencrypted
+#endif
+ {
+ QPID_LOG(error, "Rejected un-encrypted connection.");
+ throw ConnectionForcedException("Connection must be encrypted.");
+ }
+ }
+ if (mechanism == "PLAIN") { // Old behavior
+ if (response.size() > 0) {
+ string uid;
+ string::size_type i = response.find((char)0);
+ if (i == 0 && response.size() > 1) {
+ //no authorization id; use authentication id
+ i = response.find((char)0, 1);
+ if (i != string::npos) uid = response.substr(1, i-1);
+ } else if (i != string::npos) {
+ //authorization id is first null delimited field
+ uid = response.substr(0, i);
+ }//else not a valid SASL PLAIN response, throw error?
+ if (!uid.empty()) {
+ //append realm if it has not already been added
+ i = uid.find(realm);
+ if (i == string::npos || realm.size() + i < uid.size()) {
+ uid = str(format("%1%@%2%") % uid % realm);
+ }
+ connection.setUserId(uid);
+ }
+ }
+ } else {
+ connection.setUserId("anonymous");
+ }
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax());
+}
+
+
+std::auto_ptr<SecurityLayer> NullAuthenticator::getSecurityLayer(uint16_t)
+{
+ std::auto_ptr<SecurityLayer> securityLayer;
+ return securityLayer;
+}
+
+
+#if HAVE_SASL
+
+CyrusAuthenticator::CyrusAuthenticator(Connection& c, bool _encrypt) :
+ sasl_conn(0), connection(c), client(c.getOutput()), encrypt(_encrypt)
+{
+ init();
+}
+
+void CyrusAuthenticator::init()
+{
+ /* Next to the service name, which specifies the
+ * /etc/sasl2/<service name>.conf file to read, the realm is
+ * currently the most important argument below. When
+ * performing authentication the user that is authenticating
+ * will be looked up in a specific realm. If none is given
+ * then the realm defaults to the hostname, which can cause
+ * confusion when the daemon is run on different hosts that
+ * may be logically sharing a realm (aka a user domain). This
+ * is especially important for SASL PLAIN authentication,
+ * which cannot specify a realm for the user that is
+ * authenticating.
+ */
+ int code;
+
+ const char *realm = connection.getBroker().getOptions().realm.c_str();
+ code = sasl_server_new(BROKER_SASL_NAME, /* Service name */
+ NULL, /* Server FQDN, gethostname() */
+ realm, /* Authentication realm */
+ NULL, /* Local IP, needed for some mechanism */
+ NULL, /* Remote IP, needed for some mechanism */
+ NULL, /* Callbacks */
+ 0, /* Connection flags */
+ &sasl_conn);
+
+ if (SASL_OK != code) {
+ QPID_LOG(error, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw ConnectionForcedException("Unable to perform authentication");
+ }
+
+ sasl_security_properties_t secprops;
+
+ //TODO: should the actual SSF values be configurable here?
+ secprops.min_ssf = encrypt ? 10: 0;
+ secprops.max_ssf = 256;
+
+ // If the transport provides encryption, notify the SASL library of
+ // the key length and set the ssf range to prevent double encryption.
+ SecuritySettings external = connection.getExternalSecuritySettings();
+ QPID_LOG(debug, "External ssf=" << external.ssf << " and auth=" << external.authid);
+ sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf;
+ if (external_ssf) {
+ int result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, &external_ssf);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result));
+ }
+
+ secprops.max_ssf = secprops.min_ssf = 0;
+ }
+
+ QPID_LOG(debug, "min_ssf: " << secprops.min_ssf <<
+ ", max_ssf: " << secprops.max_ssf <<
+ ", external_ssf: " << external_ssf );
+
+ if (!external.authid.empty()) {
+ const char* external_authid = external.authid.c_str();
+ int result = sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, external_authid);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result));
+ }
+
+ QPID_LOG(debug, "external auth detected and set to " << external_authid);
+ }
+
+ secprops.maxbufsize = 65535;
+ secprops.property_names = 0;
+ secprops.property_values = 0;
+ secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */
+ /*
+ * The nodict flag restricts SASL authentication mechanisms
+ * to those that are not susceptible to dictionary attacks.
+ * They are:
+ * SRP
+ * PASSDSS-3DES-1
+ * EXTERNAL
+ */
+ if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY;
+ int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << result));
+ }
+}
+
+CyrusAuthenticator::~CyrusAuthenticator()
+{
+ if (sasl_conn) {
+ sasl_dispose(&sasl_conn);
+ sasl_conn = 0;
+ }
+}
+
+void CyrusAuthenticator::getError(string& error)
+{
+ error = string(sasl_errdetail(sasl_conn));
+}
+
+bool CyrusAuthenticator::getUsername(string& uid)
+{
+ const void* ptr;
+
+ int code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr);
+ if (SASL_OK == code) {
+ uid = string(const_cast<char*>(static_cast<const char*>(ptr)));
+ return true;
+ } else {
+ QPID_LOG(warning, "Failed to retrieve sasl username");
+ return false;
+ }
+}
+
+void CyrusAuthenticator::getMechanisms(Array& mechanisms)
+{
+ const char *separator = " ";
+ const char *list;
+ unsigned int list_len;
+ int count;
+
+ int code = sasl_listmech(sasl_conn, NULL,
+ "", separator, "",
+ &list, &list_len,
+ &count);
+
+ if (SASL_OK != code) {
+ QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw ConnectionForcedException("Mechanism listing failed");
+ } else {
+ string mechanism;
+ unsigned int start;
+ unsigned int end;
+
+ QPID_LOG(info, "SASL: Mechanism list: " << list);
+
+ end = 0;
+ do {
+ start = end;
+
+ // Seek to end of next mechanism
+ while (end < list_len && separator[0] != list[end])
+ end++;
+
+ // Record the mechanism
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string(list, start, end - start))));
+ end++;
+ } while (end < list_len);
+ }
+}
+
+void CyrusAuthenticator::start(const string& mechanism, const string& response)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ QPID_LOG(debug, "SASL: Starting authentication with mechanism: " << mechanism);
+ int code = sasl_server_start(sasl_conn,
+ mechanism.c_str(),
+ response.c_str(), response.length(),
+ &challenge, &challenge_len);
+
+ processAuthenticationStep(code, challenge, challenge_len);
+}
+
+void CyrusAuthenticator::step(const string& response)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ int code = sasl_server_step(sasl_conn,
+ response.c_str(), response.length(),
+ &challenge, &challenge_len);
+
+ processAuthenticationStep(code, challenge, challenge_len);
+}
+
+void CyrusAuthenticator::processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len)
+{
+ if (SASL_OK == code) {
+ std::string uid;
+ if (!getUsername(uid)) {
+ // TODO: Change this to an exception signaling
+ // authentication failure, when one is available
+ throw ConnectionForcedException("Authenticated username unavailable");
+ }
+ QPID_LOG(info, connection.getMgmtId() << " SASL: Authentication succeeded for: " << uid);
+
+ connection.setUserId(uid);
+
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax());
+ } else if (SASL_CONTINUE == code) {
+ string challenge_str(challenge, challenge_len);
+
+ QPID_LOG(debug, "SASL: sending challenge to client");
+
+ client.secure(challenge_str);
+ } else {
+ std::string uid;
+ if (!getUsername(uid)) {
+ QPID_LOG(info, "SASL: Authentication failed (no username available):" << sasl_errdetail(sasl_conn));
+ } else {
+ QPID_LOG(info, "SASL: Authentication failed for " << uid << ":" << sasl_errdetail(sasl_conn));
+ }
+
+ // TODO: Change to more specific exceptions, when they are
+ // available
+ switch (code) {
+ case SASL_NOMECH:
+ throw ConnectionForcedException("Unsupported mechanism");
+ break;
+ case SASL_TRYAGAIN:
+ throw ConnectionForcedException("Transient failure, try again");
+ break;
+ default:
+ throw ConnectionForcedException("Authentication failed");
+ break;
+ }
+ }
+}
+
+std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFrameSize)
+{
+
+ const void* value(0);
+ int result = sasl_getprop(sasl_conn, SASL_SSF, &value);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(sasl_conn)));
+ }
+ uint ssf = *(reinterpret_cast<const unsigned*>(value));
+ std::auto_ptr<SecurityLayer> securityLayer;
+ if (ssf) {
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize));
+ }
+ return securityLayer;
+}
+
+#endif
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/SaslAuthenticator.h b/qpid/cpp/src/qpid/broker/SaslAuthenticator.h
new file mode 100644
index 0000000000..cfbe1a0cd1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.h
@@ -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.
+ *
+ */
+#ifndef _SaslAuthenticator_
+#define _SaslAuthenticator_
+
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/SecurityLayer.h"
+#include <memory>
+#include <vector>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace broker {
+
+class Connection;
+
+class SaslAuthenticator
+{
+public:
+ virtual ~SaslAuthenticator() {}
+ virtual void getMechanisms(framing::Array& mechanisms) = 0;
+ virtual void start(const std::string& mechanism, const std::string& response) = 0;
+ virtual void step(const std::string& response) = 0;
+ virtual void getUid(std::string&) {}
+ virtual bool getUsername(std::string&) { return false; };
+ virtual void getError(std::string&) {}
+ virtual std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(uint16_t maxFrameSize) = 0;
+
+ static bool available(void);
+
+ // Initialize the SASL mechanism; throw if it fails.
+ static void init(const std::string& saslName, std::string const & saslConfigPath );
+ static void fini(void);
+
+ static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection, bool isShadow);
+
+ virtual void callUserIdCallbacks() { }
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/SecureConnection.cpp b/qpid/cpp/src/qpid/broker/SecureConnection.cpp
new file mode 100644
index 0000000000..5c1ebf3e8b
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SecureConnection.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/SecureConnection.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/framing/reply_exceptions.h"
+
+namespace qpid {
+namespace broker {
+
+using qpid::sys::SecurityLayer;
+
+SecureConnection::SecureConnection() : secured(false) {}
+
+size_t SecureConnection::decode(const char* buffer, size_t size)
+{
+ if (!secured && securityLayer.get()) {
+ //security layer comes into effect on first read after its
+ //activated
+ secured = true;
+ }
+ if (secured) {
+ return securityLayer->decode(buffer, size);
+ } else {
+ return codec->decode(buffer, size);
+ }
+}
+
+size_t SecureConnection::encode(const char* buffer, size_t size)
+{
+ if (secured) {
+ return securityLayer->encode(buffer, size);
+ } else {
+ return codec->encode(buffer, size);
+ }
+}
+
+bool SecureConnection::canEncode()
+{
+ if (secured) return securityLayer->canEncode();
+ else return codec->canEncode();
+}
+
+void SecureConnection::closed()
+{
+ codec->closed();
+}
+
+bool SecureConnection::isClosed() const
+{
+ return codec->isClosed();
+}
+
+framing::ProtocolVersion SecureConnection::getVersion() const
+{
+ return codec->getVersion();
+}
+
+void SecureConnection:: setCodec(std::auto_ptr<ConnectionCodec> c)
+{
+ codec = c;
+}
+
+void SecureConnection::activateSecurityLayer(std::auto_ptr<SecurityLayer> sl, bool secureImmediately)
+{
+ securityLayer = sl;
+ securityLayer->init(codec.get());
+
+ if ( secureImmediately )
+ secured = true;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SecureConnection.h b/qpid/cpp/src/qpid/broker/SecureConnection.h
new file mode 100644
index 0000000000..1547faae1e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SecureConnection.h
@@ -0,0 +1,60 @@
+#ifndef QPID_BROKER_SECURECONNECTION_H
+#define QPID_BROKER_SECURECONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ConnectionCodec.h"
+#include <memory>
+
+namespace qpid {
+
+namespace sys {
+class SecurityLayer;
+}
+
+namespace broker {
+
+/**
+ * A ConnectionCodec 'wrapper' that allows a connection to be
+ * 'secured' e.g. encrypted based on settings negotiatiated at the
+ * time of establishment.
+ */
+class SecureConnection : public qpid::sys::ConnectionCodec
+{
+ public:
+ SecureConnection();
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+ void closed();
+ bool isClosed() const;
+ framing::ProtocolVersion getVersion() const;
+ void setCodec(std::auto_ptr<ConnectionCodec>);
+ void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>, bool secureImmediately=false);
+ private:
+ std::auto_ptr<ConnectionCodec> codec;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+ bool secured;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_SECURECONNECTION_H*/
diff --git a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp
new file mode 100644
index 0000000000..754b443c22
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/SecureConnectionFactory.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/SecureConnection.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+
+using framing::ProtocolVersion;
+using qpid::sys::SecuritySettings;
+typedef std::auto_ptr<amqp_0_10::Connection> CodecPtr;
+typedef std::auto_ptr<SecureConnection> SecureConnectionPtr;
+typedef std::auto_ptr<Connection> ConnectionPtr;
+typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr;
+
+SecureConnectionFactory::SecureConnectionFactory(Broker& b) : broker(b) {}
+
+sys::ConnectionCodec*
+SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ if (broker.getConnectionCounter().allowConnection())
+ {
+ QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused");
+ return 0;
+ }
+ if (v == ProtocolVersion(0, 10)) {
+ SecureConnectionPtr sc(new SecureConnection());
+ CodecPtr c(new amqp_0_10::Connection(out, id, false));
+ ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, false));
+ i->setSecureConnection(sc.get());
+ c->setInputHandler(InputPtr(i.release()));
+ sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c));
+ return sc.release();
+ }
+ return 0;
+}
+
+sys::ConnectionCodec*
+SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ // used to create connections from one broker to another
+ SecureConnectionPtr sc(new SecureConnection());
+ CodecPtr c(new amqp_0_10::Connection(out, id, true));
+ ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, true ));
+ i->setSecureConnection(sc.get());
+ c->setInputHandler(InputPtr(i.release()));
+ sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c));
+ return sc.release();
+}
+
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h
new file mode 100644
index 0000000000..8a04dfcb15
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h
@@ -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.
+ *
+ */
+#ifndef _SecureConnectionFactory_
+#define _SecureConnectionFactory_
+
+#include "qpid/sys/ConnectionCodec.h"
+
+namespace qpid {
+namespace broker {
+class Broker;
+
+class SecureConnectionFactory : public sys::ConnectionCodec::Factory
+{
+ public:
+ SecureConnectionFactory(Broker& b);
+
+ sys::ConnectionCodec*
+ create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id,
+ const qpid::sys::SecuritySettings&);
+
+ sys::ConnectionCodec*
+ create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&);
+
+ private:
+ Broker& broker;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp
new file mode 100644
index 0000000000..ce86253f4a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp
@@ -0,0 +1,823 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/DtxAck.h"
+#include "qpid/broker/DtxTimeout.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/SessionContext.h"
+#include "qpid/broker/SessionOutputException.h"
+#include "qpid/broker/TxAccept.h"
+#include "qpid/broker/TxPublish.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/IsInSequenceSet.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/ClusterSafe.h"
+#include "qpid/ptr_map.h"
+#include "qpid/broker/AclModule.h"
+
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include <iostream>
+#include <sstream>
+#include <algorithm>
+#include <functional>
+
+#include <assert.h>
+
+namespace qpid {
+namespace broker {
+
+using namespace std;
+using boost::intrusive_ptr;
+using boost::bind;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using qpid::ptr_map_ptr;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss)
+ : session(ss),
+ deliveryAdapter(da),
+ tagGenerator("sgen"),
+ dtxSelected(false),
+ authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()),
+ userID(getSession().getConnection().getUserId()),
+ userName(getSession().getConnection().getUserId().substr(0,getSession().getConnection().getUserId().find('@'))),
+ isDefaultRealm(userID.find('@') != std::string::npos && getSession().getBroker().getOptions().realm == userID.substr(userID.find('@')+1,userID.size())),
+ closeComplete(false)
+{
+ acl = getSession().getBroker().getAcl();
+}
+
+SemanticState::~SemanticState() {
+ closed();
+}
+
+void SemanticState::closed() {
+ if (!closeComplete) {
+ //prevent requeued messages being redelivered to consumers
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
+ disable(i->second);
+ }
+ if (dtxBuffer.get()) {
+ dtxBuffer->fail();
+ }
+ recover(true);
+
+ //now unsubscribe, which may trigger queue deletion and thus
+ //needs to occur after the requeueing of unacked messages
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
+ unsubscribe(i->second);
+ }
+ closeComplete = true;
+ }
+}
+
+bool SemanticState::exists(const string& consumerTag){
+ return consumers.find(consumerTag) != consumers.end();
+}
+
+void SemanticState::consume(const string& tag,
+ Queue::shared_ptr queue, bool ackRequired, bool acquire,
+ bool exclusive, const string& resumeId, uint64_t resumeTtl, const FieldTable& arguments)
+{
+ ConsumerImpl::shared_ptr c(new ConsumerImpl(this, tag, queue, ackRequired, acquire, exclusive, resumeId, resumeTtl, arguments));
+ queue->consume(c, exclusive);//may throw exception
+ consumers[tag] = c;
+}
+
+bool SemanticState::cancel(const string& tag)
+{
+ ConsumerImplMap::iterator i = consumers.find(tag);
+ if (i != consumers.end()) {
+ cancel(i->second);
+ consumers.erase(i);
+ //should cancel all unacked messages for this consumer so that
+ //they are not redelivered on recovery
+ for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::cancel, _1, tag));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+void SemanticState::startTx()
+{
+ txBuffer = TxBuffer::shared_ptr(new TxBuffer());
+}
+
+void SemanticState::commit(MessageStore* const store)
+{
+ if (!txBuffer) throw
+ CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions"));
+
+ TxOp::shared_ptr txAck(static_cast<TxOp*>(new TxAccept(accumulatedAck, unacked)));
+ txBuffer->enlist(txAck);
+ if (txBuffer->commitLocal(store)) {
+ accumulatedAck.clear();
+ } else {
+ throw InternalErrorException(QPID_MSG("Commit failed"));
+ }
+}
+
+void SemanticState::rollback()
+{
+ if (!txBuffer)
+ throw CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions"));
+
+ txBuffer->rollback();
+ accumulatedAck.clear();
+}
+
+void SemanticState::selectDtx()
+{
+ dtxSelected = true;
+}
+
+void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join)
+{
+ if (!dtxSelected) {
+ throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx"));
+ }
+ dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid));
+ txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
+ if (join) {
+ mgr.join(xid, dtxBuffer);
+ } else {
+ mgr.start(xid, dtxBuffer);
+ }
+}
+
+void SemanticState::endDtx(const std::string& xid, bool fail)
+{
+ if (!dtxBuffer) {
+ throw IllegalStateException(QPID_MSG("xid " << xid << " not associated with this session"));
+ }
+ if (dtxBuffer->getXid() != xid) {
+ throw CommandInvalidException(
+ QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on end"));
+
+ }
+
+ txBuffer.reset();//ops on this session no longer transactional
+
+ checkDtxTimeout();
+ if (fail) {
+ dtxBuffer->fail();
+ } else {
+ dtxBuffer->markEnded();
+ }
+ dtxBuffer.reset();
+}
+
+void SemanticState::suspendDtx(const std::string& xid)
+{
+ if (dtxBuffer->getXid() != xid) {
+ throw CommandInvalidException(
+ QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on suspend"));
+ }
+ txBuffer.reset();//ops on this session no longer transactional
+
+ checkDtxTimeout();
+ dtxBuffer->setSuspended(true);
+ suspendedXids[xid] = dtxBuffer;
+ dtxBuffer.reset();
+}
+
+void SemanticState::resumeDtx(const std::string& xid)
+{
+ if (!dtxSelected) {
+ throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx"));
+ }
+
+ dtxBuffer = suspendedXids[xid];
+ if (!dtxBuffer) {
+ throw CommandInvalidException(QPID_MSG("xid " << xid << " not attached"));
+ } else {
+ suspendedXids.erase(xid);
+ }
+
+ if (dtxBuffer->getXid() != xid) {
+ throw CommandInvalidException(
+ QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on resume"));
+
+ }
+ if (!dtxBuffer->isSuspended()) {
+ throw CommandInvalidException(QPID_MSG("xid " << xid << " not suspended"));
+ }
+
+ checkDtxTimeout();
+ dtxBuffer->setSuspended(false);
+ txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
+}
+
+void SemanticState::checkDtxTimeout()
+{
+ if (dtxBuffer->isExpired()) {
+ dtxBuffer.reset();
+ throw DtxTimeoutException();
+ }
+}
+
+void SemanticState::record(const DeliveryRecord& delivery)
+{
+ unacked.push_back(delivery);
+}
+
+const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
+
+SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
+ const string& _name,
+ Queue::shared_ptr _queue,
+ bool ack,
+ bool _acquire,
+ bool _exclusive,
+ const string& _resumeId,
+ uint64_t _resumeTtl,
+ const framing::FieldTable& _arguments
+
+
+) :
+ Consumer(_acquire),
+ parent(_parent),
+ name(_name),
+ queue(_queue),
+ ackExpected(ack),
+ acquire(_acquire),
+ blocked(true),
+ windowing(true),
+ exclusive(_exclusive),
+ resumeId(_resumeId),
+ resumeTtl(_resumeTtl),
+ arguments(_arguments),
+ msgCredit(0),
+ byteCredit(0),
+ notifyEnabled(true),
+ syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)),
+ deliveryCount(0),
+ mgmtObject(0)
+{
+ if (parent != 0 && queue.get() != 0 && queue->GetManagementObject() !=0)
+ {
+ ManagementAgent* agent = parent->session.getBroker().getManagementAgent();
+ qpid::management::Manageable* ms = dynamic_cast<qpid::management::Manageable*> (&(parent->session));
+
+ if (agent != 0)
+ {
+ mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId() ,name,
+ !acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments));
+ agent->addObject (mgmtObject);
+ mgmtObject->set_creditMode("WINDOW");
+ }
+ }
+}
+
+ManagementObject* SemanticState::ConsumerImpl::GetManagementObject (void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]");
+
+ return status;
+}
+
+
+OwnershipToken* SemanticState::ConsumerImpl::getSession()
+{
+ return &(parent->session);
+}
+
+bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg)
+{
+ assertClusterSafe();
+ allocateCredit(msg.payload);
+ DeliveryRecord record(msg, queue, name, acquire, !ackExpected, windowing);
+ bool sync = syncFrequency && ++deliveryCount >= syncFrequency;
+ if (sync) deliveryCount = 0;//reset
+ parent->deliver(record, sync);
+ if (!ackExpected && acquire) record.setEnded();//allows message to be released now its been delivered
+ if (windowing || ackExpected || !acquire) {
+ parent->record(record);
+ }
+ if (acquire && !ackExpected) {
+ queue->dequeue(0, msg);
+ }
+ if (mgmtObject) { mgmtObject->inc_delivered(); }
+ return true;
+}
+
+bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message>)
+{
+ return true;
+}
+
+bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg)
+{
+ assertClusterSafe();
+ // FIXME aconway 2009-06-08: if we have byte & message credit but
+ // checkCredit fails because the message is to big, we should
+ // remain on queue's listener list for possible smaller messages
+ // in future.
+ //
+ blocked = !(filter(msg) && checkCredit(msg));
+ return !blocked;
+}
+
+namespace {
+struct ConsumerName {
+ const SemanticState::ConsumerImpl& consumer;
+ ConsumerName(const SemanticState::ConsumerImpl& ci) : consumer(ci) {}
+};
+
+ostream& operator<<(ostream& o, const ConsumerName& pc) {
+ return o << pc.consumer.getName() << " on "
+ << pc.consumer.getParent().getSession().getSessionId();
+}
+}
+
+void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg)
+{
+ assertClusterSafe();
+ uint32_t originalMsgCredit = msgCredit;
+ uint32_t originalByteCredit = byteCredit;
+ if (msgCredit != 0xFFFFFFFF) {
+ msgCredit--;
+ }
+ if (byteCredit != 0xFFFFFFFF) {
+ byteCredit -= msg->getRequiredCredit();
+ }
+ QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this)
+ << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit
+ << " now bytes: " << byteCredit << " msgs: " << msgCredit);
+
+}
+
+bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg)
+{
+ bool enoughCredit = msgCredit > 0 &&
+ (byteCredit == 0xFFFFFFFF || byteCredit >= msg->getRequiredCredit());
+ QPID_LOG(debug, (enoughCredit ? "Sufficient credit for " : "Insufficient credit for ")
+ << ConsumerName(*this)
+ << ", have bytes: " << byteCredit << " msgs: " << msgCredit
+ << ", need " << msg->getRequiredCredit() << " bytes");
+ return enoughCredit;
+}
+
+SemanticState::ConsumerImpl::~ConsumerImpl()
+{
+ if (mgmtObject != 0)
+ mgmtObject->resourceDestroy ();
+}
+
+void SemanticState::disable(ConsumerImpl::shared_ptr c)
+{
+ c->disableNotify();
+ if (session.isAttached())
+ session.getConnection().outputTasks.removeOutputTask(c.get());
+}
+
+void SemanticState::unsubscribe(ConsumerImpl::shared_ptr c)
+{
+ Queue::shared_ptr queue = c->getQueue();
+ if(queue) {
+ queue->cancel(c);
+ if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
+ Queue::tryAutoDelete(session.getBroker(), queue);
+ }
+ }
+}
+
+void SemanticState::cancel(ConsumerImpl::shared_ptr c)
+{
+ disable(c);
+ unsubscribe(c);
+}
+
+void SemanticState::handle(intrusive_ptr<Message> msg) {
+ if (txBuffer.get()) {
+ TxPublish* deliverable(new TxPublish(msg));
+ TxOp::shared_ptr op(deliverable);
+ route(msg, *deliverable);
+ txBuffer->enlist(op);
+ } else {
+ DeliverableMessage deliverable(msg);
+ route(msg, deliverable);
+ if (msg->isContentReleaseRequested()) {
+ // NOTE: The log messages in this section are used for flow-to-disk testing (which checks the log for the
+ // presence of these messages). Do not change these without also checking these tests.
+ if (msg->isContentReleaseBlocked()) {
+ QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" <<
+ std::hex << msg->getPersistenceId() << std::dec << ": Content release blocked");
+ } else {
+ msg->releaseContent();
+ QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" <<
+ std::hex << msg->getPersistenceId() << std::dec << ": Content released");
+ }
+ }
+ }
+}
+
+namespace
+{
+const std::string nullstring;
+}
+
+void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) {
+ msg->setTimestamp(getSession().getBroker().getExpiryPolicy());
+
+ std::string exchangeName = msg->getExchangeName();
+ if (!cacheExchange || cacheExchange->getName() != exchangeName || cacheExchange->isDestroyed())
+ cacheExchange = session.getBroker().getExchanges().get(exchangeName);
+ cacheExchange->setProperties(msg);
+
+ /* verify the userid if specified: */
+ std::string id =
+ msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring;
+
+ if (authMsg && !id.empty() && !(id == userID || (isDefaultRealm && id == userName)))
+ {
+ QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id);
+ throw UnauthorizedAccessException(QPID_MSG("authorised user id : " << userID << " but user id in message declared as " << id));
+ }
+
+ if (acl && acl->doTransferAcl())
+ {
+ if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() ))
+ throw UnauthorizedAccessException(QPID_MSG(userID << " cannot publish to " <<
+ exchangeName << " with routing-key " << msg->getRoutingKey()));
+ }
+
+ cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders());
+
+ if (!strategy.delivered) {
+ //TODO:if discard-unroutable, just drop it
+ //TODO:else if accept-mode is explicit, reject it
+ //else route it to alternate exchange
+ if (cacheExchange->getAlternate()) {
+ cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders());
+ }
+ if (!strategy.delivered) {
+ msg->destroy();
+ }
+ }
+
+}
+
+void SemanticState::requestDispatch()
+{
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++)
+ i->second->requestDispatch();
+}
+
+void SemanticState::ConsumerImpl::requestDispatch()
+{
+ assertClusterSafe();
+ if (blocked) {
+ parent->session.getConnection().outputTasks.addOutputTask(this);
+ parent->session.getConnection().outputTasks.activateOutput();
+ blocked = false;
+ }
+}
+
+bool SemanticState::complete(DeliveryRecord& delivery)
+{
+ ConsumerImplMap::iterator i = consumers.find(delivery.getTag());
+ if (i != consumers.end()) {
+ i->second->complete(delivery);
+ }
+ return delivery.isRedundant();
+}
+
+void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery)
+{
+ if (!delivery.isComplete()) {
+ delivery.complete();
+ if (windowing) {
+ if (msgCredit != 0xFFFFFFFF) msgCredit++;
+ if (byteCredit != 0xFFFFFFFF) byteCredit += delivery.getCredit();
+ }
+ }
+}
+
+void SemanticState::recover(bool requeue)
+{
+ if(requeue){
+ //take copy and clear unacked as requeue may result in redelivery to this session
+ //which will in turn result in additions to unacked
+ DeliveryRecords copy = unacked;
+ unacked.clear();
+ for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue));
+ }else{
+ for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this));
+ //unconfirmed messages re redelivered and therefore have their
+ //id adjusted, confirmed messages are not and so the ordering
+ //w.r.t id is lost
+ sort(unacked.begin(), unacked.end());
+ }
+}
+
+void SemanticState::deliver(DeliveryRecord& msg, bool sync)
+{
+ return deliveryAdapter.deliver(msg, sync);
+}
+
+SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination)
+{
+ ConsumerImplMap::iterator i = consumers.find(destination);
+ if (i == consumers.end()) {
+ throw NotFoundException(QPID_MSG("Unknown destination " << destination));
+ } else {
+ return *(i->second);
+ }
+}
+
+void SemanticState::setWindowMode(const std::string& destination)
+{
+ find(destination).setWindowMode();
+}
+
+void SemanticState::setCreditMode(const std::string& destination)
+{
+ find(destination).setCreditMode();
+}
+
+void SemanticState::addByteCredit(const std::string& destination, uint32_t value)
+{
+ ConsumerImpl& c = find(destination);
+ c.addByteCredit(value);
+ c.requestDispatch();
+}
+
+
+void SemanticState::addMessageCredit(const std::string& destination, uint32_t value)
+{
+ ConsumerImpl& c = find(destination);
+ c.addMessageCredit(value);
+ c.requestDispatch();
+}
+
+void SemanticState::flush(const std::string& destination)
+{
+ find(destination).flush();
+}
+
+
+void SemanticState::stop(const std::string& destination)
+{
+ find(destination).stop();
+}
+
+void SemanticState::ConsumerImpl::setWindowMode()
+{
+ assertClusterSafe();
+ windowing = true;
+ if (mgmtObject){
+ mgmtObject->set_creditMode("WINDOW");
+ }
+}
+
+void SemanticState::ConsumerImpl::setCreditMode()
+{
+ assertClusterSafe();
+ windowing = false;
+ if (mgmtObject){
+ mgmtObject->set_creditMode("CREDIT");
+ }
+}
+
+void SemanticState::ConsumerImpl::addByteCredit(uint32_t value)
+{
+ assertClusterSafe();
+ if (byteCredit != 0xFFFFFFFF) {
+ if (value == 0xFFFFFFFF) byteCredit = value;
+ else byteCredit += value;
+ }
+}
+
+void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value)
+{
+ assertClusterSafe();
+ if (msgCredit != 0xFFFFFFFF) {
+ if (value == 0xFFFFFFFF) msgCredit = value;
+ else msgCredit += value;
+ }
+}
+
+bool SemanticState::ConsumerImpl::haveCredit()
+{
+ if (msgCredit && byteCredit) {
+ return true;
+ } else {
+ blocked = true;
+ return false;
+ }
+}
+
+void SemanticState::ConsumerImpl::flush()
+{
+ while(haveCredit() && queue->dispatch(shared_from_this()))
+ ;
+ stop();
+}
+
+void SemanticState::ConsumerImpl::stop()
+{
+ assertClusterSafe();
+ msgCredit = 0;
+ byteCredit = 0;
+}
+
+Queue::shared_ptr SemanticState::getQueue(const string& name) const {
+ Queue::shared_ptr queue;
+ if (name.empty()) {
+ throw NotAllowedException(QPID_MSG("No queue name specified."));
+ } else {
+ queue = session.getBroker().getQueues().find(name);
+ if (!queue)
+ throw NotFoundException(QPID_MSG("Queue not found: "<<name));
+ }
+ return queue;
+}
+
+AckRange SemanticState::findRange(DeliveryId first, DeliveryId last)
+{
+ return DeliveryRecord::findRange(unacked, first, last);
+}
+
+void SemanticState::acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired)
+{
+ AckRange range = findRange(first, last);
+ for_each(range.start, range.end, AcquireFunctor(acquired));
+}
+
+void SemanticState::release(DeliveryId first, DeliveryId last, bool setRedelivered)
+{
+ AckRange range = findRange(first, last);
+ //release results in the message being added to the head so want
+ //to release in reverse order to keep the original transfer order
+ DeliveryRecords::reverse_iterator start(range.end);
+ DeliveryRecords::reverse_iterator end(range.start);
+ for_each(start, end, boost::bind(&DeliveryRecord::release, _1, setRedelivered));
+}
+
+void SemanticState::reject(DeliveryId first, DeliveryId last)
+{
+ AckRange range = findRange(first, last);
+ for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::reject));
+ //may need to remove the delivery records as well
+ for (DeliveryRecords::iterator i = range.start; i != unacked.end() && i->getId() <= last; ) {
+ if (i->isRedundant()) i = unacked.erase(i);
+ else i++;
+ }
+}
+
+bool SemanticState::ConsumerImpl::doOutput()
+{
+ try {
+ return haveCredit() && queue->dispatch(shared_from_this());
+ } catch (const SessionException& e) {
+ throw SessionOutputException(e, parent->session.getChannel());
+ }
+}
+
+void SemanticState::ConsumerImpl::enableNotify()
+{
+ Mutex::ScopedLock l(lock);
+ assertClusterSafe();
+ notifyEnabled = true;
+}
+
+void SemanticState::ConsumerImpl::disableNotify()
+{
+ Mutex::ScopedLock l(lock);
+ notifyEnabled = false;
+}
+
+bool SemanticState::ConsumerImpl::isNotifyEnabled() const {
+ Mutex::ScopedLock l(lock);
+ return notifyEnabled;
+}
+
+void SemanticState::ConsumerImpl::notify()
+{
+ Mutex::ScopedLock l(lock);
+ assertClusterSafe();
+ if (notifyEnabled) {
+ parent->session.getConnection().outputTasks.addOutputTask(this);
+ parent->session.getConnection().outputTasks.activateOutput();
+ }
+}
+
+
+// Test that a DeliveryRecord's ID is in a sequence set and some other
+// predicate on DeliveryRecord holds.
+template <class Predicate> struct IsInSequenceSetAnd {
+ IsInSequenceSet isInSet;
+ Predicate predicate;
+ IsInSequenceSetAnd(const SequenceSet& s, Predicate p) : isInSet(s), predicate(p) {}
+ bool operator()(DeliveryRecord& dr) {
+ return isInSet(dr.getId()) && predicate(dr);
+ }
+};
+
+template<class Predicate> IsInSequenceSetAnd<Predicate>
+isInSequenceSetAnd(const SequenceSet& s, Predicate p) {
+ return IsInSequenceSetAnd<Predicate>(s,p);
+}
+
+void SemanticState::accepted(const SequenceSet& commands) {
+ assertClusterSafe();
+ if (txBuffer.get()) {
+ //in transactional mode, don't dequeue or remove, just
+ //maintain set of acknowledged messages:
+ accumulatedAck.add(commands);
+
+ if (dtxBuffer.get()) {
+ //if enlisted in a dtx, copy the relevant slice from
+ //unacked and record it against that transaction
+ TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked));
+ accumulatedAck.clear();
+ dtxBuffer->enlist(txAck);
+
+ //mark the relevant messages as 'ended' in unacked
+ //if the messages are already completed, they can be
+ //removed from the record
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&DeliveryRecord::setEnded, _1)));
+ unacked.erase(removed, unacked.end());
+ }
+ } else {
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&DeliveryRecord::accept, _1,
+ (TransactionContext*) 0)));
+ unacked.erase(removed, unacked.end());
+ }
+}
+
+void SemanticState::completed(const SequenceSet& commands) {
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&SemanticState::complete, this, _1)));
+ unacked.erase(removed, unacked.end());
+ requestDispatch();
+}
+
+void SemanticState::attached()
+{
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
+ i->second->enableNotify();
+ session.getConnection().outputTasks.addOutputTask(i->second.get());
+ }
+ session.getConnection().outputTasks.activateOutput();
+}
+
+void SemanticState::detached()
+{
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
+ i->second->disableNotify();
+ session.getConnection().outputTasks.removeOutputTask(i->second.get());
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h
new file mode 100644
index 0000000000..8c69d6b89b
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SemanticState.h
@@ -0,0 +1,257 @@
+#ifndef QPID_BROKER_SEMANTICSTATE_H
+#define QPID_BROKER_SEMANTICSTATE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Consumer.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/DeliveryAdapter.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/DtxBuffer.h"
+#include "qpid/broker/DtxManager.h"
+#include "qpid/broker/NameGenerator.h"
+#include "qpid/broker/TxBuffer.h"
+
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/AggregateOutput.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/broker/AclModule.h"
+#include "qmf/org/apache/qpid/broker/Subscription.h"
+
+#include <list>
+#include <map>
+#include <vector>
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/cast.hpp>
+
+namespace qpid {
+namespace broker {
+
+class SessionContext;
+
+/**
+ *
+ * SemanticState implements the behavior of a Session, especially the
+ * state of consumers subscribed to queues. The code for ConsumerImpl
+ * is also in SemanticState.cpp
+ *
+ * SemanticState holds the AMQP Execution and Model state of an open
+ * session, whether attached to a channel or suspended. It is not
+ * dependent on any specific AMQP version.
+ *
+ * Message delivery is driven by ConsumerImpl::doOutput(), which is
+ * called when a client's socket is ready to write data.
+ *
+ */
+class SemanticState : private boost::noncopyable {
+ public:
+ class ConsumerImpl : public Consumer, public sys::OutputTask,
+ public boost::enable_shared_from_this<ConsumerImpl>,
+ public management::Manageable
+ {
+ mutable qpid::sys::Mutex lock;
+ SemanticState* const parent;
+ const std::string name;
+ const boost::shared_ptr<Queue> queue;
+ const bool ackExpected;
+ const bool acquire;
+ bool blocked;
+ bool windowing;
+ bool exclusive;
+ std::string resumeId;
+ uint64_t resumeTtl;
+ framing::FieldTable arguments;
+ uint32_t msgCredit;
+ uint32_t byteCredit;
+ bool notifyEnabled;
+ const int syncFrequency;
+ int deliveryCount;
+ qmf::org::apache::qpid::broker::Subscription* mgmtObject;
+
+ bool checkCredit(boost::intrusive_ptr<Message>& msg);
+ void allocateCredit(boost::intrusive_ptr<Message>& msg);
+ bool haveCredit();
+
+ public:
+ typedef boost::shared_ptr<ConsumerImpl> shared_ptr;
+
+ ConsumerImpl(SemanticState* parent,
+ const std::string& name, boost::shared_ptr<Queue> queue,
+ bool ack, bool acquire, bool exclusive,
+ const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments);
+ ~ConsumerImpl();
+ OwnershipToken* getSession();
+ bool deliver(QueuedMessage& msg);
+ bool filter(boost::intrusive_ptr<Message> msg);
+ bool accept(boost::intrusive_ptr<Message> msg);
+
+ void disableNotify();
+ void enableNotify();
+ void notify();
+ bool isNotifyEnabled() const;
+
+ void requestDispatch();
+
+ void setWindowMode();
+ void setCreditMode();
+ void addByteCredit(uint32_t value);
+ void addMessageCredit(uint32_t value);
+ void flush();
+ void stop();
+ void complete(DeliveryRecord&);
+ boost::shared_ptr<Queue> getQueue() const { return queue; }
+ bool isBlocked() const { return blocked; }
+ bool setBlocked(bool set) { std::swap(set, blocked); return set; }
+
+ bool doOutput();
+
+ std::string getName() const { return name; }
+
+ bool isAckExpected() const { return ackExpected; }
+ bool isAcquire() const { return acquire; }
+ bool isWindowing() const { return windowing; }
+ bool isExclusive() const { return exclusive; }
+ uint32_t getMsgCredit() const { return msgCredit; }
+ uint32_t getByteCredit() const { return byteCredit; }
+ std::string getResumeId() const { return resumeId; };
+ uint64_t getResumeTtl() const { return resumeTtl; }
+ const framing::FieldTable& getArguments() const { return arguments; }
+
+ SemanticState& getParent() { return *parent; }
+ const SemanticState& getParent() const { return *parent; }
+ // Manageable entry points
+ management::ManagementObject* GetManagementObject (void) const;
+ management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+ };
+
+ private:
+ typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap;
+ typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap;
+
+ SessionContext& session;
+ DeliveryAdapter& deliveryAdapter;
+ ConsumerImplMap consumers;
+ NameGenerator tagGenerator;
+ DeliveryRecords unacked;
+ TxBuffer::shared_ptr txBuffer;
+ DtxBuffer::shared_ptr dtxBuffer;
+ bool dtxSelected;
+ DtxBufferMap suspendedXids;
+ framing::SequenceSet accumulatedAck;
+ boost::shared_ptr<Exchange> cacheExchange;
+ AclModule* acl;
+ const bool authMsg;
+ const std::string userID;
+ const std::string userName;
+ const bool isDefaultRealm;
+ bool closeComplete;
+
+ void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy);
+ void checkDtxTimeout();
+
+ bool complete(DeliveryRecord&);
+ AckRange findRange(DeliveryId first, DeliveryId last);
+ void requestDispatch();
+ void cancel(ConsumerImpl::shared_ptr);
+ void unsubscribe(ConsumerImpl::shared_ptr);
+ void disable(ConsumerImpl::shared_ptr);
+
+ public:
+ SemanticState(DeliveryAdapter&, SessionContext&);
+ ~SemanticState();
+
+ SessionContext& getSession() { return session; }
+ const SessionContext& getSession() const { return session; }
+
+ ConsumerImpl& find(const std::string& destination);
+
+ /**
+ * Get named queue, never returns 0.
+ * @return: named queue
+ * @exception: ChannelException if no queue of that name is found.
+ * @exception: ConnectionException if name="" and session has no default.
+ */
+ boost::shared_ptr<Queue> getQueue(const std::string& name) const;
+
+ bool exists(const std::string& consumerTag);
+
+ void consume(const std::string& destination,
+ boost::shared_ptr<Queue> queue,
+ bool ackRequired, bool acquire, bool exclusive,
+ const std::string& resumeId=std::string(), uint64_t resumeTtl=0,
+ const framing::FieldTable& = framing::FieldTable());
+
+ bool cancel(const std::string& tag);
+
+ void setWindowMode(const std::string& destination);
+ void setCreditMode(const std::string& destination);
+ void addByteCredit(const std::string& destination, uint32_t value);
+ void addMessageCredit(const std::string& destination, uint32_t value);
+ void flush(const std::string& destination);
+ void stop(const std::string& destination);
+
+ void startTx();
+ void commit(MessageStore* const store);
+ void rollback();
+ void selectDtx();
+ void startDtx(const std::string& xid, DtxManager& mgr, bool join);
+ void endDtx(const std::string& xid, bool fail);
+ void suspendDtx(const std::string& xid);
+ void resumeDtx(const std::string& xid);
+ void recover(bool requeue);
+ void deliver(DeliveryRecord& message, bool sync);
+ void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired);
+ void release(DeliveryId first, DeliveryId last, bool setRedelivered);
+ void reject(DeliveryId first, DeliveryId last);
+ void handle(boost::intrusive_ptr<Message> msg);
+
+ void completed(const framing::SequenceSet& commands);
+ void accepted(const framing::SequenceSet& commands);
+
+ void attached();
+ void detached();
+ void closed();
+
+ // Used by cluster to re-create sessions
+ template <class F> void eachConsumer(F f) {
+ for(ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); ++i)
+ f(i->second);
+ }
+ DeliveryRecords& getUnacked() { return unacked; }
+ framing::SequenceSet getAccumulatedAck() const { return accumulatedAck; }
+ TxBuffer::shared_ptr getTxBuffer() const { return txBuffer; }
+ void setTxBuffer(const TxBuffer::shared_ptr& txb) { txBuffer = txb; }
+ void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; }
+ void record(const DeliveryRecord& delivery);
+};
+
+}} // namespace qpid::broker
+
+
+
+
+#endif /*!QPID_BROKER_SEMANTICSTATE_H*/
diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
new file mode 100644
index 0000000000..63c4b660b2
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -0,0 +1,693 @@
+/*
+ *
+ * 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.
+ *
+ */
+#include "qpid/broker/SessionAdapter.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/broker/SessionState.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
+#include "qmf/org/apache/qpid/broker/EventBind.h"
+#include "qmf/org/apache/qpid/broker/EventUnbind.h"
+#include "qmf/org/apache/qpid/broker/EventSubscribe.h"
+#include "qmf/org/apache/qpid/broker/EventUnsubscribe.h"
+#include <boost/format.hpp>
+#include <boost/cast.hpp>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace broker {
+
+using namespace qpid;
+using namespace qpid::framing;
+using namespace qpid::framing::dtx;
+using namespace qpid::management;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+typedef std::vector<Queue::shared_ptr> QueueVector;
+
+SessionAdapter::SessionAdapter(SemanticState& s) :
+ HandlerImpl(s),
+ exchangeImpl(s),
+ queueImpl(s),
+ messageImpl(s),
+ executionImpl(s),
+ txImpl(s),
+ dtxImpl(s)
+{}
+
+static const std::string _TRUE("true");
+static const std::string _FALSE("false");
+
+void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const string& type,
+ const string& alternateExchange,
+ bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){
+
+ //TODO: implement autoDelete
+ Exchange::shared_ptr alternate;
+ if (!alternateExchange.empty()) {
+ alternate = getBroker().getExchanges().get(alternateExchange);
+ }
+ if(passive){
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ //TODO: why does a passive declare require create
+ //permission? The purpose of the passive flag is to state
+ //that the exchange should *not* created. For
+ //authorisation a passive declare is similar to
+ //exchange-query.
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_TYPE, type));
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << getConnection().getUserId()));
+ }
+ Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange));
+ checkType(actual, type);
+ checkAlternate(actual, alternate);
+ }else{
+ if(exchange.find("amq.") == 0 || exchange.find("qpid.") == 0) {
+ throw framing::NotAllowedException(QPID_MSG("Exchange names beginning with \"amq.\" or \"qpid.\" are reserved. (exchange=\"" << exchange << "\")"));
+ }
+ try{
+ std::pair<Exchange::shared_ptr, bool> response =
+ getBroker().createExchange(exchange, type, durable, alternateExchange, args,
+ getConnection().getUserId(), getConnection().getUrl());
+ if (!response.second) {
+ //exchange already there, not created
+ checkType(response.first, type);
+ checkAlternate(response.first, alternate);
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(),
+ getConnection().getUserId(),
+ exchange,
+ type,
+ alternateExchange,
+ durable,
+ false,
+ ManagementAgent::toMap(args),
+ "existing"));
+ }
+ }catch(UnknownExchangeTypeException& /*e*/){
+ throw NotFoundException(QPID_MSG("Exchange type not implemented: " << type));
+ }
+ }
+}
+
+void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchange, const std::string& type)
+{
+ if (!type.empty() && exchange->getType() != type) {
+ throw NotAllowedException(QPID_MSG("Exchange declared to be of type " << exchange->getType() << ", requested " << type));
+ }
+}
+
+void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate)
+{
+ if (alternate && ((exchange->getAlternate() && alternate != exchange->getAlternate())
+ || !exchange->getAlternate()))
+ throw NotAllowedException(QPID_MSG("Exchange declared with alternate-exchange "
+ << (exchange->getAlternate() ? exchange->getAlternate()->getName() : "<nonexistent>")
+ << ", requested "
+ << alternate->getName()));
+}
+
+void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/)
+{
+ //TODO: implement if-unused
+ getBroker().deleteExchange(name, getConnection().getUserId(), getConnection().getUrl());
+}
+
+ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name)
+{
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange query request from " << getConnection().getUserId()));
+ }
+
+ try {
+ Exchange::shared_ptr exchange(getBroker().getExchanges().get(name));
+ return ExchangeQueryResult(exchange->getType(), exchange->isDurable(), false, exchange->getArgs());
+ } catch (const NotFoundException& /*e*/) {
+ return ExchangeQueryResult("", false, true, FieldTable());
+ }
+}
+
+void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName,
+ const string& exchangeName, const string& routingKey,
+ const FieldTable& arguments)
+{
+ getBroker().bind(queueName, exchangeName, routingKey, arguments,
+ getConnection().getUserId(), getConnection().getUrl());
+}
+
+void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
+ const string& exchangeName,
+ const string& routingKey)
+{
+ getBroker().unbind(queueName, exchangeName, routingKey,
+ getConnection().getUserId(), getConnection().getUrl());
+}
+
+ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName,
+ const std::string& queueName,
+ const std::string& key,
+ const framing::FieldTable& args)
+{
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
+ params.insert(make_pair(acl::PROP_ROUTINGKEY, key));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchangeName,&params) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange bound request from " << getConnection().getUserId()));
+ }
+
+ Exchange::shared_ptr exchange;
+ try {
+ exchange = getBroker().getExchanges().get(exchangeName);
+ } catch (const NotFoundException&) {}
+
+ Queue::shared_ptr queue;
+ if (!queueName.empty()) {
+ queue = getBroker().getQueues().find(queueName);
+ }
+
+ if (!exchange) {
+ return ExchangeBoundResult(true, (!queueName.empty() && !queue), false, false, false);
+ } else if (!queueName.empty() && !queue) {
+ return ExchangeBoundResult(false, true, false, false, false);
+ } else if (exchange->isBound(queue, key.empty() ? 0 : &key, args.count() > 0 ? &args : &args)) {
+ return ExchangeBoundResult(false, false, false, false, false);
+ } else {
+ //need to test each specified option individually
+ bool queueMatched = queueName.empty() || exchange->isBound(queue, 0, 0);
+ bool keyMatched = key.empty() || exchange->isBound(Queue::shared_ptr(), &key, 0);
+ bool argsMatched = args.count() == 0 || exchange->isBound(Queue::shared_ptr(), 0, &args);
+
+ return ExchangeBoundResult(false, false, !queueMatched, !keyMatched, !argsMatched);
+ }
+}
+
+SessionAdapter::QueueHandlerImpl::QueueHandlerImpl(SemanticState& session) : HandlerHelper(session), broker(getBroker())
+{}
+
+
+SessionAdapter::QueueHandlerImpl::~QueueHandlerImpl()
+{
+ try {
+ destroyExclusiveQueues();
+ } catch (std::exception& e) {
+ QPID_LOG(error, e.what());
+ }
+}
+
+void SessionAdapter::QueueHandlerImpl::destroyExclusiveQueues()
+{
+ while (!exclusiveQueues.empty()) {
+ Queue::shared_ptr q(exclusiveQueues.front());
+ q->releaseExclusiveOwnership();
+ if (q->canAutoDelete()) {
+ Queue::tryAutoDelete(broker, q);
+ }
+ exclusiveQueues.erase(exclusiveQueues.begin());
+ }
+}
+
+bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const
+{
+ return session.isLocal(t);
+}
+
+
+QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name)
+{
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,NULL) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied queue query request from " << getConnection().getUserId()));
+ }
+
+ Queue::shared_ptr queue = session.getBroker().getQueues().find(name);
+ if (queue) {
+
+ Exchange::shared_ptr alternateExchange = queue->getAlternateExchange();
+
+ return QueueQueryResult(queue->getName(),
+ alternateExchange ? alternateExchange->getName() : "",
+ queue->isDurable(),
+ queue->hasExclusiveOwner(),
+ queue->isAutoDelete(),
+ queue->getSettings(),
+ queue->getMessageCount(),
+ queue->getConsumerCount());
+ } else {
+ return QueueQueryResult();
+ }
+}
+
+void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange,
+ bool passive, bool durable, bool exclusive,
+ bool autoDelete, const qpid::framing::FieldTable& arguments)
+{
+ Queue::shared_ptr queue;
+ if (passive && !name.empty()) {
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ //TODO: why does a passive declare require create
+ //permission? The purpose of the passive flag is to state
+ //that the queue should *not* created. For
+ //authorisation a passive declare is similar to
+ //queue-query (or indeed a qmf query).
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
+ params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
+ params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
+ params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId()));
+ }
+ queue = getQueue(name);
+ //TODO: check alternate-exchange is as expected
+ } else {
+ std::pair<Queue::shared_ptr, bool> queue_created =
+ getBroker().createQueue(name, durable,
+ autoDelete,
+ exclusive ? &session : 0,
+ alternateExchange,
+ arguments,
+ getConnection().getUserId(),
+ getConnection().getUrl());
+ queue = queue_created.first;
+ assert(queue);
+ if (queue_created.second) { // This is a new queue
+ //handle automatic cleanup:
+ if (exclusive) {
+ exclusiveQueues.push_back(queue);
+ }
+ } else {
+ if (exclusive && queue->setExclusiveOwner(&session)) {
+ exclusiveQueues.push_back(queue);
+ }
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(),
+ name, durable, exclusive, autoDelete, ManagementAgent::toMap(arguments),
+ "existing"));
+ }
+
+ }
+
+ if (exclusive && !queue->isExclusiveOwner(&session))
+ throw ResourceLockedException(QPID_MSG("Cannot grant exclusive access to queue "
+ << queue->getName()));
+}
+
+void SessionAdapter::QueueHandlerImpl::purge(const string& queue){
+ AclModule* acl = getBroker().getAcl();
+ if (acl)
+ {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_PURGE,acl::OBJ_QUEUE,queue,NULL) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied queue purge request from " << getConnection().getUserId()));
+ }
+ getQueue(queue)->purge();
+}
+
+void SessionAdapter::QueueHandlerImpl::checkDelete(Queue::shared_ptr queue, bool ifUnused, bool ifEmpty)
+{
+ if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session)) {
+ throw ResourceLockedException(QPID_MSG("Cannot delete queue "
+ << queue->getName() << "; it is exclusive to another session"));
+ } else if(ifEmpty && queue->getMessageCount() > 0) {
+ throw PreconditionFailedException(QPID_MSG("Cannot delete queue "
+ << queue->getName() << "; queue not empty"));
+ } else if(ifUnused && queue->getConsumerCount() > 0) {
+ throw PreconditionFailedException(QPID_MSG("Cannot delete queue "
+ << queue->getName() << "; queue in use"));
+ } else if (queue->isExclusiveOwner(&session)) {
+ //remove the queue from the list of exclusive queues if necessary
+ QueueVector::iterator i = std::find(exclusiveQueues.begin(),
+ exclusiveQueues.end(),
+ queue);
+ if (i < exclusiveQueues.end()) exclusiveQueues.erase(i);
+ }
+}
+
+void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty)
+{
+ getBroker().deleteQueue(queue, getConnection().getUserId(), getConnection().getUrl(),
+ boost::bind(&SessionAdapter::QueueHandlerImpl::checkDelete, this, _1, ifUnused, ifEmpty));
+}
+
+SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) :
+ HandlerHelper(s),
+ releaseRedeliveredOp(boost::bind(&SemanticState::release, &state, _1, _2, true)),
+ releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)),
+ rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2))
+ {}
+
+//
+// Message class method handlers
+//
+
+void SessionAdapter::MessageHandlerImpl::transfer(const string& /*destination*/,
+ uint8_t /*acceptMode*/,
+ uint8_t /*acquireMode*/)
+{
+ //not yet used (content containing assemblies treated differently at present
+ std::cout << "SessionAdapter::MessageHandlerImpl::transfer() called" << std::endl;
+}
+
+void SessionAdapter::MessageHandlerImpl::release(const SequenceSet& transfers, bool setRedelivered)
+{
+ transfers.for_each(setRedelivered ? releaseRedeliveredOp : releaseOp);
+}
+
+void
+SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
+ const string& destination,
+ uint8_t acceptMode,
+ uint8_t acquireMode,
+ bool exclusive,
+ const string& resumeId,
+ uint64_t resumeTtl,
+ const FieldTable& arguments)
+{
+
+ AclModule* acl = getBroker().getAcl();
+ if (acl)
+ {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CONSUME,acl::OBJ_QUEUE,queueName,NULL) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied Queue subscribe request from " << getConnection().getUserId()));
+ }
+
+ Queue::shared_ptr queue = getQueue(queueName);
+ if(!destination.empty() && state.exists(destination))
+ throw NotAllowedException(QPID_MSG("Consumer tags must be unique"));
+
+ if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0)
+ throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue "
+ << queue->getName()));
+
+ state.consume(destination, queue,
+ acceptMode == 0, acquireMode == 0, exclusive,
+ resumeId, resumeTtl, arguments);
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventSubscribe(getConnection().getUrl(), getConnection().getUserId(),
+ queueName, destination, exclusive, ManagementAgent::toMap(arguments)));
+}
+
+void
+SessionAdapter::MessageHandlerImpl::cancel(const string& destination )
+{
+ if (!state.cancel(destination)) {
+ throw NotFoundException(QPID_MSG("No such subscription: " << destination));
+ }
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventUnsubscribe(getConnection().getUrl(), getConnection().getUserId(), destination));
+}
+
+void
+SessionAdapter::MessageHandlerImpl::reject(const SequenceSet& transfers, uint16_t /*code*/, const string& /*text*/ )
+{
+ transfers.for_each(rejectOp);
+}
+
+void SessionAdapter::MessageHandlerImpl::flow(const std::string& destination, uint8_t unit, uint32_t value)
+{
+ if (unit == 0) {
+ //message
+ state.addMessageCredit(destination, value);
+ } else if (unit == 1) {
+ //bytes
+ state.addByteCredit(destination, value);
+ } else {
+ //unknown
+ throw InvalidArgumentException(QPID_MSG("Invalid value for unit " << unit));
+ }
+
+}
+
+void SessionAdapter::MessageHandlerImpl::setFlowMode(const std::string& destination, uint8_t mode)
+{
+ if (mode == 0) {
+ //credit
+ state.setCreditMode(destination);
+ } else if (mode == 1) {
+ //window
+ state.setWindowMode(destination);
+ } else{
+ throw InvalidArgumentException(QPID_MSG("Invalid value for mode " << mode));
+ }
+}
+
+void SessionAdapter::MessageHandlerImpl::flush(const std::string& destination)
+{
+ state.flush(destination);
+}
+
+void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination)
+{
+ state.stop(destination);
+}
+
+void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands)
+{
+ state.accepted(commands);
+}
+
+framing::MessageAcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers)
+{
+ // FIXME aconway 2008-05-12: create SequenceSet directly, no need for intermediate results vector.
+ SequenceNumberSet results;
+ RangedOperation f = boost::bind(&SemanticState::acquire, &state, _1, _2, boost::ref(results));
+ transfers.for_each(f);
+
+ results = results.condense();
+ SequenceSet acquisitions;
+ RangedOperation g = boost::bind(&SequenceSet::add, &acquisitions, _1, _2);
+ results.processRanges(g);
+
+ return MessageAcquireResult(acquisitions);
+}
+
+framing::MessageResumeResult SessionAdapter::MessageHandlerImpl::resume(const std::string& /*destination*/,
+ const std::string& /*resumeId*/)
+{
+ throw NotImplementedException("resuming transfers not yet supported");
+}
+
+
+
+void SessionAdapter::ExecutionHandlerImpl::sync()
+{
+ session.addPendingExecutionSync();
+ /** @todo KAG - need a generic mechanism to allow a command to returning "not completed" status back to SessionState */
+
+}
+
+void SessionAdapter::ExecutionHandlerImpl::result(const SequenceNumber& /*commandId*/, const string& /*value*/)
+{
+ //TODO: but currently never used client->server
+}
+
+void SessionAdapter::ExecutionHandlerImpl::exception(uint16_t /*errorCode*/,
+ const SequenceNumber& /*commandId*/,
+ uint8_t /*classCode*/,
+ uint8_t /*commandCode*/,
+ uint8_t /*fieldIndex*/,
+ const std::string& /*description*/,
+ const framing::FieldTable& /*errorInfo*/)
+{
+ //TODO: again, not really used client->server but may be important
+ //for inter-broker links
+}
+
+
+
+void SessionAdapter::TxHandlerImpl::select()
+{
+ state.startTx();
+}
+
+void SessionAdapter::TxHandlerImpl::commit()
+{
+ state.commit(&getBroker().getStore());
+}
+
+void SessionAdapter::TxHandlerImpl::rollback()
+{
+ state.rollback();
+}
+
+std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid& xid)
+{
+ std::string encoded;
+ encode(xid, encoded);
+ return encoded;
+}
+
+void SessionAdapter::DtxHandlerImpl::select()
+{
+ state.selectDtx();
+}
+
+XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid,
+ bool fail,
+ bool suspend)
+{
+ try {
+ if (fail) {
+ state.endDtx(convert(xid), true);
+ if (suspend) {
+ throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set."));
+ } else {
+ return XaResult(XA_STATUS_XA_RBROLLBACK);
+ }
+ } else {
+ if (suspend) {
+ state.suspendDtx(convert(xid));
+ } else {
+ state.endDtx(convert(xid), false);
+ }
+ return XaResult(XA_STATUS_XA_OK);
+ }
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
+ }
+}
+
+XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid,
+ bool join,
+ bool resume)
+{
+ if (join && resume) {
+ throw CommandInvalidException(QPID_MSG("Join and resume cannot both be set."));
+ }
+ try {
+ if (resume) {
+ state.resumeDtx(convert(xid));
+ } else {
+ state.startDtx(convert(xid), getBroker().getDtxManager(), join);
+ }
+ return XaResult(XA_STATUS_XA_OK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
+ }
+}
+
+XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid)
+{
+ try {
+ bool ok = getBroker().getDtxManager().prepare(convert(xid));
+ return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
+ }
+}
+
+XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid,
+ bool onePhase)
+{
+ try {
+ bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase);
+ return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
+ }
+}
+
+
+XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid)
+{
+ try {
+ getBroker().getDtxManager().rollback(convert(xid));
+ return XaResult(XA_STATUS_XA_OK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
+ }
+}
+
+DtxRecoverResult SessionAdapter::DtxHandlerImpl::recover()
+{
+ std::set<std::string> xids;
+ getBroker().getStore().collectPreparedXids(xids);
+ /*
+ * create array of long structs
+ */
+ Array indoubt(0xAB);
+ for (std::set<std::string>::iterator i = xids.begin(); i != xids.end(); i++) {
+ boost::shared_ptr<FieldValue> xid(new Struct32Value(*i));
+ indoubt.add(xid);
+ }
+ return DtxRecoverResult(indoubt);
+}
+
+void SessionAdapter::DtxHandlerImpl::forget(const Xid& xid)
+{
+ //Currently no heuristic completion is supported, so this should never be used.
+ throw NotImplementedException(QPID_MSG("Forget not implemented. Branch with xid " << xid << " not heuristically completed!"));
+}
+
+DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid)
+{
+ uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid));
+ return DtxGetTimeoutResult(timeout);
+}
+
+
+void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid,
+ uint32_t timeout)
+{
+ getBroker().getDtxManager().setTimeout(convert(xid), timeout);
+}
+
+
+Queue::shared_ptr SessionAdapter::HandlerHelper::getQueue(const string& name) const {
+ Queue::shared_ptr queue;
+ if (name.empty()) {
+ throw framing::IllegalArgumentException(QPID_MSG("No queue name specified."));
+ } else {
+ queue = session.getBroker().getQueues().find(name);
+ if (!queue)
+ throw framing::NotFoundException(QPID_MSG("Queue not found: "<<name));
+ }
+ return queue;
+}
+
+}} // namespace qpid::broker
+
+
diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.h b/qpid/cpp/src/qpid/broker/SessionAdapter.h
new file mode 100644
index 0000000000..8987c4812f
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionAdapter.h
@@ -0,0 +1,273 @@
+#ifndef _broker_SessionAdapter_h
+#define _broker_SessionAdapter_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/HandlerImpl.h"
+
+#include "qpid/broker/ConnectionToken.h"
+#include "qpid/broker/OwnershipToken.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/AMQP_ServerOperations.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/StructHelper.h"
+
+#include <algorithm>
+#include <vector>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+class Channel;
+class Connection;
+class Broker;
+class Queue;
+
+/**
+ * SessionAdapter translates protocol-specific AMQP commands for one
+ * specific version of AMQP into calls on the core broker objects. It
+ * is a container for a collection of adapters.
+ *
+ * Each adapter class provides a client proxy to send methods to the
+ * peer broker or client.
+ *
+ */
+
+ class SessionAdapter : public HandlerImpl, public framing::AMQP_ServerOperations
+{
+ public:
+ SessionAdapter(SemanticState& session);
+
+ framing::ProtocolVersion getVersion() const { return session.getConnection().getVersion();}
+
+ MessageHandler* getMessageHandler(){ return &messageImpl; }
+ ExchangeHandler* getExchangeHandler(){ return &exchangeImpl; }
+ QueueHandler* getQueueHandler(){ return &queueImpl; }
+ ExecutionHandler* getExecutionHandler(){ return &executionImpl; }
+ TxHandler* getTxHandler(){ return &txImpl; }
+ DtxHandler* getDtxHandler(){ return &dtxImpl; }
+
+ ConnectionHandler* getConnectionHandler() { throw framing::NotImplementedException("Class not implemented"); }
+ SessionHandler* getSessionHandler() { throw framing::NotImplementedException("Class not implemented"); }
+ FileHandler* getFileHandler() { throw framing::NotImplementedException("Class not implemented"); }
+ StreamHandler* getStreamHandler() { throw framing::NotImplementedException("Class not implemented"); }
+
+ template <class F> void eachExclusiveQueue(F f)
+ {
+ queueImpl.eachExclusiveQueue(f);
+ }
+
+
+ private:
+ //common base for utility methods etc that are specific to this adapter
+ struct HandlerHelper : public HandlerImpl
+ {
+ HandlerHelper(SemanticState& s) : HandlerImpl(s) {}
+
+ boost::shared_ptr<Queue> getQueue(const std::string& name) const;
+ };
+
+
+ class ExchangeHandlerImpl :
+ public ExchangeHandler,
+ public HandlerHelper
+ {
+ public:
+ ExchangeHandlerImpl(SemanticState& session) : HandlerHelper(session) {}
+
+ void declare(const std::string& exchange, const std::string& type,
+ const std::string& alternateExchange,
+ bool passive, bool durable, bool autoDelete,
+ const qpid::framing::FieldTable& arguments);
+ void delete_(const std::string& exchange, bool ifUnused);
+ framing::ExchangeQueryResult query(const std::string& name);
+ void bind(const std::string& queue,
+ const std::string& exchange, const std::string& routingKey,
+ const qpid::framing::FieldTable& arguments);
+ void unbind(const std::string& queue,
+ const std::string& exchange,
+ const std::string& routingKey);
+ framing::ExchangeBoundResult bound(const std::string& exchange,
+ const std::string& queue,
+ const std::string& routingKey,
+ const framing::FieldTable& arguments);
+ private:
+ void checkType(boost::shared_ptr<Exchange> exchange, const std::string& type);
+
+ void checkAlternate(boost::shared_ptr<Exchange> exchange,
+ boost::shared_ptr<Exchange> alternate);
+ };
+
+ class QueueHandlerImpl : public QueueHandler,
+ public HandlerHelper
+ {
+ Broker& broker;
+ std::vector< boost::shared_ptr<Queue> > exclusiveQueues;
+
+ public:
+ QueueHandlerImpl(SemanticState& session);
+ ~QueueHandlerImpl();
+
+ void declare(const std::string& queue,
+ const std::string& alternateExchange,
+ bool passive, bool durable, bool exclusive,
+ bool autoDelete,
+ const qpid::framing::FieldTable& arguments);
+ void delete_(const std::string& queue,
+ bool ifUnused, bool ifEmpty);
+ void purge(const std::string& queue);
+ framing::QueueQueryResult query(const std::string& queue);
+ bool isLocal(const ConnectionToken* t) const;
+
+ void destroyExclusiveQueues();
+ void checkDelete(boost::shared_ptr<Queue> queue, bool ifUnused, bool ifEmpty);
+ template <class F> void eachExclusiveQueue(F f)
+ {
+ std::for_each(exclusiveQueues.begin(), exclusiveQueues.end(), f);
+ }
+ };
+
+ class MessageHandlerImpl :
+ public MessageHandler,
+ public HandlerHelper
+ {
+ typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation;
+ RangedOperation releaseRedeliveredOp;
+ RangedOperation releaseOp;
+ RangedOperation rejectOp;
+ RangedOperation acceptOp;
+
+ public:
+ MessageHandlerImpl(SemanticState& session);
+ void transfer(const std::string& destination,
+ uint8_t acceptMode,
+ uint8_t acquireMode);
+
+ void accept(const framing::SequenceSet& commands);
+
+ void reject(const framing::SequenceSet& commands,
+ uint16_t code,
+ const std::string& text);
+
+ void release(const framing::SequenceSet& commands,
+ bool setRedelivered);
+
+ framing::MessageAcquireResult acquire(const framing::SequenceSet&);
+
+ void subscribe(const std::string& queue,
+ const std::string& destination,
+ uint8_t acceptMode,
+ uint8_t acquireMode,
+ bool exclusive,
+ const std::string& resumeId,
+ uint64_t resumeTtl,
+ const framing::FieldTable& arguments);
+
+ void cancel(const std::string& destination);
+
+ void setFlowMode(const std::string& destination,
+ uint8_t flowMode);
+
+ void flow(const std::string& destination,
+ uint8_t unit,
+ uint32_t value);
+
+ void flush(const std::string& destination);
+
+ void stop(const std::string& destination);
+
+ framing::MessageResumeResult resume(const std::string& destination,
+ const std::string& resumeId);
+
+ };
+
+ class ExecutionHandlerImpl : public ExecutionHandler, public HandlerHelper
+ {
+ public:
+ ExecutionHandlerImpl(SemanticState& session) : HandlerHelper(session) {}
+
+ void sync();
+ void result(const framing::SequenceNumber& commandId, const std::string& value);
+ void exception(uint16_t errorCode,
+ const framing::SequenceNumber& commandId,
+ uint8_t classCode,
+ uint8_t commandCode,
+ uint8_t fieldIndex,
+ const std::string& description,
+ const framing::FieldTable& errorInfo);
+
+ };
+
+ class TxHandlerImpl : public TxHandler, public HandlerHelper
+ {
+ public:
+ TxHandlerImpl(SemanticState& session) : HandlerHelper(session) {}
+
+ void select();
+ void commit();
+ void rollback();
+ };
+
+ class DtxHandlerImpl : public DtxHandler, public HandlerHelper, private framing::StructHelper
+ {
+ std::string convert(const framing::Xid& xid);
+
+ public:
+ DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {}
+
+ void select();
+
+ framing::XaResult start(const framing::Xid& xid,
+ bool join,
+ bool resume);
+
+ framing::XaResult end(const framing::Xid& xid,
+ bool fail,
+ bool suspend);
+
+ framing::XaResult commit(const framing::Xid& xid,
+ bool onePhase);
+
+ void forget(const framing::Xid& xid);
+
+ framing::DtxGetTimeoutResult getTimeout(const framing::Xid& xid);
+
+ framing::XaResult prepare(const framing::Xid& xid);
+
+ framing::DtxRecoverResult recover();
+
+ framing::XaResult rollback(const framing::Xid& xid);
+
+ void setTimeout(const framing::Xid& xid, uint32_t timeout);
+ };
+
+ ExchangeHandlerImpl exchangeImpl;
+ QueueHandlerImpl queueImpl;
+ MessageHandlerImpl messageImpl;
+ ExecutionHandlerImpl executionImpl;
+ TxHandlerImpl txImpl;
+ DtxHandlerImpl dtxImpl;
+};
+}} // namespace qpid::broker
+
+
+
+#endif /*!_broker_SessionAdapter_h*/
diff --git a/qpid/cpp/src/qpid/broker/SessionContext.h b/qpid/cpp/src/qpid/broker/SessionContext.h
new file mode 100644
index 0000000000..253ce8dcf2
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionContext.h
@@ -0,0 +1,56 @@
+#ifndef QPID_BROKER_SESSIONCONTEXT_H
+#define QPID_BROKER_SESSIONCONTEXT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/OwnershipToken.h"
+#include "qpid/SessionId.h"
+
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+namespace broker {
+
+class SessionContext : public OwnershipToken, public sys::OutputControl
+{
+ public:
+ virtual ~SessionContext(){}
+ virtual bool isLocal(const ConnectionToken* t) const = 0;
+ virtual bool isAttached() const = 0;
+ virtual ConnectionState& getConnection() = 0;
+ virtual framing::AMQP_ClientProxy& getProxy() = 0;
+ virtual Broker& getBroker() = 0;
+ virtual uint16_t getChannel() const = 0;
+ virtual const SessionId& getSessionId() const = 0;
+ virtual void addPendingExecutionSync() = 0;
+};
+
+}} // namespace qpid::broker
+
+
+
+#endif /*!QPID_BROKER_SESSIONCONTEXT_H*/
diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.cpp b/qpid/cpp/src/qpid/broker/SessionHandler.cpp
new file mode 100644
index 0000000000..752fa55535
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionHandler.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/SessionHandler.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace broker {
+using namespace framing;
+using namespace std;
+using namespace qpid::sys;
+
+SessionHandler::SessionHandler(Connection& c, ChannelId ch)
+ : amqp_0_10::SessionHandler(&c.getOutput(), ch),
+ connection(c),
+ proxy(out),
+ clusterOrderProxy(c.getClusterOrderOutput() ? new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0)
+{}
+
+SessionHandler::~SessionHandler() {}
+
+void SessionHandler::connectionException(framing::connection::CloseCode code, const std::string& msg) {
+ // NOTE: must tell the error listener _before_ calling connection.close()
+ if (connection.getErrorListener()) connection.getErrorListener()->connectionError(msg);
+ connection.close(code, msg);
+}
+
+void SessionHandler::channelException(framing::session::DetachCode, const std::string& msg) {
+ if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg);
+}
+
+void SessionHandler::executionException(framing::execution::ErrorCode, const std::string& msg) {
+ if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg);
+}
+
+ConnectionState& SessionHandler::getConnection() { return connection; }
+
+const ConnectionState& SessionHandler::getConnection() const { return connection; }
+
+void SessionHandler::handleDetach() {
+ amqp_0_10::SessionHandler::handleDetach();
+ assert(&connection.getChannel(channel.get()) == this);
+ if (session.get())
+ connection.getBroker().getSessionManager().detach(session);
+ assert(!session.get());
+ connection.closeChannel(channel.get());
+}
+
+void SessionHandler::setState(const std::string& name, bool force) {
+ assert(!session.get());
+ SessionId id(connection.getUserId(), name);
+ session = connection.broker.getSessionManager().attach(*this, id, force);
+}
+
+void SessionHandler::detaching()
+{
+ assert(session.get());
+ session->disableOutput();
+}
+
+FrameHandler* SessionHandler::getInHandler() { return session.get() ? &session->in : 0; }
+qpid::SessionState* SessionHandler::getState() { return session.get(); }
+
+void SessionHandler::readyToSend() {
+ if (session.get()) session->readyToSend();
+}
+
+/**
+ * Used by inter-broker bridges to set up session id and attach
+ */
+void SessionHandler::attachAs(const std::string& name)
+{
+ SessionId id(connection.getUserId(), name);
+ SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig();
+ // Delay creating management object till attached(). In a cluster,
+ // only the active link broker calls attachAs but all brokers
+ // receive the subsequent attached() call.
+ session.reset(new SessionState(connection.getBroker(), *this, id, config, true));
+ sendAttach(false);
+}
+
+/**
+ * TODO: this is a little ugly, fix it; its currently still relied on
+ * for 'push' bridges
+ */
+void SessionHandler::attached(const std::string& name)
+{
+ if (session.get()) {
+ session->addManagementObject(); // Delayed from attachAs()
+ amqp_0_10::SessionHandler::attached(name);
+ } else {
+ SessionId id(connection.getUserId(), name);
+ SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig();
+ session.reset(new SessionState(connection.getBroker(), *this, id, config));
+ markReadyToSend();
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.h b/qpid/cpp/src/qpid/broker/SessionHandler.h
new file mode 100644
index 0000000000..ca6d6bb193
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionHandler.h
@@ -0,0 +1,99 @@
+#ifndef QPID_BROKER_SESSIONHANDLER_H
+#define QPID_BROKER_SESSIONHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/SessionHandler.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+
+namespace qpid {
+class SessionState;
+
+namespace broker {
+
+class Connection;
+class ConnectionState;
+class SessionState;
+
+/**
+ * A SessionHandler is associated with each active channel. It
+ * receives incoming frames, handles session controls and manages the
+ * association between the channel and a session.
+ */
+class SessionHandler : public amqp_0_10::SessionHandler {
+ public:
+ SessionHandler(Connection&, framing::ChannelId);
+ ~SessionHandler();
+
+ /** Get broker::SessionState */
+ SessionState* getSession() { return session.get(); }
+ const SessionState* getSession() const { return session.get(); }
+
+ ConnectionState& getConnection();
+ const ConnectionState& getConnection() const;
+
+ framing::AMQP_ClientProxy& getProxy() { return proxy; }
+ const framing::AMQP_ClientProxy& getProxy() const { return proxy; }
+
+ /**
+ * If commands are sent based on the local time (e.g. in timers), they don't have
+ * a well-defined ordering across cluster nodes.
+ * This proxy is for sending such commands. In a clustered broker it will take steps
+ * to synchronize command order across the cluster. In a stand-alone broker
+ * it is just a synonym for getProxy()
+ */
+ framing::AMQP_ClientProxy& getClusterOrderProxy() {
+ return clusterOrderProxy.get() ? *clusterOrderProxy : proxy;
+ }
+
+ virtual void handleDetach();
+ void attached(const std::string& name);//used by 'pushing' inter-broker bridges
+ void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges
+
+ protected:
+ virtual void setState(const std::string& sessionName, bool force);
+ virtual qpid::SessionState* getState();
+ virtual framing::FrameHandler* getInHandler();
+ virtual void connectionException(framing::connection::CloseCode code, const std::string& msg);
+ virtual void channelException(framing::session::DetachCode, const std::string& msg);
+ virtual void executionException(framing::execution::ErrorCode, const std::string& msg);
+ virtual void detaching();
+ virtual void readyToSend();
+
+ private:
+ struct SetChannelProxy : public framing::AMQP_ClientProxy { // Proxy that sets the channel.
+ framing::ChannelHandler setChannel;
+ SetChannelProxy(uint16_t ch, framing::FrameHandler* out)
+ : framing::AMQP_ClientProxy(setChannel), setChannel(ch, out) {}
+ };
+
+ Connection& connection;
+ framing::AMQP_ClientProxy proxy;
+ std::auto_ptr<SessionState> session;
+ std::auto_ptr<SetChannelProxy> clusterOrderProxy;
+};
+
+}} // namespace qpid::broker
+
+
+
+#endif /*!QPID_BROKER_SESSIONHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/broker/SessionManager.cpp b/qpid/cpp/src/qpid/broker/SessionManager.cpp
new file mode 100644
index 0000000000..8cc58571af
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionManager.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/SessionManager.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/log/Helpers.h"
+#include "qpid/memory.h"
+
+#include <boost/bind.hpp>
+#include <boost/range.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <ostream>
+
+namespace qpid {
+namespace broker {
+
+using boost::intrusive_ptr;
+using namespace sys;
+using namespace framing;
+
+SessionManager::SessionManager(const SessionState::Configuration& c, Broker& b)
+ : config(c), broker(b) {}
+
+SessionManager::~SessionManager() {
+ detached.clear(); // Must clear before destructor as session dtor will call forget()
+}
+
+std::auto_ptr<SessionState> SessionManager::attach(SessionHandler& h, const SessionId& id, bool/*force*/) {
+ Mutex::ScopedLock l(lock);
+ eraseExpired(); // Clean up expired table
+ std::pair<Attached::iterator, bool> insert = attached.insert(id);
+ if (!insert.second)
+ throw SessionBusyException(QPID_MSG("Session already attached: " << id));
+ Detached::iterator i = std::find(detached.begin(), detached.end(), id);
+ std::auto_ptr<SessionState> state;
+ if (i == detached.end())
+ state.reset(new SessionState(broker, h, id, config));
+ else {
+ state.reset(detached.release(i).release());
+ state->attach(h);
+ }
+ return state;
+ // FIXME aconway 2008-04-29: implement force
+}
+
+void SessionManager::detach(std::auto_ptr<SessionState> session) {
+ Mutex::ScopedLock l(lock);
+ attached.erase(session->getId());
+ session->detach();
+ if (session->getTimeout() > 0) {
+ session->expiry = AbsTime(now(),session->getTimeout()*TIME_SEC);
+ if (session->mgmtObject != 0)
+ session->mgmtObject->set_expireTime ((uint64_t) Duration (EPOCH, session->expiry));
+ detached.push_back(session.release()); // In expiry order
+ eraseExpired();
+}
+}
+
+void SessionManager::forget(const SessionId& id) {
+ Mutex::ScopedLock l(lock);
+ attached.erase(id);
+}
+
+void SessionManager::eraseExpired() {
+ // Called with lock held.
+ if (!detached.empty()) {
+ // This used to use a more elegant invocation of std::lower_bound
+ // but violated the strict weak ordering rule which Visual Studio
+ // enforced. See QPID-1424 for more info should you be tempted to
+ // replace the loop with something more elegant.
+ AbsTime now = AbsTime::now();
+ Detached::iterator keep = detached.begin();
+ while ((keep != detached.end()) && ((*keep).expiry < now))
+ keep++;
+ if (detached.begin() != keep) {
+ QPID_LOG(debug, "Expiring sessions: " << log::formatList(detached.begin(), keep));
+ detached.erase(detached.begin(), keep);
+ }
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SessionManager.h b/qpid/cpp/src/qpid/broker/SessionManager.h
new file mode 100644
index 0000000000..db88e7ec10
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionManager.h
@@ -0,0 +1,87 @@
+#ifndef QPID_BROKER_SESSIONMANAGER_H
+#define QPID_BROKER_SESSIONMANAGER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/SessionState.h>
+#include <qpid/sys/Time.h>
+#include <qpid/sys/Mutex.h>
+#include <qpid/RefCounted.h>
+
+#include <set>
+#include <vector>
+#include <memory>
+
+#include <boost/noncopyable.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+class Broker;
+class SessionState;
+class SessionHandler;
+
+/**
+ * Create and manage SessionState objects.
+ */
+class SessionManager : private boost::noncopyable {
+ public:
+ SessionManager(const qpid::SessionState::Configuration&, Broker&);
+
+ ~SessionManager();
+
+ /** Open a new active session, caller takes ownership */
+ std::auto_ptr<SessionState> attach(SessionHandler& h, const SessionId& id, bool/*force*/);
+
+ /** Return a detached session to the manager, start the timeout counter. */
+ void detach(std::auto_ptr<SessionState>);
+
+ /** Forget about an attached session. Called by SessionState destructor. */
+ void forget(const SessionId&);
+
+ Broker& getBroker() const { return broker; }
+
+ const qpid::SessionState::Configuration& getSessionConfig() const { return config; }
+
+ private:
+ typedef boost::ptr_vector<SessionState> Detached; // Sorted in expiry order.
+ typedef std::set<SessionId> Attached;
+
+ void eraseExpired();
+
+ sys::Mutex lock;
+ Detached detached;
+ Attached attached;
+ qpid::SessionState::Configuration config;
+ Broker& broker;
+};
+
+
+
+}} // namespace qpid::broker
+
+
+
+
+
+#endif /*!QPID_BROKER_SESSIONMANAGER_H*/
diff --git a/qpid/cpp/src/qpid/broker/SessionOutputException.h b/qpid/cpp/src/qpid/broker/SessionOutputException.h
new file mode 100644
index 0000000000..7c1c5de926
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionOutputException.h
@@ -0,0 +1,47 @@
+#ifndef QPID_BROKER_SESSIONOUTPUTEXCEPTION_H
+#define QPID_BROKER_SESSIONOUTPUTEXCEPTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * This exception is used to signal 'session' exceptions (aka
+ * execution exceptions in AMQP 0-10 terms) that occur during output
+ * processing. It simply allows the channel of the session to be
+ * specified in addition to the other details. Special treatment is
+ * required at present because the output processing chain is
+ * different from that which handles incoming commands (specifically
+ * AggregateOutput cannot reasonably handle exceptions as it has no
+ * context).
+ */
+struct SessionOutputException : qpid::SessionException
+{
+ const uint16_t channel;
+ SessionOutputException(const qpid::SessionException& e, uint16_t c) : qpid::SessionException(e.code, e.getMessage()), channel(c) {}
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_SESSIONOUTPUTEXCEPTION_H*/
diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp
new file mode 100644
index 0000000000..119e5732c4
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionState.cpp
@@ -0,0 +1,593 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/SessionManager.h"
+#include "qpid/broker/SessionHandler.h"
+#include "qpid/broker/RateFlowcontrol.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/ServerInvoker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace broker {
+
+using namespace framing;
+using sys::Mutex;
+using boost::intrusive_ptr;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+using qpid::sys::AbsTime;
+//using qpid::sys::Timer;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+SessionState::SessionState(
+ Broker& b, SessionHandler& h, const SessionId& id,
+ const SessionState::Configuration& config, bool delayManagement)
+ : qpid::SessionState(id, config),
+ broker(b), handler(&h),
+ semanticState(*this, *this),
+ adapter(semanticState),
+ msgBuilder(&broker.getStore()),
+ mgmtObject(0),
+ rateFlowcontrol(0),
+ asyncCommandCompleter(new AsyncCommandCompleter(this))
+{
+ uint32_t maxRate = broker.getOptions().maxSessionRate;
+ if (maxRate) {
+ if (handler->getConnection().getClientThrottling()) {
+ rateFlowcontrol.reset(new RateFlowcontrol(maxRate));
+ } else {
+ QPID_LOG(warning, getId() << ": Unable to flow control client - client doesn't support");
+ }
+ }
+ if (!delayManagement) addManagementObject();
+ attach(h);
+}
+
+void SessionState::addManagementObject() {
+ if (GetManagementObject()) return; // Already added.
+ Manageable* parent = broker.GetVhostObject ();
+ if (parent != 0) {
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent != 0) {
+ mgmtObject = new _qmf::Session
+ (agent, this, parent, getId().getName());
+ mgmtObject->set_attached (0);
+ mgmtObject->set_detachedLifespan (0);
+ mgmtObject->clr_expireTime();
+ if (rateFlowcontrol)
+ mgmtObject->set_maxClientRate(rateFlowcontrol->getRate());
+ agent->addObject(mgmtObject);
+ }
+ }
+}
+
+SessionState::~SessionState() {
+ asyncCommandCompleter->cancel();
+ semanticState.closed();
+ if (mgmtObject != 0)
+ mgmtObject->resourceDestroy ();
+
+ if (flowControlTimer)
+ flowControlTimer->cancel();
+}
+
+AMQP_ClientProxy& SessionState::getProxy() {
+ assert(isAttached());
+ return handler->getProxy();
+}
+
+uint16_t SessionState::getChannel() const {
+ assert(isAttached());
+ return handler->getChannel();
+}
+
+ConnectionState& SessionState::getConnection() {
+ assert(isAttached());
+ return handler->getConnection();
+}
+
+bool SessionState::isLocal(const ConnectionToken* t) const
+{
+ return isAttached() && &(handler->getConnection()) == t;
+}
+
+void SessionState::detach() {
+ QPID_LOG(debug, getId() << ": detached on broker.");
+ asyncCommandCompleter->detached();
+ disableOutput();
+ handler = 0;
+ if (mgmtObject != 0)
+ mgmtObject->set_attached (0);
+}
+
+void SessionState::disableOutput()
+{
+ semanticState.detached(); //prevents further activateOutput calls until reattached
+}
+
+void SessionState::attach(SessionHandler& h) {
+ QPID_LOG(debug, getId() << ": attached on broker.");
+ handler = &h;
+ if (mgmtObject != 0)
+ {
+ mgmtObject->set_attached (1);
+ mgmtObject->set_connectionRef (h.getConnection().GetManagementObject()->getObjectId());
+ mgmtObject->set_channelId (h.getChannel());
+ }
+ asyncCommandCompleter->attached();
+}
+
+void SessionState::abort() {
+ if (isAttached())
+ getConnection().outputTasks.abort();
+}
+
+void SessionState::activateOutput() {
+ if (isAttached())
+ getConnection().outputTasks.activateOutput();
+}
+
+void SessionState::giveReadCredit(int32_t credit) {
+ if (isAttached())
+ getConnection().outputTasks.giveReadCredit(credit);
+}
+
+ManagementObject* SessionState::GetManagementObject (void) const
+{
+ return (ManagementObject*) mgmtObject;
+}
+
+Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
+ Args& /*args*/,
+ string& /*text*/)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+
+ switch (methodId)
+ {
+ case _qmf::Session::METHOD_DETACH :
+ if (handler != 0) {
+ handler->sendDetach();
+ }
+ status = Manageable::STATUS_OK;
+ break;
+
+ case _qmf::Session::METHOD_CLOSE :
+ /*
+ if (handler != 0)
+ {
+ handler->getConnection().closeChannel(handler->getChannel());
+ }
+ status = Manageable::STATUS_OK;
+ break;
+ */
+
+ case _qmf::Session::METHOD_SOLICITACK :
+ case _qmf::Session::METHOD_RESETLIFESPAN :
+ status = Manageable::STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return status;
+}
+
+void SessionState::handleCommand(framing::AMQMethodBody* method, const SequenceNumber& id) {
+ currentCommandComplete = true; // assumed, can be overridden by invoker method (this sucks).
+ Invoker::Result invocation = invoke(adapter, *method);
+ if (currentCommandComplete) receiverCompleted(id);
+
+ if (!invocation.wasHandled()) {
+ throw NotImplementedException(QPID_MSG("Not implemented: " << *method));
+ } else if (invocation.hasResult()) {
+ getProxy().getExecution().result(id, invocation.getResult());
+ }
+
+ if (method->isSync() && currentCommandComplete) {
+ sendAcceptAndCompletion();
+ }
+}
+
+struct ScheduledCreditTask : public sys::TimerTask {
+ sys::Timer& timer;
+ SessionState& sessionState;
+ ScheduledCreditTask(const qpid::sys::Duration& d, sys::Timer& t,
+ SessionState& s) :
+ TimerTask(d,"ScheduledCredit"),
+ timer(t),
+ sessionState(s)
+ {}
+
+ void fire() {
+ // This is the best we can currently do to avoid a destruction/fire race
+ sessionState.getConnection().requestIOProcessing(boost::bind(&ScheduledCreditTask::sendCredit, this));
+ }
+
+ void sendCredit() {
+ if ( !sessionState.processSendCredit(0) ) {
+ QPID_LOG(warning, sessionState.getId() << ": Reschedule sending credit");
+ setupNextFire();
+ timer.add(this);
+ }
+ }
+};
+
+void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id)
+{
+ if (frame.getBof() && frame.getBos()) //start of frameset
+ msgBuilder.start(id);
+ intrusive_ptr<Message> msg(msgBuilder.getMessage());
+ msgBuilder.handle(frame);
+ if (frame.getEof() && frame.getEos()) {//end of frameset
+ if (frame.getBof()) {
+ //i.e this is a just a command frame, add a dummy header
+ AMQFrame header((AMQHeaderBody()));
+ header.setBof(false);
+ header.setEof(false);
+ msg->getFrames().append(header);
+ }
+ msg->setPublisher(&getConnection());
+ msg->getIngressCompletion().begin();
+ semanticState.handle(msg);
+ msgBuilder.end();
+ IncompleteIngressMsgXfer xfer(this, msg);
+ msg->getIngressCompletion().end(xfer); // allows msg to complete xfer
+ }
+
+ // Handle producer session flow control
+ if (rateFlowcontrol && frame.getBof() && frame.getBos()) {
+ if ( !processSendCredit(1) ) {
+ QPID_LOG(debug, getId() << ": Schedule sending credit");
+ sys::Timer& timer = getBroker().getTimer();
+ // Use heuristic for scheduled credit of time for 50 messages, but not longer than 500ms
+ sys::Duration d = std::min(sys::TIME_SEC * 50 / rateFlowcontrol->getRate(), 500 * sys::TIME_MSEC);
+ flowControlTimer = new ScheduledCreditTask(d, timer, *this);
+ timer.add(flowControlTimer);
+ }
+ }
+}
+
+bool SessionState::processSendCredit(uint32_t msgs)
+{
+ qpid::sys::ScopedLock<Mutex> l(rateLock);
+ // Check for violating flow control
+ if ( msgs > 0 && rateFlowcontrol->flowStopped() ) {
+ QPID_LOG(warning, getId() << ": producer throttling violation");
+ // TODO: Probably do message.stop("") first time then disconnect
+ // See comment on getClusterOrderProxy() in .h file
+ getClusterOrderProxy().getMessage().stop("");
+ return true;
+ }
+ AbsTime now = AbsTime::now();
+ uint32_t sendCredit = rateFlowcontrol->receivedMessage(now, msgs);
+ if (mgmtObject) mgmtObject->dec_clientCredit(msgs);
+ if ( sendCredit>0 ) {
+ QPID_LOG(debug, getId() << ": send producer credit " << sendCredit);
+ getClusterOrderProxy().getMessage().flow("", 0, sendCredit);
+ rateFlowcontrol->sentCredit(now, sendCredit);
+ if (mgmtObject) mgmtObject->inc_clientCredit(sendCredit);
+ return true;
+ } else {
+ return !rateFlowcontrol->flowStopped() ;
+ }
+}
+
+void SessionState::sendAcceptAndCompletion()
+{
+ if (!accepted.empty()) {
+ getProxy().getMessage().accept(accepted);
+ accepted.clear();
+ }
+ sendCompletion();
+}
+
+/** Invoked when the given inbound message is finished being processed
+ * by all interested parties (eg. it is done being enqueued to all queues,
+ * its credit has been accounted for, etc). At this point, msg is considered
+ * by this receiver as 'completed' (as defined by AMQP 0_10)
+ */
+void SessionState::completeRcvMsg(SequenceNumber id,
+ bool requiresAccept,
+ bool requiresSync)
+{
+ bool callSendCompletion = false;
+ receiverCompleted(id);
+ if (requiresAccept)
+ // will cause msg's seq to appear in the next message.accept we send.
+ accepted.add(id);
+
+ // Are there any outstanding Execution.Sync commands pending the
+ // completion of this msg? If so, complete them.
+ while (!pendingExecutionSyncs.empty() &&
+ receiverGetIncomplete().front() >= pendingExecutionSyncs.front()) {
+ const SequenceNumber id = pendingExecutionSyncs.front();
+ pendingExecutionSyncs.pop();
+ QPID_LOG(debug, getId() << ": delayed execution.sync " << id << " is completed.");
+ receiverCompleted(id);
+ callSendCompletion = true; // likely peer is pending for this completion.
+ }
+
+ // if the sender has requested immediate notification of the completion...
+ if (requiresSync) {
+ sendAcceptAndCompletion();
+ } else if (callSendCompletion) {
+ sendCompletion();
+ }
+}
+
+void SessionState::handleIn(AMQFrame& frame) {
+ SequenceNumber commandId = receiverGetCurrent();
+ //TODO: make command handling more uniform, regardless of whether
+ //commands carry content.
+ AMQMethodBody* m = frame.getMethod();
+ if (m == 0 || m->isContentBearing()) {
+ handleContent(frame, commandId);
+ } else if (frame.getBof() && frame.getEof()) {
+ handleCommand(frame.getMethod(), commandId);
+ } else {
+ throw InternalErrorException("Cannot handle multi-frame command segments yet");
+ }
+}
+
+void SessionState::handleOut(AMQFrame& frame) {
+ assert(handler);
+ handler->out(frame);
+}
+
+void SessionState::deliver(DeliveryRecord& msg, bool sync)
+{
+ uint32_t maxFrameSize = getConnection().getFrameMax();
+ assert(senderGetCommandPoint().offset == 0);
+ SequenceNumber commandId = senderGetCommandPoint().command;
+ msg.deliver(getProxy().getHandler(), commandId, maxFrameSize);
+ assert(senderGetCommandPoint() == SessionPoint(commandId+1, 0)); // Delivery has moved sendPoint.
+ if (sync) {
+ AMQP_ClientProxy::Execution& p(getProxy().getExecution());
+ Proxy::ScopedSync s(p);
+ p.sync();
+ }
+}
+
+void SessionState::sendCompletion() {
+ handler->sendCompletion();
+}
+
+void SessionState::senderCompleted(const SequenceSet& commands) {
+ qpid::SessionState::senderCompleted(commands);
+ semanticState.completed(commands);
+}
+
+void SessionState::readyToSend() {
+ QPID_LOG(debug, getId() << ": ready to send, activating output.");
+ assert(handler);
+ semanticState.attached();
+ if (rateFlowcontrol) {
+ qpid::sys::ScopedLock<Mutex> l(rateLock);
+ // Issue initial credit - use a heuristic here issue min of 300 messages or 1 secs worth
+ uint32_t credit = std::min(rateFlowcontrol->getRate(), 300U);
+ QPID_LOG(debug, getId() << ": Issuing producer message credit " << credit);
+ // See comment on getClusterOrderProxy() in .h file
+ getClusterOrderProxy().getMessage().setFlowMode("", 0);
+ getClusterOrderProxy().getMessage().flow("", 0, credit);
+ rateFlowcontrol->sentCredit(AbsTime::now(), credit);
+ if (mgmtObject) mgmtObject->inc_clientCredit(credit);
+ }
+}
+
+Broker& SessionState::getBroker() { return broker; }
+
+// Session resume is not fully implemented so it is useless to set a
+// non-0 timeout. Moreover it creates problems in a cluster because
+// dead sessions are kept and interfere with failover.
+void SessionState::setTimeout(uint32_t) { }
+
+framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() {
+ return handler->getClusterOrderProxy();
+}
+
+
+// Current received command is an execution.sync command.
+// Complete this command only when all preceding commands have completed.
+// (called via the invoker() in handleCommand() above)
+void SessionState::addPendingExecutionSync()
+{
+ SequenceNumber syncCommandId = receiverGetCurrent();
+ if (receiverGetIncomplete().front() < syncCommandId) {
+ currentCommandComplete = false;
+ pendingExecutionSyncs.push(syncCommandId);
+ asyncCommandCompleter->flushPendingMessages();
+ QPID_LOG(debug, getId() << ": delaying completion of execution.sync " << syncCommandId);
+ }
+}
+
+
+/** factory for creating a reference-counted IncompleteIngressMsgXfer object
+ * which will be attached to a message that will be completed asynchronously.
+ */
+boost::intrusive_ptr<AsyncCompletion::Callback>
+SessionState::IncompleteIngressMsgXfer::clone()
+{
+ boost::intrusive_ptr<SessionState::IncompleteIngressMsgXfer> cb(new SessionState::IncompleteIngressMsgXfer(session, msg));
+
+ // Optimization: this routine is *only* invoked when the message needs to be asynchronously completed.
+ // If the client is pending the message.transfer completion, flush now to force immediate write to journal.
+ if (requiresSync)
+ msg->flush();
+ else {
+ // otherwise, we need to track this message in order to flush it if an execution.sync arrives
+ // before it has been completed (see flushPendingMessages())
+ pending = true;
+ completerContext->addPendingMessage(msg);
+ }
+ return cb;
+}
+
+
+/** Invoked by the asynchronous completer associated with a received
+ * msg that is pending Completion. May be invoked by the IO thread
+ * (sync == true), or some external thread (!sync).
+ */
+void SessionState::IncompleteIngressMsgXfer::completed(bool sync)
+{
+ if (pending) completerContext->deletePendingMessage(id);
+ if (!sync) {
+ /** note well: this path may execute in any thread. It is safe to access
+ * the scheduledCompleterContext, since *this has a shared pointer to it.
+ * but not session!
+ */
+ session = 0;
+ QPID_LOG(debug, ": async completion callback scheduled for msg seq=" << id);
+ completerContext->scheduleMsgCompletion(id, requiresAccept, requiresSync);
+ } else {
+ // this path runs directly from the ac->end() call in handleContent() above,
+ // so *session is definately valid.
+ if (session->isAttached()) {
+ QPID_LOG(debug, ": receive completed for msg seq=" << id);
+ session->completeRcvMsg(id, requiresAccept, requiresSync);
+ }
+ }
+ completerContext = boost::intrusive_ptr<AsyncCommandCompleter>();
+}
+
+
+/** Scheduled from an asynchronous command's completed callback to run on
+ * the IO thread.
+ */
+void SessionState::AsyncCommandCompleter::schedule(boost::intrusive_ptr<AsyncCommandCompleter> ctxt)
+{
+ ctxt->completeCommands();
+}
+
+
+/** Track an ingress message that is pending completion */
+void SessionState::AsyncCommandCompleter::addPendingMessage(boost::intrusive_ptr<Message> msg)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ std::pair<SequenceNumber, boost::intrusive_ptr<Message> > item(msg->getCommandId(), msg);
+ bool unique = pendingMsgs.insert(item).second;
+ if (!unique) {
+ assert(false);
+ }
+}
+
+
+/** pending message has completed */
+void SessionState::AsyncCommandCompleter::deletePendingMessage(SequenceNumber id)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ pendingMsgs.erase(id);
+}
+
+
+/** done when an execution.sync arrives */
+void SessionState::AsyncCommandCompleter::flushPendingMessages()
+{
+ std::map<SequenceNumber, boost::intrusive_ptr<Message> > copy;
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ pendingMsgs.swap(copy); // we've only tracked these in case a flush is needed, so nuke 'em now.
+ }
+ // drop lock, so it is safe to call "flush()"
+ for (std::map<SequenceNumber, boost::intrusive_ptr<Message> >::iterator i = copy.begin();
+ i != copy.end(); ++i) {
+ i->second->flush();
+ }
+}
+
+
+/** mark an ingress Message.Transfer command as completed.
+ * This method must be thread safe - it may run on any thread.
+ */
+void SessionState::AsyncCommandCompleter::scheduleMsgCompletion(SequenceNumber cmd,
+ bool requiresAccept,
+ bool requiresSync)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+
+ if (session && isAttached) {
+ MessageInfo msg(cmd, requiresAccept, requiresSync);
+ completedMsgs.push_back(msg);
+ if (completedMsgs.size() == 1) {
+ session->getConnection().requestIOProcessing(boost::bind(&schedule,
+ session->asyncCommandCompleter));
+ }
+ }
+}
+
+
+/** Cause the session to complete all completed commands.
+ * Executes on the IO thread.
+ */
+void SessionState::AsyncCommandCompleter::completeCommands()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+
+ // when session is destroyed, it clears the session pointer via cancel().
+ if (session && session->isAttached()) {
+ for (std::vector<MessageInfo>::iterator msg = completedMsgs.begin();
+ msg != completedMsgs.end(); ++msg) {
+ session->completeRcvMsg(msg->cmd, msg->requiresAccept, msg->requiresSync);
+ }
+ }
+ completedMsgs.clear();
+}
+
+
+/** cancel any pending calls to scheduleComplete */
+void SessionState::AsyncCommandCompleter::cancel()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ session = 0;
+}
+
+
+/** inform the completer that the session has attached,
+ * allows command completion scheduling from any thread */
+void SessionState::AsyncCommandCompleter::attached()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ isAttached = true;
+}
+
+
+/** inform the completer that the session has detached,
+ * disables command completion scheduling from any thread */
+void SessionState::AsyncCommandCompleter::detached()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ isAttached = false;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SessionState.h b/qpid/cpp/src/qpid/broker/SessionState.h
new file mode 100644
index 0000000000..b43df0c0aa
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SessionState.h
@@ -0,0 +1,284 @@
+#ifndef QPID_BROKER_SESSION_H
+#define QPID_BROKER_SESSION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/SessionState.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/sys/Time.h"
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Session.h"
+#include "qpid/broker/SessionAdapter.h"
+#include "qpid/broker/DeliveryAdapter.h"
+#include "qpid/broker/AsyncCompletion.h"
+#include "qpid/broker/MessageBuilder.h"
+#include "qpid/broker/SessionContext.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/sys/Monitor.h"
+
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+#include <set>
+#include <vector>
+#include <ostream>
+
+namespace qpid {
+
+namespace framing {
+class AMQP_ClientProxy;
+}
+
+namespace sys {
+class TimerTask;
+}
+
+namespace broker {
+
+class Broker;
+class ConnectionState;
+class Message;
+class SessionHandler;
+class SessionManager;
+class RateFlowcontrol;
+
+/**
+ * Broker-side session state includes session's handler chains, which
+ * may themselves have state.
+ */
+class SessionState : public qpid::SessionState,
+ public SessionContext,
+ public DeliveryAdapter,
+ public management::Manageable,
+ public framing::FrameHandler::InOutHandler
+{
+ public:
+ SessionState(Broker&, SessionHandler&, const SessionId&,
+ const SessionState::Configuration&, bool delayManagement=false);
+ ~SessionState();
+ bool isAttached() const { return handler; }
+
+ void detach();
+ void attach(SessionHandler& handler);
+ void disableOutput();
+
+ /** @pre isAttached() */
+ framing::AMQP_ClientProxy& getProxy();
+
+ /** @pre isAttached() */
+ uint16_t getChannel() const;
+
+ /** @pre isAttached() */
+ ConnectionState& getConnection();
+ bool isLocal(const ConnectionToken* t) const;
+
+ Broker& getBroker();
+
+ void setTimeout(uint32_t seconds);
+
+ /** OutputControl **/
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t);
+
+ void senderCompleted(const framing::SequenceSet& ranges);
+
+ void sendCompletion();
+
+ //delivery adapter methods:
+ void deliver(DeliveryRecord&, bool sync);
+
+ // Manageable entry points
+ management::ManagementObject* GetManagementObject (void) const;
+ management::Manageable::status_t
+ ManagementMethod (uint32_t methodId, management::Args& args, std::string&);
+
+ void readyToSend();
+
+ // Used by cluster to create replica sessions.
+ SemanticState& getSemanticState() { return semanticState; }
+ boost::intrusive_ptr<Message> getMessageInProgress() { return msgBuilder.getMessage(); }
+ SessionAdapter& getSessionAdapter() { return adapter; }
+
+ bool processSendCredit(uint32_t msgs);
+
+ const SessionId& getSessionId() const { return getId(); }
+
+ // Used by ExecutionHandler sync command processing. Notifies
+ // the SessionState of a received Execution.Sync command.
+ void addPendingExecutionSync();
+
+ // Used to delay creation of management object for sessions
+ // belonging to inter-broker bridges
+ void addManagementObject();
+
+ private:
+ void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id);
+ void handleContent(framing::AMQFrame& frame, const framing::SequenceNumber& id);
+
+ // indicate that the given ingress msg has been completely received by the
+ // broker, and the msg's message.transfer command can be considered completed.
+ void completeRcvMsg(SequenceNumber id, bool requiresAccept, bool requiresSync);
+
+ void handleIn(framing::AMQFrame& frame);
+ void handleOut(framing::AMQFrame& frame);
+
+ // End of the input & output chains.
+ void handleInLast(framing::AMQFrame& frame);
+ void handleOutLast(framing::AMQFrame& frame);
+
+ void sendAcceptAndCompletion();
+
+ /**
+ * If commands are sent based on the local time (e.g. in timers), they don't have
+ * a well-defined ordering across cluster nodes.
+ * This proxy is for sending such commands. In a clustered broker it will take steps
+ * to synchronize command order across the cluster. In a stand-alone broker
+ * it is just a synonym for getProxy()
+ */
+ framing::AMQP_ClientProxy& getClusterOrderProxy();
+
+ Broker& broker;
+ SessionHandler* handler;
+ sys::AbsTime expiry; // Used by SessionManager.
+ SemanticState semanticState;
+ SessionAdapter adapter;
+ MessageBuilder msgBuilder;
+ qmf::org::apache::qpid::broker::Session* mgmtObject;
+ qpid::framing::SequenceSet accepted;
+
+ // State used for producer flow control (rate limited)
+ qpid::sys::Mutex rateLock;
+ boost::scoped_ptr<RateFlowcontrol> rateFlowcontrol;
+ boost::intrusive_ptr<sys::TimerTask> flowControlTimer;
+
+ // sequence numbers for pending received Execution.Sync commands
+ std::queue<SequenceNumber> pendingExecutionSyncs;
+ bool currentCommandComplete;
+
+ /** This class provides a context for completing asynchronous commands in a thread
+ * safe manner. Asynchronous commands save their completion state in this class.
+ * This class then schedules the completeCommands() method in the IO thread.
+ * While running in the IO thread, completeCommands() may safely complete all
+ * saved commands without the risk of colliding with other operations on this
+ * SessionState.
+ */
+ class AsyncCommandCompleter : public RefCounted {
+ private:
+ SessionState *session;
+ bool isAttached;
+ qpid::sys::Mutex completerLock;
+
+ // special-case message.transfer commands for optimization
+ struct MessageInfo {
+ SequenceNumber cmd; // message.transfer command id
+ bool requiresAccept;
+ bool requiresSync;
+ MessageInfo(SequenceNumber c, bool a, bool s)
+ : cmd(c), requiresAccept(a), requiresSync(s) {}
+ };
+ std::vector<MessageInfo> completedMsgs;
+ // If an ingress message does not require a Sync, we need to
+ // hold a reference to it in case an Execution.Sync command is received and we
+ // have to manually flush the message.
+ std::map<SequenceNumber, boost::intrusive_ptr<Message> > pendingMsgs;
+
+ /** complete all pending commands, runs in IO thread */
+ void completeCommands();
+
+ /** for scheduling a run of "completeCommands()" on the IO thread */
+ static void schedule(boost::intrusive_ptr<AsyncCommandCompleter>);
+
+ public:
+ AsyncCommandCompleter(SessionState *s) : session(s), isAttached(s->isAttached()) {};
+ ~AsyncCommandCompleter() {};
+
+ /** track a message pending ingress completion */
+ void addPendingMessage(boost::intrusive_ptr<Message> m);
+ void deletePendingMessage(SequenceNumber id);
+ void flushPendingMessages();
+ /** schedule the processing of a completed ingress message.transfer command */
+ void scheduleMsgCompletion(SequenceNumber cmd,
+ bool requiresAccept,
+ bool requiresSync);
+ void cancel(); // called by SessionState destructor.
+ void attached(); // called by SessionState on attach()
+ void detached(); // called by SessionState on detach()
+ };
+ boost::intrusive_ptr<AsyncCommandCompleter> asyncCommandCompleter;
+
+ /** Abstract class that represents a single asynchronous command that is
+ * pending completion.
+ */
+ class AsyncCommandContext : public AsyncCompletion::Callback
+ {
+ public:
+ AsyncCommandContext( SessionState *ss, SequenceNumber _id )
+ : id(_id), completerContext(ss->asyncCommandCompleter) {}
+ virtual ~AsyncCommandContext() {}
+
+ protected:
+ SequenceNumber id;
+ boost::intrusive_ptr<AsyncCommandCompleter> completerContext;
+ };
+
+ /** incomplete Message.transfer commands - inbound to broker from client
+ */
+ class IncompleteIngressMsgXfer : public SessionState::AsyncCommandContext
+ {
+ public:
+ IncompleteIngressMsgXfer( SessionState *ss,
+ boost::intrusive_ptr<Message> m )
+ : AsyncCommandContext(ss, m->getCommandId()),
+ session(ss),
+ msg(m),
+ requiresAccept(m->requiresAccept()),
+ requiresSync(m->getFrames().getMethod()->isSync()),
+ pending(false) {}
+ virtual ~IncompleteIngressMsgXfer() {};
+
+ virtual void completed(bool);
+ virtual boost::intrusive_ptr<AsyncCompletion::Callback> clone();
+
+ private:
+ SessionState *session; // only valid if sync flag in callback is true
+ boost::intrusive_ptr<Message> msg;
+ bool requiresAccept;
+ bool requiresSync;
+ bool pending; // true if msg saved on pending list...
+ };
+
+ friend class SessionManager;
+};
+
+
+inline std::ostream& operator<<(std::ostream& out, const SessionState& session) {
+ return out << session.getId();
+}
+
+}} // namespace qpid::broker
+
+
+
+#endif /*!QPID_BROKER_SESSION_H*/
diff --git a/qpid/cpp/src/qpid/broker/SignalHandler.cpp b/qpid/cpp/src/qpid/broker/SignalHandler.cpp
new file mode 100644
index 0000000000..16c141f21c
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SignalHandler.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/SignalHandler.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/sys/Mutex.h"
+#include <signal.h>
+
+namespace qpid {
+namespace broker {
+
+// Lock is to ensure that broker is not concurrently set to 0 and
+// deleted while we are in a call to broker->shutdown()
+
+sys::Mutex brokerLock;
+Broker* SignalHandler::broker;
+
+void SignalHandler::setBroker(Broker* b) {
+ sys::Mutex::ScopedLock l(brokerLock);
+ broker = b;
+ signal(SIGINT,shutdownHandler);
+ signal(SIGTERM, shutdownHandler);
+ signal(SIGHUP,SIG_IGN);
+ signal(SIGCHLD,SIG_IGN);
+}
+
+void SignalHandler::shutdown() { shutdownHandler(0); }
+
+void SignalHandler::shutdownHandler(int) {
+ sys::Mutex::ScopedLock l(brokerLock);
+ if (broker) {
+ broker->shutdown();
+ broker = 0;
+ }
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SignalHandler.h b/qpid/cpp/src/qpid/broker/SignalHandler.h
new file mode 100644
index 0000000000..7bfa9ea630
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/SignalHandler.h
@@ -0,0 +1,50 @@
+#ifndef QPID_BROKER_SIGNALHANDLER_H
+#define QPID_BROKER_SIGNALHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+/**
+ * Handle signals e.g. to shut-down a broker.
+ */
+class SignalHandler
+{
+ public:
+ /** Set the broker to be shutdown on signals.
+ * Must be reset by calling setBroker(0) before the broker is deleted.
+ */
+ static void setBroker(Broker* broker);
+
+ /** Initiate shut-down of broker */
+ static void shutdown();
+
+ private:
+ static void shutdownHandler(int);
+ static Broker* broker;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_SIGNALHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/broker/StatefulQueueObserver.h b/qpid/cpp/src/qpid/broker/StatefulQueueObserver.h
new file mode 100644
index 0000000000..c682d460b7
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/StatefulQueueObserver.h
@@ -0,0 +1,63 @@
+#ifndef QPID_BROKER_STATEFULQUEUEOBSERVER_H
+#define QPID_BROKER_STATEFULQUEUEOBSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/framing/FieldTable.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Specialized type of QueueObserver that maintains internal state that has to
+ * be replicated across clustered brokers.
+ */
+class StatefulQueueObserver : public QueueObserver
+{
+ public:
+ StatefulQueueObserver(std::string _id) : id(_id) {}
+ virtual ~StatefulQueueObserver() {}
+
+ /** This identifier must uniquely identify this particular observer amoung
+ * all observers on a queue. For cluster replication, this id will be used
+ * to identify the peer queue observer for synchronization across
+ * brokers.
+ */
+ const std::string& getId() const { return id; }
+
+ /** This method should return the observer's internal state as an opaque
+ * map.
+ */
+ virtual void getState(qpid::framing::FieldTable& state ) const = 0;
+
+ /** The input map represents the internal state of the peer observer that
+ * this observer should synchonize to.
+ */
+ virtual void setState(const qpid::framing::FieldTable&) = 0;
+
+
+ private:
+ std::string id;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_STATEFULQUEUEOBSERVER_H*/
diff --git a/qpid/cpp/src/qpid/broker/System.cpp b/qpid/cpp/src/qpid/broker/System.cpp
new file mode 100644
index 0000000000..8cd2edda76
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/System.cpp
@@ -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.
+//
+
+#include "qpid/broker/System.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/types/Uuid.h"
+#include <iostream>
+#include <fstream>
+
+using qpid::management::ManagementAgent;
+using namespace qpid::broker;
+using namespace std;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+System::System (string _dataDir, Broker* broker) : mgmtObject(0)
+{
+ ManagementAgent* agent = broker ? broker->getManagementAgent() : 0;
+
+ if (agent != 0)
+ {
+ framing::Uuid systemId;
+
+ if (_dataDir.empty ())
+ {
+ systemId.generate ();
+ }
+ else
+ {
+ string filename (_dataDir + "/systemId");
+ ifstream inFile (filename.c_str ());
+
+ if (inFile.good ())
+ {
+ inFile >> systemId;
+ inFile.close ();
+ }
+ else
+ {
+ systemId.generate ();
+ ofstream outFile (filename.c_str ());
+ if (outFile.good ())
+ {
+ outFile << systemId << endl;
+ outFile.close ();
+ }
+ }
+ }
+
+ mgmtObject = new _qmf::System(agent, this, types::Uuid(systemId.c_array()));
+ std::string sysname, nodename, release, version, machine;
+ qpid::sys::SystemInfo::getSystemId (sysname,
+ nodename,
+ release,
+ version,
+ machine);
+ mgmtObject->set_osName (sysname);
+ mgmtObject->set_nodeName (nodename);
+ mgmtObject->set_release (release);
+ mgmtObject->set_version (version);
+ mgmtObject->set_machine (machine);
+
+ agent->addObject(mgmtObject, 0, true);
+ }
+}
+
diff --git a/qpid/cpp/src/qpid/broker/System.h b/qpid/cpp/src/qpid/broker/System.h
new file mode 100644
index 0000000000..0fc2c2bd88
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/System.h
@@ -0,0 +1,51 @@
+#ifndef _BrokerSystem_
+#define _BrokerSystem_
+
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/System.h"
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+class System : public management::Manageable
+{
+ private:
+
+ qmf::org::apache::qpid::broker::System* mgmtObject;
+
+ public:
+
+ typedef boost::shared_ptr<System> shared_ptr;
+
+ System (std::string _dataDir, Broker* broker = 0);
+
+ management::ManagementObject* GetManagementObject (void) const
+ { return mgmtObject; }
+};
+
+}}
+
+#endif /*!_BrokerSystem_*/
diff --git a/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp
new file mode 100644
index 0000000000..3c9e210d4d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/ThresholdAlerts.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/broker/EventQueueThresholdExceeded.h"
+
+namespace qpid {
+namespace broker {
+namespace {
+const qmf::org::apache::qpid::broker::EventQueueThresholdExceeded EVENT("dummy", 0, 0);
+bool isQMFv2(const boost::intrusive_ptr<Message> message)
+{
+ const qpid::framing::MessageProperties* props = message->getProperties<qpid::framing::MessageProperties>();
+ return props && props->getAppId() == "qmf2";
+}
+
+bool isThresholdEvent(const boost::intrusive_ptr<Message> message)
+{
+ if (message->getIsManagementMessage()) {
+ //is this a qmf event? if so is it a threshold event?
+ if (isQMFv2(message)) {
+ const qpid::framing::FieldTable* headers = message->getApplicationHeaders();
+ if (headers && headers->getAsString("qmf.content") == "_event") {
+ //decode as list
+ std::string content = message->getFrames().getContent();
+ qpid::types::Variant::List list;
+ qpid::amqp_0_10::ListCodec::decode(content, list);
+ if (list.empty() || list.front().getType() != qpid::types::VAR_MAP) return false;
+ qpid::types::Variant::Map map = list.front().asMap();
+ try {
+ std::string eventName = map["_schema_id"].asMap()["_class_name"].asString();
+ return eventName == EVENT.getEventName();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error checking for recursive threshold alert: " << e.what());
+ }
+ }
+ } else {
+ std::string content = message->getFrames().getContent();
+ qpid::framing::Buffer buffer(const_cast<char*>(content.data()), content.size());
+ if (buffer.getOctet() == 'A' && buffer.getOctet() == 'M' && buffer.getOctet() == '2' && buffer.getOctet() == 'e') {
+ buffer.getLong();//sequence
+ std::string packageName;
+ buffer.getShortString(packageName);
+ if (packageName != EVENT.getPackageName()) return false;
+ std::string eventName;
+ buffer.getShortString(eventName);
+ return eventName == EVENT.getEventName();
+ }
+ }
+ }
+ return false;
+}
+}
+
+ThresholdAlerts::ThresholdAlerts(const std::string& n,
+ qpid::management::ManagementAgent& a,
+ const uint32_t ct,
+ const uint64_t st,
+ const long repeat)
+ : name(n), agent(a), countThreshold(ct), sizeThreshold(st),
+ repeatInterval(repeat ? repeat*qpid::sys::TIME_SEC : 0),
+ count(0), size(0), lastAlert(qpid::sys::EPOCH) {}
+
+void ThresholdAlerts::enqueued(const QueuedMessage& m)
+{
+ size += m.payload->contentSize();
+ ++count;
+ if ((countThreshold && count >= countThreshold) || (sizeThreshold && size >= sizeThreshold)) {
+ if ((repeatInterval == 0 && lastAlert == qpid::sys::EPOCH)
+ || qpid::sys::Duration(lastAlert, qpid::sys::now()) > repeatInterval) {
+ //Note: Raising an event may result in messages being
+ //enqueued on queues; it may even be that this event
+ //causes a message to be enqueued on the queue we are
+ //tracking, and so we need to avoid recursing
+ if (isThresholdEvent(m.payload)) return;
+ lastAlert = qpid::sys::now();
+ agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size));
+ QPID_LOG(info, "Threshold event triggered for " << name << ", count=" << count << ", size=" << size);
+ }
+ }
+}
+
+void ThresholdAlerts::dequeued(const QueuedMessage& m)
+{
+ size -= m.payload->contentSize();
+ --count;
+ if ((countThreshold && count < countThreshold) || (sizeThreshold && size < sizeThreshold)) {
+ lastAlert = qpid::sys::EPOCH;
+ }
+}
+
+
+
+void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const uint64_t countThreshold,
+ const uint64_t sizeThreshold,
+ const long repeatInterval)
+{
+ if (countThreshold || sizeThreshold) {
+ boost::shared_ptr<QueueObserver> observer(
+ new ThresholdAlerts(queue.getName(), agent, countThreshold, sizeThreshold, repeatInterval)
+ );
+ queue.addObserver(observer);
+ }
+}
+
+void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const qpid::framing::FieldTable& settings, uint16_t limitRatio)
+
+{
+ qpid::types::Variant::Map map;
+ qpid::amqp_0_10::translate(settings, map);
+ observe(queue, agent, map, limitRatio);
+}
+
+template <class T>
+class Option
+{
+ public:
+ Option(const std::string& name, T d) : defaultValue(d) { names.push_back(name); }
+ void addAlias(const std::string& name) { names.push_back(name); }
+ T get(const qpid::types::Variant::Map& settings) const
+ {
+ T value(defaultValue);
+ for (std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
+ if (get(settings, *i, value)) break;
+ }
+ return value;
+ }
+ private:
+ std::vector<std::string> names;
+ T defaultValue;
+
+ bool get(const qpid::types::Variant::Map& settings, const std::string& name, T& value) const
+ {
+ qpid::types::Variant::Map::const_iterator i = settings.find(name);
+ if (i != settings.end()) {
+ try {
+ value = (T) i->second;
+ } catch (const qpid::types::InvalidConversion&) {
+ QPID_LOG(warning, "Bad value for" << name << ": " << i->second);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const qpid::types::Variant::Map& settings, uint16_t limitRatio)
+
+{
+ //Note: aliases are keys defined by java broker
+ Option<int64_t> repeatInterval("qpid.alert_repeat_gap", 60);
+ repeatInterval.addAlias("x-qpid-minimum-alert-repeat-gap");
+
+ //If no explicit threshold settings were given use specified
+ //percentage of any limit from the policy.
+ const QueuePolicy* policy = queue.getPolicy();
+ Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy && limitRatio ? (policy->getMaxCount()*limitRatio/100) : 0));
+ countThreshold.addAlias("x-qpid-maximum-message-count");
+ Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy && limitRatio ? (policy->getMaxSize()*limitRatio/100) : 0));
+ sizeThreshold.addAlias("x-qpid-maximum-message-size");
+
+ observe(queue, agent, countThreshold.get(settings), sizeThreshold.get(settings), repeatInterval.get(settings));
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/ThresholdAlerts.h b/qpid/cpp/src/qpid/broker/ThresholdAlerts.h
new file mode 100644
index 0000000000..c77722e700
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ThresholdAlerts.h
@@ -0,0 +1,73 @@
+#ifndef QPID_BROKER_THRESHOLDALERTS_H
+#define QPID_BROKER_THRESHOLDALERTS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/sys/Time.h"
+#include "qpid/types/Variant.h"
+#include <string>
+
+namespace qpid {
+namespace framing {
+class FieldTable;
+}
+namespace management {
+class ManagementAgent;
+}
+namespace broker {
+
+class Queue;
+/**
+ * Class to manage generation of QMF alerts when particular thresholds
+ * are breached on a queue.
+ */
+class ThresholdAlerts : public QueueObserver
+{
+ public:
+ ThresholdAlerts(const std::string& name,
+ qpid::management::ManagementAgent& agent,
+ const uint32_t countThreshold,
+ const uint64_t sizeThreshold,
+ const long repeatInterval);
+ void enqueued(const QueuedMessage&);
+ void dequeued(const QueuedMessage&);
+ static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const uint64_t countThreshold,
+ const uint64_t sizeThreshold,
+ const long repeatInterval);
+ static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const qpid::framing::FieldTable& settings, uint16_t limitRatio);
+ static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
+ const qpid::types::Variant::Map& settings, uint16_t limitRatio);
+ private:
+ const std::string name;
+ qpid::management::ManagementAgent& agent;
+ const uint32_t countThreshold;
+ const uint64_t sizeThreshold;
+ const qpid::sys::Duration repeatInterval;
+ uint64_t count;
+ uint64_t size;
+ qpid::sys::AbsTime lastAlert;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_THRESHOLDALERTS_H*/
diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
new file mode 100644
index 0000000000..644a3d628e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
@@ -0,0 +1,692 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/broker/FedOps.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+
+
+namespace qpid {
+namespace broker {
+
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace std;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+
+// TODO aconway 2006-09-20: More efficient matching algorithm.
+// Areas for improvement:
+// - excessive string copying: should be 0 copy, match from original buffer.
+// - match/lookup: use descision tree or other more efficient structure.
+
+namespace
+{
+const std::string STAR("*");
+const std::string HASH("#");
+}
+
+// iterator for federation ReOrigin bind operation
+class TopicExchange::ReOriginIter : public TopicExchange::BindingNode::TreeIterator {
+public:
+ ReOriginIter() {};
+ ~ReOriginIter() {};
+ bool visit(BindingNode& node) {
+ if (node.bindings.fedBinding.hasLocal()) {
+ keys2prop.push_back(node.routePattern);
+ }
+ return true;
+ }
+ std::vector<std::string> keys2prop;
+};
+
+
+// match iterator used by route(): builds BindingList of all unique queues
+// that match the routing key.
+class TopicExchange::BindingsFinderIter : public TopicExchange::BindingNode::TreeIterator {
+public:
+ BindingsFinderIter(BindingList &bl) : b(bl) {};
+ ~BindingsFinderIter() {};
+
+ BindingList& b;
+ std::set<std::string> qSet;
+
+ bool visit(BindingNode& node) {
+
+ Binding::vector& qv(node.bindings.bindingVector);
+ for (Binding::vector::iterator j = qv.begin(); j != qv.end(); j++) {
+ // do not duplicate queues on the binding list
+ if (qSet.insert(j->get()->queue->getName()).second) {
+ b->push_back(*j);
+ }
+ }
+ return true;
+ }
+};
+
+
+
+// Iterator to visit all bindings until a given queue is found
+class TopicExchange::QueueFinderIter : public TopicExchange::BindingNode::TreeIterator {
+public:
+ QueueFinderIter(Queue::shared_ptr queue) : queue(queue), found(false) {};
+ ~QueueFinderIter() {};
+ bool visit(BindingNode& node) {
+
+ Binding::vector& qv(node.bindings.bindingVector);
+ Binding::vector::iterator q;
+ for (q = qv.begin(); q != qv.end(); q++) {
+ if ((*q)->queue == queue) {
+ found = true;
+ return false; // search done
+ }
+ }
+ return true; // continue search
+ }
+
+ Queue::shared_ptr queue;
+ bool found;
+};
+
+
+// Iterate over a string of '.'-separated tokens.
+struct TopicExchange::TokenIterator {
+ typedef pair<const char*,const char*> Token;
+
+ TokenIterator(const char* b, const char* e) : end(e), token(make_pair(b, find(b,e,'.'))) {}
+
+ TokenIterator(const string& key) : end(&key[0]+key.size()), token(make_pair(&key[0], find(&key[0],end,'.'))) {}
+
+ bool finished() const { return !token.first; }
+
+ void next() {
+ if (token.second == end)
+ token.first = token.second = 0;
+ else {
+ token.first=token.second+1;
+ token.second=(find(token.first, end, '.'));
+ }
+ }
+
+ void pop(string &top) {
+ ptrdiff_t l = len();
+ if (l) {
+ top.assign(token.first, l);
+ } else top.clear();
+ next();
+ }
+
+ bool match1(char c) const {
+ return token.second==token.first+1 && *token.first == c;
+ }
+
+ bool match(const Token& token2) const {
+ ptrdiff_t l=len();
+ return l == token2.second-token2.first &&
+ strncmp(token.first, token2.first, l) == 0;
+ }
+
+ bool match(const string& str) const {
+ ptrdiff_t l=len();
+ return l == ptrdiff_t(str.size()) &&
+ str.compare(0, l, token.first, l) == 0;
+ }
+
+ ptrdiff_t len() const { return token.second - token.first; }
+
+
+ const char* end;
+ Token token;
+};
+
+
+class TopicExchange::Normalizer : public TopicExchange::TokenIterator {
+ public:
+ Normalizer(string& p)
+ : TokenIterator(&p[0], &p[0]+p.size()), pattern(p)
+ { normalize(); }
+
+ private:
+ // Apply 2 transformations: #.* -> *.# and #.# -> #
+ void normalize() {
+ while (!finished()) {
+ if (match1('#')) {
+ const char* hash1=token.first;
+ next();
+ if (!finished()) {
+ if (match1('#')) { // Erase #.# -> #
+ pattern.erase(hash1-pattern.data(), 2);
+ token.first -= 2;
+ token.second -= 2;
+ end -= 2;
+ }
+ else if (match1('*')) { // Swap #.* -> *.#
+ swap(*const_cast<char*>(hash1),
+ *const_cast<char*>(token.first));
+ }
+ }
+ }
+ else
+ next();
+ }
+ }
+
+ string& pattern;
+};
+
+
+
+// Convert sequences of * and # to a sequence of * followed by a single #
+string TopicExchange::normalize(const string& pattern) {
+ string normal(pattern);
+ Normalizer n(normal);
+ return normal;
+}
+
+
+TopicExchange::TopicExchange(const string& _name, Manageable* _parent, Broker* b)
+ : Exchange(_name, _parent, b),
+ nBindings(0)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+TopicExchange::TopicExchange(const std::string& _name, bool _durable,
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b),
+ nBindings(0)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args)
+{
+ ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit.
+ string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind);
+ string fedTags(args ? args->getAsString(qpidFedTags) : "");
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ bool propagate = false;
+ string routingPattern = normalize(routingKey);
+
+ if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
+ RWlock::ScopedWlock l(lock);
+ BindingKey *bk = bindingTree.addBindingKey(routingPattern);
+ if (bk) {
+ Binding::vector& qv(bk->bindingVector);
+ Binding::vector::iterator q;
+ for (q = qv.begin(); q != qv.end(); q++) {
+ if ((*q)->queue == queue) {
+ // already bound, but may be from a different fedOrigin
+ bk->fedBinding.addOrigin(queue->getName(), fedOrigin);
+ return false;
+ }
+ }
+
+ Binding::shared_ptr binding (new Binding (routingPattern, queue, this, FieldTable(), fedOrigin));
+ binding->startManagement();
+ bk->bindingVector.push_back(binding);
+ nBindings++;
+ propagate = bk->fedBinding.addOrigin(queue->getName(), fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ QPID_LOG(debug, "Binding key [" << routingPattern << "] to queue " << queue->getName()
+ << " on exchange " << getName() << " (origin=" << fedOrigin << ")");
+ }
+ } else if (fedOp == fedOpUnbind) {
+ RWlock::ScopedWlock l(lock);
+ BindingKey* bk = getQueueBinding(queue, routingPattern);
+ if (bk) {
+ QPID_LOG(debug, "FedOpUnbind [" << routingPattern << "] from exchange " << getName()
+ << " on queue=" << queue->getName() << " origin=" << fedOrigin);
+ propagate = bk->fedBinding.delOrigin(queue->getName(), fedOrigin);
+ // if this was the last binding for the queue, delete the binding
+ if (bk->fedBinding.countFedBindings(queue->getName()) == 0) {
+ deleteBinding(queue, routingPattern, bk);
+ }
+ }
+ } else if (fedOp == fedOpReorigin) {
+ /** gather up all the keys that need rebinding in a local vector
+ * while holding the lock. Then propagate once the lock is
+ * released
+ */
+ ReOriginIter reOriginIter;
+ {
+ RWlock::ScopedRlock l(lock);
+ bindingTree.iterateAll( reOriginIter );
+ } /* lock dropped */
+
+ for (std::vector<std::string>::const_iterator key = reOriginIter.keys2prop.begin();
+ key != reOriginIter.keys2prop.end(); key++) {
+ propagateFedOp( *key, string(), fedOpBind, string());
+ }
+ }
+
+ cc.clearCache(); // clear the cache before we IVE route.
+ routeIVE();
+ if (propagate)
+ propagateFedOp(routingKey, fedTags, fedOp, fedOrigin);
+ return true;
+}
+
+bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* args)
+{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ QPID_LOG(debug, "Unbinding key [" << constRoutingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName() << " origin=" << fedOrigin << ")" );
+
+ ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit.
+ RWlock::ScopedWlock l(lock);
+ string routingKey = normalize(constRoutingKey);
+ BindingKey* bk = getQueueBinding(queue, routingKey);
+ if (!bk) return false;
+ bool propagate = bk->fedBinding.delOrigin(queue->getName(), fedOrigin);
+ deleteBinding(queue, routingKey, bk);
+ if (propagate)
+ propagateFedOp(routingKey, string(), fedOpUnbind, string());
+ return true;
+}
+
+
+bool TopicExchange::deleteBinding(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ BindingKey *bk)
+{
+ // Note well: write lock held by caller
+ Binding::vector& qv(bk->bindingVector);
+ Binding::vector::iterator q;
+ for (q = qv.begin(); q != qv.end(); q++)
+ if ((*q)->queue == queue)
+ break;
+ if(q == qv.end()) return false;
+ qv.erase(q);
+ assert(nBindings > 0);
+ nBindings--;
+
+ if(qv.empty()) {
+ bindingTree.removeBindingKey(routingKey);
+ }
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ QPID_LOG(debug, "Unbound key [" << routingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName());
+ return true;
+}
+
+/** returns a pointer to the BindingKey if the given queue is bound to this
+ * exchange using the routing pattern. 0 if queue binding does not exist.
+ */
+TopicExchange::BindingKey *TopicExchange::getQueueBinding(Queue::shared_ptr queue, const string& pattern)
+{
+ // Note well: lock held by caller....
+ BindingKey *bk = bindingTree.getBindingKey(pattern); // Exact match against binding pattern
+ if (!bk) return 0;
+ Binding::vector& qv(bk->bindingVector);
+ Binding::vector::iterator q;
+ for (q = qv.begin(); q != qv.end(); q++)
+ if ((*q)->queue == queue)
+ break;
+ return (q != qv.end()) ? bk : 0;
+}
+
+void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/)
+{
+ // Note: PERFORMANCE CRITICAL!!!
+ BindingList b;
+ std::map<std::string, BindingList>::iterator it;
+ { // only lock the cache for read
+ RWlock::ScopedRlock cl(cacheLock);
+ it = bindingCache.find(routingKey);
+ if (it != bindingCache.end()) {
+ b = it->second;
+ }
+ }
+ PreRoute pr(msg, this);
+ if (!b.get()) // no cache hit
+ {
+ RWlock::ScopedRlock l(lock);
+ b = BindingList(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ BindingsFinderIter bindingsFinder(b);
+ bindingTree.iterateMatch(routingKey, bindingsFinder);
+ RWlock::ScopedWlock cwl(cacheLock);
+ bindingCache[routingKey] = b; // update cache
+ }
+ doRoute(msg, b);
+}
+
+bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const)
+{
+ RWlock::ScopedRlock l(lock);
+ if (routingKey && queue) {
+ string key(normalize(*routingKey));
+ return getQueueBinding(queue, key) != 0;
+ } else if (!routingKey && !queue) {
+ return nBindings > 0;
+ } else if (routingKey) {
+ if (bindingTree.getBindingKey(*routingKey)) {
+ return true;
+ }
+ } else {
+ QueueFinderIter queueFinder(queue);
+ bindingTree.iterateAll( queueFinder );
+ return queueFinder.found;
+ }
+ return false;
+}
+
+TopicExchange::~TopicExchange() {}
+
+const std::string TopicExchange::typeName("topic");
+
+//
+// class BindingNode
+//
+
+TopicExchange::BindingNode::~BindingNode()
+{
+ childTokens.clear();
+}
+
+
+// Add a binding pattern to the tree. Return a pointer to the binding key
+// of the node that matches the binding pattern.
+TopicExchange::BindingKey*
+TopicExchange::BindingNode::addBindingKey(const std::string& normalizedRoute)
+{
+ TokenIterator bKey(normalizedRoute);
+ return addBindingKey(bKey, normalizedRoute);
+}
+
+
+// Return a pointer to the binding key of the leaf node that matches the binding pattern.
+TopicExchange::BindingKey*
+TopicExchange::BindingNode::getBindingKey(const std::string& normalizedRoute)
+{
+ TokenIterator bKey(normalizedRoute);
+ return getBindingKey(bKey);
+}
+
+
+// Delete the binding associated with the given route.
+void TopicExchange::BindingNode::removeBindingKey(const std::string& normalizedRoute)
+{
+ TokenIterator bKey2(normalizedRoute);
+ removeBindingKey(bKey2, normalizedRoute);
+}
+
+// visit each node in the tree. Note: all nodes are visited,
+// even non-leaf nodes (i.e. nodes without any bindings)
+bool TopicExchange::BindingNode::iterateAll(TopicExchange::BindingNode::TreeIterator& iter)
+{
+ if (!iter.visit(*this)) return false;
+
+ if (starChild && !starChild->iterateAll(iter)) return false;
+
+ if (hashChild && !hashChild->iterateAll(iter)) return false;
+
+ for (ChildMap::iterator ptr = childTokens.begin();
+ ptr != childTokens.end(); ptr++) {
+
+ if (!ptr->second->iterateAll(iter)) return false;
+ }
+
+ return true;
+}
+
+// applies iter against only matching nodes until iter returns false
+// Note Well: the iter may match against the same node more than once
+// if # wildcards are present!
+bool TopicExchange::BindingNode::iterateMatch(const std::string& routingKey, TreeIterator& iter)
+{
+ TopicExchange::TokenIterator rKey(routingKey);
+ return iterateMatch( rKey, iter );
+}
+
+
+// recurse over binding using token iterator.
+// Note well: bkey is modified!
+TopicExchange::BindingKey*
+TopicExchange::BindingNode::addBindingKey(TokenIterator &bKey,
+ const string& fullPattern)
+{
+ if (bKey.finished()) {
+ // this node's binding
+ if (routePattern.empty()) {
+ routePattern = fullPattern;
+ } else assert(routePattern == fullPattern);
+
+ return &bindings;
+
+ } else {
+ // pop the topmost token & recurse...
+
+ if (bKey.match(STAR)) {
+ if (!starChild) {
+ starChild.reset(new StarNode());
+ }
+ bKey.next();
+ return starChild->addBindingKey(bKey, fullPattern);
+
+ } else if (bKey.match(HASH)) {
+ if (!hashChild) {
+ hashChild.reset(new HashNode());
+ }
+ bKey.next();
+ return hashChild->addBindingKey(bKey, fullPattern);
+
+ } else {
+ ChildMap::iterator ptr;
+ std::string next_token;
+ bKey.pop(next_token);
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->addBindingKey(bKey, fullPattern);
+ } else {
+ BindingNode::shared_ptr child(new BindingNode(next_token));
+ childTokens[next_token] = child;
+ return child->addBindingKey(bKey, fullPattern);
+ }
+ }
+ }
+}
+
+
+// Remove a binding pattern from the tree. Return true if the current
+// node becomes a leaf without any bindings (therefore can be deleted).
+// Note Well: modifies parameter bKey's value!
+bool
+TopicExchange::BindingNode::removeBindingKey(TokenIterator &bKey,
+ const string& fullPattern)
+{
+ bool remove;
+
+ if (!bKey.finished()) {
+
+ if (bKey.match(STAR)) {
+ bKey.next();
+ if (starChild) {
+ remove = starChild->removeBindingKey(bKey, fullPattern);
+ if (remove) {
+ starChild.reset();
+ }
+ }
+ } else if (bKey.match(HASH)) {
+ bKey.next();
+ if (hashChild) {
+ remove = hashChild->removeBindingKey(bKey, fullPattern);
+ if (remove) {
+ hashChild.reset();
+ }
+ }
+ } else {
+ ChildMap::iterator ptr;
+ std::string next_token;
+ bKey.pop(next_token);
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ remove = ptr->second->removeBindingKey(bKey, fullPattern);
+ if (remove) {
+ childTokens.erase(ptr);
+ }
+ }
+ }
+ }
+
+ // no bindings and no children == parent can delete this node.
+ return getChildCount() == 0 && bindings.bindingVector.empty();
+}
+
+
+// find the binding key that matches the given binding pattern.
+// Note Well: modifies key parameter!
+TopicExchange::BindingKey*
+TopicExchange::BindingNode::getBindingKey(TokenIterator &key)
+{
+ if (key.finished()) {
+ return &bindings;
+ }
+
+ string next_token;
+
+ key.pop(next_token);
+
+ if (next_token == STAR) {
+ if (starChild)
+ return starChild->getBindingKey(key);
+ } else if (next_token == HASH) {
+ if (hashChild)
+ return hashChild->getBindingKey(key);
+ } else {
+ ChildMap::iterator ptr;
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->getBindingKey(key);
+ }
+ }
+
+ return 0;
+}
+
+
+
+// iterate over all nodes that match the given key. Note well: the set of nodes
+// that are visited includes matching non-leaf nodes.
+// Note well: parameter key is modified!
+bool TopicExchange::BindingNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
+{
+ // invariant: key has matched all previous tokens up to this node.
+ if (key.finished()) {
+ // exact match this node: visit if bound
+ if (!bindings.bindingVector.empty())
+ if (!iter.visit(*this)) return false;
+ }
+
+ // check remaining key against children, even if empty.
+ return iterateMatchChildren(key, iter);
+}
+
+
+TopicExchange::StarNode::StarNode()
+ : BindingNode(STAR) {}
+
+
+// See iterateMatch() above.
+// Special case: this node must verify a token is available (match exactly one).
+bool TopicExchange::StarNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
+{
+ // must match one token:
+ if (key.finished())
+ return true; // match failed, but continue iteration on siblings
+
+ // pop the topmost token
+ key.next();
+
+ if (key.finished()) {
+ // exact match this node: visit if bound
+ if (!bindings.bindingVector.empty())
+ if (!iter.visit(*this)) return false;
+ }
+
+ return iterateMatchChildren(key, iter);
+}
+
+
+TopicExchange::HashNode::HashNode()
+ : BindingNode(HASH) {}
+
+
+// See iterateMatch() above.
+// Special case: can match zero or more tokens at the head of the key.
+bool TopicExchange::HashNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
+{
+ // consume each token and look for a match on the
+ // remaining key.
+ while (!key.finished()) {
+ if (!iterateMatchChildren(key, iter)) return false;
+ key.next();
+ }
+
+ if (!bindings.bindingVector.empty())
+ return iter.visit(*this);
+
+ return true;
+}
+
+
+// helper: iterate over current node's matching children
+bool
+TopicExchange::BindingNode::iterateMatchChildren(const TopicExchange::TokenIterator& key,
+ TopicExchange::BindingNode::TreeIterator& iter)
+{
+ // always try glob - it can match empty keys
+ if (hashChild) {
+ TokenIterator tmp(key);
+ if (!hashChild->iterateMatch(tmp, iter))
+ return false;
+ }
+
+ if (!key.finished()) {
+
+ if (starChild) {
+ TokenIterator tmp(key);
+ if (!starChild->iterateMatch(tmp, iter))
+ return false;
+ }
+
+ if (!childTokens.empty()) {
+ TokenIterator newKey(key);
+ std::string next_token;
+ newKey.pop(next_token);
+
+ ChildMap::iterator ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->iterateMatch(newKey, iter);
+ }
+ }
+ }
+
+ return true;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.h b/qpid/cpp/src/qpid/broker/TopicExchange.h
new file mode 100644
index 0000000000..636918f8a1
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TopicExchange.h
@@ -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.
+ *
+ */
+#ifndef _TopicExchange_
+#define _TopicExchange_
+
+#include <map>
+#include <vector>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/broker/Queue.h"
+
+
+namespace qpid {
+namespace broker {
+
+class TopicExchange : public virtual Exchange {
+
+ struct TokenIterator;
+ class Normalizer;
+
+ struct BindingKey { // binding for this node
+ Binding::vector bindingVector;
+ FedBinding fedBinding;
+ };
+
+ // Binding database:
+ // The dotted form of a binding key is broken up and stored in a directed tree graph.
+ // Common binding prefix are merged. This allows the route match alogrithm to quickly
+ // isolate those sub-trees that match a given routingKey.
+ // For example, given the routes:
+ // a.b.c.<...>
+ // a.b.d.<...>
+ // a.x.y.<...>
+ // The resulting tree would be:
+ // a-->b-->c-->...
+ // | +-->d-->...
+ // +-->x-->y-->...
+ //
+ class QPID_BROKER_CLASS_EXTERN BindingNode {
+ public:
+
+ typedef boost::shared_ptr<BindingNode> shared_ptr;
+
+ // for database transversal (visit a node).
+ class TreeIterator {
+ public:
+ TreeIterator() {};
+ virtual ~TreeIterator() {};
+ virtual bool visit(BindingNode& node) = 0;
+ };
+
+ BindingNode() {};
+ BindingNode(const std::string& token) : token(token) {};
+ QPID_BROKER_EXTERN virtual ~BindingNode();
+
+ // add normalizedRoute to tree, return associated BindingKey
+ QPID_BROKER_EXTERN BindingKey* addBindingKey(const std::string& normalizedRoute);
+
+ // return BindingKey associated with normalizedRoute
+ QPID_BROKER_EXTERN BindingKey* getBindingKey(const std::string& normalizedRoute);
+
+ // remove BindingKey associated with normalizedRoute
+ QPID_BROKER_EXTERN void removeBindingKey(const std::string& normalizedRoute);
+
+ // applies iter against each node in tree until iter returns false
+ QPID_BROKER_EXTERN bool iterateAll(TreeIterator& iter);
+
+ // applies iter against only matching nodes until iter returns false
+ QPID_BROKER_EXTERN bool iterateMatch(const std::string& routingKey, TreeIterator& iter);
+
+ std::string routePattern; // normalized binding that matches this node
+ BindingKey bindings; // for matches against this node
+
+ protected:
+
+ std::string token; // portion of pattern represented by this node
+
+ // children
+ typedef std::map<const std::string, BindingNode::shared_ptr> ChildMap;
+ ChildMap childTokens;
+ BindingNode::shared_ptr starChild; // "*" subtree
+ BindingNode::shared_ptr hashChild; // "#" subtree
+
+ unsigned int getChildCount() { return childTokens.size() +
+ (starChild ? 1 : 0) + (hashChild ? 1 : 0); }
+ BindingKey* addBindingKey(TokenIterator& bKey,
+ const std::string& fullPattern);
+ bool removeBindingKey(TokenIterator& bKey,
+ const std::string& fullPattern);
+ BindingKey* getBindingKey(TokenIterator& bKey);
+ QPID_BROKER_EXTERN virtual bool iterateMatch(TokenIterator& rKey, TreeIterator& iter);
+ bool iterateMatchChildren(const TokenIterator& key, TreeIterator& iter);
+ };
+
+ // Special case: ("*" token) Node in the tree for a match exactly one wildcard
+ class StarNode : public BindingNode {
+ public:
+ StarNode();
+ ~StarNode() {};
+
+ protected:
+ virtual bool iterateMatch(TokenIterator& key, TreeIterator& iter);
+ };
+
+ // Special case: ("#" token) Node in the tree for a match zero or more
+ class HashNode : public BindingNode {
+ public:
+ HashNode();
+ ~HashNode() {};
+
+ protected:
+ virtual bool iterateMatch(TokenIterator& key, TreeIterator& iter);
+ };
+
+ BindingNode bindingTree;
+ unsigned long nBindings;
+ qpid::sys::RWlock lock; // protects bindingTree and nBindings
+ qpid::sys::RWlock cacheLock; // protects cache
+ std::map<std::string, BindingList> bindingCache; // cache of matched routes.
+ class ClearCache {
+ private:
+ qpid::sys::RWlock* cacheLock;
+ std::map<std::string, BindingList>* bindingCache;
+ bool cleared;
+ public:
+ ClearCache(qpid::sys::RWlock* l, std::map<std::string, BindingList>* bc): cacheLock(l),
+ bindingCache(bc),cleared(false) {};
+ void clearCache() {
+ qpid::sys::RWlock::ScopedWlock l(*cacheLock);
+ if (!cleared) {
+ bindingCache->clear();
+ cleared =true;
+ }
+ };
+ ~ClearCache(){
+ clearCache();
+ };
+ };
+ BindingKey *getQueueBinding(Queue::shared_ptr queue, const std::string& pattern);
+ bool deleteBinding(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ BindingKey *bk);
+
+ class ReOriginIter;
+ class BindingsFinderIter;
+ class QueueFinderIter;
+
+ public:
+ static const std::string typeName;
+
+ static QPID_BROKER_EXTERN std::string normalize(const std::string& pattern);
+
+ QPID_BROKER_EXTERN TopicExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN TopicExchange(const std::string& _name,
+ bool _durable,
+ const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const std::string* const routingKey,
+ const qpid::framing::FieldTable* const args);
+
+ QPID_BROKER_EXTERN virtual ~TopicExchange();
+ virtual bool supportsDynamicBinding() { return true; }
+
+ class TopicExchangeTester;
+ friend class TopicExchangeTester;
+};
+
+
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/TransactionalStore.h b/qpid/cpp/src/qpid/broker/TransactionalStore.h
new file mode 100644
index 0000000000..2a2bac0c51
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TransactionalStore.h
@@ -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.
+ *
+ */
+#ifndef _TransactionalStore_
+#define _TransactionalStore_
+
+#include <memory>
+#include <string>
+#include <set>
+
+namespace qpid {
+namespace broker {
+
+struct InvalidTransactionContextException : public std::exception {};
+
+class TransactionContext {
+public:
+ virtual ~TransactionContext(){}
+};
+
+class TPCTransactionContext : public TransactionContext {
+public:
+ virtual ~TPCTransactionContext(){}
+};
+
+class TransactionalStore {
+public:
+ virtual std::auto_ptr<TransactionContext> begin() = 0;
+ virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) = 0;
+ virtual void prepare(TPCTransactionContext& txn) = 0;
+ virtual void commit(TransactionContext& txn) = 0;
+ virtual void abort(TransactionContext& txn) = 0;
+
+ virtual void collectPreparedXids(std::set<std::string>& xids) = 0;
+
+ virtual ~TransactionalStore(){}
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/TxAccept.cpp b/qpid/cpp/src/qpid/broker/TxAccept.cpp
new file mode 100644
index 0000000000..928ac12c10
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxAccept.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/TxAccept.h"
+#include "qpid/log/Statement.h"
+
+using std::bind1st;
+using std::bind2nd;
+using std::mem_fun_ref;
+using namespace qpid::broker;
+using qpid::framing::SequenceSet;
+using qpid::framing::SequenceNumber;
+
+TxAccept::RangeOp::RangeOp(const AckRange& r) : range(r) {}
+
+void TxAccept::RangeOp::prepare(TransactionContext* ctxt)
+{
+ for_each(range.start, range.end, bind(&DeliveryRecord::dequeue, _1, ctxt));
+}
+
+void TxAccept::RangeOp::commit()
+{
+ for_each(range.start, range.end, bind(&DeliveryRecord::committed, _1));
+ for_each(range.start, range.end, bind(&DeliveryRecord::setEnded, _1));
+}
+
+TxAccept::RangeOps::RangeOps(DeliveryRecords& u) : unacked(u) {}
+
+void TxAccept::RangeOps::operator()(SequenceNumber start, SequenceNumber end)
+{
+ ranges.push_back(RangeOp(DeliveryRecord::findRange(unacked, start, end)));
+}
+
+void TxAccept::RangeOps::prepare(TransactionContext* ctxt)
+{
+ std::for_each(ranges.begin(), ranges.end(), bind(&RangeOp::prepare, _1, ctxt));
+}
+
+void TxAccept::RangeOps::commit()
+{
+ std::for_each(ranges.begin(), ranges.end(), bind(&RangeOp::commit, _1));
+ //now remove if isRedundant():
+ if (!ranges.empty()) {
+ DeliveryRecords::iterator begin = ranges.front().range.start;
+ DeliveryRecords::iterator end = ranges.back().range.end;
+ DeliveryRecords::iterator removed = remove_if(begin, end, mem_fun_ref(&DeliveryRecord::isRedundant));
+ unacked.erase(removed, end);
+ }
+}
+
+TxAccept::TxAccept(const SequenceSet& _acked, DeliveryRecords& _unacked) :
+ acked(_acked), unacked(_unacked), ops(unacked)
+{
+ //populate the ops
+ acked.for_each(ops);
+}
+
+bool TxAccept::prepare(TransactionContext* ctxt) throw()
+{
+ try{
+ ops.prepare(ctxt);
+ return true;
+ }catch(const std::exception& e){
+ QPID_LOG(error, "Failed to prepare: " << e.what());
+ return false;
+ }catch(...){
+ QPID_LOG(error, "Failed to prepare");
+ return false;
+ }
+}
+
+void TxAccept::commit() throw()
+{
+ try {
+ ops.commit();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to commit: " << e.what());
+ } catch(...) {
+ QPID_LOG(error, "Failed to commit (unknown error)");
+ }
+}
+
+void TxAccept::rollback() throw() {}
diff --git a/qpid/cpp/src/qpid/broker/TxAccept.h b/qpid/cpp/src/qpid/broker/TxAccept.h
new file mode 100644
index 0000000000..314a150176
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxAccept.h
@@ -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.
+ *
+ */
+#ifndef _TxAccept_
+#define _TxAccept_
+
+#include <algorithm>
+#include <functional>
+#include <list>
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/TxOp.h"
+
+namespace qpid {
+ namespace broker {
+ /**
+ * Defines the transactional behaviour for accepts received by
+ * a transactional channel.
+ */
+ class TxAccept : public TxOp {
+ struct RangeOp
+ {
+ AckRange range;
+
+ RangeOp(const AckRange& r);
+ void prepare(TransactionContext* ctxt);
+ void commit();
+ };
+
+ struct RangeOps
+ {
+ std::vector<RangeOp> ranges;
+ DeliveryRecords& unacked;
+
+ RangeOps(DeliveryRecords& u);
+
+ void operator()(framing::SequenceNumber start, framing::SequenceNumber end);
+ void prepare(TransactionContext* ctxt);
+ void commit();
+ };
+
+ framing::SequenceSet acked;
+ DeliveryRecords& unacked;
+ RangeOps ops;
+
+ public:
+ /**
+ * @param acked a representation of the accumulation of
+ * acks received
+ * @param unacked the record of delivered messages
+ */
+ TxAccept(const framing::SequenceSet& acked, DeliveryRecords& unacked);
+ virtual bool prepare(TransactionContext* ctxt) throw();
+ virtual void commit() throw();
+ virtual void rollback() throw();
+ virtual ~TxAccept(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ // Used by cluster replication.
+ const framing::SequenceSet& getAcked() const { return acked; }
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/TxBuffer.cpp b/qpid/cpp/src/qpid/broker/TxBuffer.cpp
new file mode 100644
index 0000000000..b509778e89
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxBuffer.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/TxBuffer.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/mem_fn.hpp>
+#include <boost/bind.hpp>
+using boost::mem_fn;
+using namespace qpid::broker;
+
+bool TxBuffer::prepare(TransactionContext* const ctxt)
+{
+ for(op_iterator i = ops.begin(); i < ops.end(); i++){
+ if(!(*i)->prepare(ctxt)){
+ return false;
+ }
+ }
+ return true;
+}
+
+void TxBuffer::commit()
+{
+ std::for_each(ops.begin(), ops.end(), mem_fn(&TxOp::commit));
+ ops.clear();
+}
+
+void TxBuffer::rollback()
+{
+ std::for_each(ops.begin(), ops.end(), mem_fn(&TxOp::rollback));
+ ops.clear();
+}
+
+void TxBuffer::enlist(TxOp::shared_ptr op)
+{
+ ops.push_back(op);
+}
+
+bool TxBuffer::commitLocal(TransactionalStore* const store)
+{
+ if (!store) return false;
+ try {
+ std::auto_ptr<TransactionContext> ctxt = store->begin();
+ if (prepare(ctxt.get())) {
+ store->commit(*ctxt);
+ commit();
+ return true;
+ } else {
+ store->abort(*ctxt);
+ rollback();
+ return false;
+ }
+ } catch (std::exception& e) {
+ QPID_LOG(error, "Commit failed with exception: " << e.what());
+ } catch (...) {
+ QPID_LOG(error, "Commit failed with unknown exception");
+ }
+ return false;
+}
+
+void TxBuffer::accept(TxOpConstVisitor& v) const {
+ std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v)));
+}
diff --git a/qpid/cpp/src/qpid/broker/TxBuffer.h b/qpid/cpp/src/qpid/broker/TxBuffer.h
new file mode 100644
index 0000000000..d49c8ba16a
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxBuffer.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#ifndef _TxBuffer_
+#define _TxBuffer_
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/broker/TxOp.h"
+
+/**
+ * Represents a single transaction. As such, an instance of this class
+ * will hold a list of operations representing the workload of the
+ * transaction. This work can be committed or rolled back. Committing
+ * is a two-stage process: first all the operations should be
+ * prepared, then if that succeeds they can be committed.
+ *
+ * In the 2pc case, a successful prepare may be followed by either a
+ * commit or a rollback.
+ *
+ * Atomicity of prepare is ensured by using a lower level
+ * transactional facility. This saves explicitly rolling back all the
+ * successfully prepared ops when one of them fails. i.e. we do not
+ * use 2pc internally, we instead ensure that prepare is atomic at a
+ * lower level. This makes individual prepare operations easier to
+ * code.
+ *
+ * Transactions on a messaging broker effect three types of 'action':
+ * (1) updates to persistent storage (2) updates to transient storage
+ * or cached data (3) network writes.
+ *
+ * Of these, (1) should always occur atomically during prepare to
+ * ensure that if the broker crashes while a transaction is being
+ * completed the persistent state (which is all that then remains) is
+ * consistent. (3) can only be done on commit, after a successful
+ * prepare. There is a little more flexibility with (2) but any
+ * changes made during prepare should be subject to the control of the
+ * TransactionalStore in use.
+ */
+namespace qpid {
+ namespace broker {
+ class TxBuffer{
+ typedef std::vector<TxOp::shared_ptr>::iterator op_iterator;
+ std::vector<TxOp::shared_ptr> ops;
+ protected:
+
+ public:
+ typedef boost::shared_ptr<TxBuffer> shared_ptr;
+ /**
+ * Adds an operation to the transaction.
+ */
+ QPID_BROKER_EXTERN void enlist(TxOp::shared_ptr op);
+
+ /**
+ * Requests that all ops are prepared. This should
+ * primarily involve making sure that a persistent record
+ * of the operations is stored where necessary.
+ *
+ * Once prepared, a transaction can be committed (or in
+ * the 2pc case, rolled back).
+ *
+ * @returns true if all the operations prepared
+ * successfully, false if not.
+ */
+ QPID_BROKER_EXTERN bool prepare(TransactionContext* const ctxt);
+
+ /**
+ * Signals that the ops all prepared successfully and can
+ * now commit, i.e. the operation can now be fully carried
+ * out.
+ *
+ * Should only be called after a call to prepare() returns
+ * true.
+ */
+ QPID_BROKER_EXTERN void commit();
+
+ /**
+ * Signals that all ops can be rolled back.
+ *
+ * Should only be called either after a call to prepare()
+ * returns true (2pc) or instead of a prepare call
+ * ('server-local')
+ */
+ QPID_BROKER_EXTERN void rollback();
+
+ /**
+ * Helper method for managing the process of server local
+ * commit
+ */
+ QPID_BROKER_EXTERN bool commitLocal(TransactionalStore* const store);
+
+ // Used by cluster to replicate transaction status.
+ void accept(TxOpConstVisitor& v) const;
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/TxOp.h b/qpid/cpp/src/qpid/broker/TxOp.h
new file mode 100644
index 0000000000..a8fa1c2621
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxOp.h
@@ -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.
+ *
+ */
+#ifndef _TxOp_
+#define _TxOp_
+
+#include "qpid/broker/TxOpVisitor.h"
+#include "qpid/broker/TransactionalStore.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+ namespace broker {
+
+ class TxOp{
+ public:
+ typedef boost::shared_ptr<TxOp> shared_ptr;
+
+ virtual bool prepare(TransactionContext*) throw() = 0;
+ virtual void commit() throw() = 0;
+ virtual void rollback() throw() = 0;
+ virtual ~TxOp(){}
+
+ virtual void accept(TxOpConstVisitor&) const = 0;
+ };
+
+}} // namespace qpid::broker
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/TxOpVisitor.h b/qpid/cpp/src/qpid/broker/TxOpVisitor.h
new file mode 100644
index 0000000000..ceb894896e
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxOpVisitor.h
@@ -0,0 +1,97 @@
+#ifndef QPID_BROKER_TXOPVISITOR_H
+#define QPID_BROKER_TXOPVISITOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qpid {
+namespace broker {
+
+class DtxAck;
+class RecoveredDequeue;
+class RecoveredEnqueue;
+class TxAccept;
+class TxPublish;
+
+/**
+ * Visitor for TxOp familly of classes.
+ */
+struct TxOpConstVisitor
+{
+ virtual ~TxOpConstVisitor() {}
+ virtual void operator()(const DtxAck&) = 0;
+ virtual void operator()(const RecoveredDequeue&) = 0;
+ virtual void operator()(const RecoveredEnqueue&) = 0;
+ virtual void operator()(const TxAccept&) = 0;
+ virtual void operator()(const TxPublish&) = 0;
+};
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_TXOPVISITOR_H*/
+#ifndef QPID_BROKER_TXOPVISITOR_H
+#define QPID_BROKER_TXOPVISITOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace broker {
+
+class DtxAck;
+class RecoveredDequeue;
+class RecoveredEnqueue;
+class TxAccept;
+class TxPublish;
+
+/**
+ * Visitor for TxOp familly of classes.
+ */
+struct TxOpConstVisitor
+{
+ virtual ~TxOpConstVisitor() {}
+ virtual void operator()(const DtxAck&) = 0;
+ virtual void operator()(const RecoveredDequeue&) = 0;
+ virtual void operator()(const RecoveredEnqueue&) = 0;
+ virtual void operator()(const TxAccept&) = 0;
+ virtual void operator()(const TxPublish&) = 0;
+};
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_TXOPVISITOR_H*/
diff --git a/qpid/cpp/src/qpid/broker/TxPublish.cpp b/qpid/cpp/src/qpid/broker/TxPublish.cpp
new file mode 100644
index 0000000000..9c2cf4a467
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxPublish.cpp
@@ -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.
+ *
+ */
+#include "qpid/log/Statement.h"
+#include "qpid/broker/TxPublish.h"
+#include "qpid/broker/Queue.h"
+
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+
+TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {}
+
+bool TxPublish::prepare(TransactionContext* ctxt) throw()
+{
+ try{
+ while (!queues.empty()) {
+ prepare(ctxt, queues.front());
+ prepared.push_back(queues.front());
+ queues.pop_front();
+ }
+ return true;
+ }catch(const std::exception& e){
+ QPID_LOG(error, "Failed to prepare: " << e.what());
+ }catch(...){
+ QPID_LOG(error, "Failed to prepare (unknown error)");
+ }
+ return false;
+}
+
+void TxPublish::commit() throw()
+{
+ try {
+ for_each(prepared.begin(), prepared.end(), Commit(msg));
+ if (msg->isContentReleaseRequested()) {
+ // NOTE: The log messages in this section are used for flow-to-disk testing (which checks the log for the
+ // presence of these messages). Do not change these without also checking these tests.
+ if (msg->isContentReleaseBlocked()) {
+ QPID_LOG(debug, "Message id=\"" << msg->getProperties<qpid::framing::MessageProperties>()->getMessageId() << "\"; pid=0x" <<
+ std::hex << msg->getPersistenceId() << std::dec << ": Content release blocked on commit");
+ } else {
+ msg->releaseContent();
+ QPID_LOG(debug, "Message id=\"" << msg->getProperties<qpid::framing::MessageProperties>()->getMessageId() << "\"; pid=0x" <<
+ std::hex << msg->getPersistenceId() << std::dec << ": Content released on commit");
+ }
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to commit: " << e.what());
+ } catch(...) {
+ QPID_LOG(error, "Failed to commit (unknown error)");
+ }
+}
+
+void TxPublish::rollback() throw()
+{
+ try {
+ for_each(prepared.begin(), prepared.end(), Rollback(msg));
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to complete rollback: " << e.what());
+ } catch(...) {
+ QPID_LOG(error, "Failed to complete rollback (unknown error)");
+ }
+
+}
+
+void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){
+ if (!queue->isLocal(msg)) {
+ queues.push_back(queue);
+ delivered = true;
+ } else {
+ QPID_LOG(debug, "Won't enqueue local message for " << queue->getName());
+ }
+}
+
+void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr<Queue> queue)
+{
+ queue->enqueue(ctxt, msg);
+}
+
+TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){}
+
+void TxPublish::Commit::operator()(const boost::shared_ptr<Queue>& queue){
+ queue->process(msg);
+}
+
+TxPublish::Rollback::Rollback(intrusive_ptr<Message>& _msg) : msg(_msg){}
+
+void TxPublish::Rollback::operator()(const boost::shared_ptr<Queue>& queue){
+ queue->enqueueAborted(msg);
+}
+
+uint64_t TxPublish::contentSize ()
+{
+ return msg->contentSize ();
+}
diff --git a/qpid/cpp/src/qpid/broker/TxPublish.h b/qpid/cpp/src/qpid/broker/TxPublish.h
new file mode 100644
index 0000000000..f0b9c0a302
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TxPublish.h
@@ -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.
+ *
+ */
+#ifndef _TxPublish_
+#define _TxPublish_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/TxOp.h"
+
+#include <algorithm>
+#include <functional>
+#include <list>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+ namespace broker {
+ /**
+ * Defines the behaviour for publish operations on a
+ * transactional channel. Messages are routed through
+ * exchanges when received but are not at that stage delivered
+ * to the matching queues, rather the queues are held in an
+ * instance of this class. On prepare() the message is marked
+ * enqueued to the relevant queues in the MessagesStore. On
+ * commit() the messages will be passed to the queue for
+ * dispatch or to be added to the in-memory queue.
+ */
+ class QPID_BROKER_CLASS_EXTERN TxPublish : public TxOp, public Deliverable{
+
+ class Commit{
+ boost::intrusive_ptr<Message>& msg;
+ public:
+ Commit(boost::intrusive_ptr<Message>& msg);
+ void operator()(const boost::shared_ptr<Queue>& queue);
+ };
+ class Rollback{
+ boost::intrusive_ptr<Message>& msg;
+ public:
+ Rollback(boost::intrusive_ptr<Message>& msg);
+ void operator()(const boost::shared_ptr<Queue>& queue);
+ };
+
+ boost::intrusive_ptr<Message> msg;
+ std::list<boost::shared_ptr<Queue> > queues;
+ std::list<boost::shared_ptr<Queue> > prepared;
+
+ void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>);
+
+ public:
+ QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr<Message> msg);
+ QPID_BROKER_EXTERN virtual bool prepare(TransactionContext* ctxt) throw();
+ QPID_BROKER_EXTERN virtual void commit() throw();
+ QPID_BROKER_EXTERN virtual void rollback() throw();
+
+ virtual Message& getMessage() { return *msg; };
+
+ QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue);
+
+ virtual ~TxPublish(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ QPID_BROKER_EXTERN uint64_t contentSize();
+
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+ const std::list<boost::shared_ptr<Queue> > getQueues() const { return queues; }
+ };
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/Vhost.cpp b/qpid/cpp/src/qpid/broker/Vhost.cpp
new file mode 100644
index 0000000000..a9ca3b42ab
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Vhost.cpp
@@ -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.
+//
+
+#include "qpid/broker/Vhost.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+
+using namespace qpid::broker;
+using qpid::management::ManagementAgent;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid { namespace management {
+class Manageable;
+}}
+
+Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmtObject(0)
+{
+ if (parentBroker != 0 && broker != 0)
+ {
+ ManagementAgent* agent = broker->getManagementAgent();
+
+ if (agent != 0)
+ {
+ mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/");
+ agent->addObject(mgmtObject, 0, true);
+ }
+ }
+}
+
+void Vhost::setFederationTag(const std::string& tag)
+{
+ mgmtObject->set_federationTag(tag);
+}
diff --git a/qpid/cpp/src/qpid/broker/Vhost.h b/qpid/cpp/src/qpid/broker/Vhost.h
new file mode 100644
index 0000000000..9554d641c2
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Vhost.h
@@ -0,0 +1,50 @@
+#ifndef _Vhost_
+#define _Vhost_
+
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Vhost.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+class Vhost : public management::Manageable
+{
+ private:
+
+ qmf::org::apache::qpid::broker::Vhost* mgmtObject;
+
+ public:
+
+ typedef boost::shared_ptr<Vhost> shared_ptr;
+
+ Vhost (management::Manageable* parentBroker, Broker* broker = 0);
+
+ management::ManagementObject* GetManagementObject (void) const
+ { return mgmtObject; }
+ void setFederationTag(const std::string& tag);
+};
+
+}}
+
+#endif /*!_Vhost_*/
diff --git a/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp b/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp
new file mode 100644
index 0000000000..9e463fa32d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/posix/BrokerDefaults.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Broker.h"
+#include <stdlib.h>
+
+namespace qpid {
+namespace broker {
+
+const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("/tmp");
+const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("/.qpidd");
+
+std::string
+Broker::Options::getHome() {
+ std::string home;
+ char *home_c = ::getenv("HOME");
+ if (home_c != 0)
+ home += home_c;
+ return home;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp b/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
new file mode 100644
index 0000000000..b65440b5ad
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Broker.h"
+#include <stdlib.h>
+
+namespace qpid {
+namespace broker {
+
+const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("\\TEMP");
+const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("\\QPIDD.DATA");
+
+std::string
+Broker::Options::getHome() {
+ std::string home;
+#ifdef _MSC_VER
+ char home_c[MAX_PATH+1];
+ size_t unused;
+ if (0 == getenv_s (&unused, home_c, sizeof(home_c), "HOME"))
+ home += home_c;
+#else
+ char *home_c = getenv("HOME");
+ if (home_c)
+ home += home_c;
+#endif
+ return home;
+}
+
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
new file mode 100644
index 0000000000..962877a471
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
@@ -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.
+ *
+ */
+
+// This source is only used on Windows; SSPI is the Windows mechanism for
+// accessing authentication mechanisms, analogous to Cyrus SASL.
+
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/reply_exceptions.h"
+
+#include <windows.h>
+
+using namespace qpid::framing;
+using qpid::sys::SecurityLayer;
+
+namespace qpid {
+namespace broker {
+
+class NullAuthenticator : public SaslAuthenticator
+{
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection client;
+public:
+ NullAuthenticator(Connection& connection);
+ ~NullAuthenticator();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string&) {}
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+};
+
+class SspiAuthenticator : public SaslAuthenticator
+{
+ HANDLE userToken;
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection client;
+
+public:
+ SspiAuthenticator(Connection& connection);
+ ~SspiAuthenticator();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string& response);
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+};
+
+bool SaslAuthenticator::available(void)
+{
+ return true;
+}
+
+// Initialize the SASL mechanism; throw if it fails.
+void SaslAuthenticator::init(const std::string& /*saslName*/, const std::string& /*saslConfig*/)
+{
+ return;
+}
+
+void SaslAuthenticator::fini(void)
+{
+ return;
+}
+
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c, bool)
+{
+ if (c.getBroker().getOptions().auth) {
+ return std::auto_ptr<SaslAuthenticator>(new SspiAuthenticator(c));
+ } else {
+ return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c));
+ }
+}
+
+NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {}
+NullAuthenticator::~NullAuthenticator() {}
+
+void NullAuthenticator::getMechanisms(Array& mechanisms)
+{
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS")));
+}
+
+void NullAuthenticator::start(const string& mechanism, const string& response)
+{
+ QPID_LOG(warning, "SASL: No Authentication Performed");
+ if (mechanism == "PLAIN") { // Old behavior
+ if (response.size() > 0 && response[0] == (char) 0) {
+ string temp = response.substr(1);
+ string::size_type i = temp.find((char)0);
+ string uid = temp.substr(0, i);
+ string pwd = temp.substr(i + 1);
+ connection.setUserId(uid);
+ }
+ } else {
+ connection.setUserId("anonymous");
+ }
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+}
+
+std::auto_ptr<SecurityLayer> NullAuthenticator::getSecurityLayer(uint16_t)
+{
+ std::auto_ptr<SecurityLayer> securityLayer;
+ return securityLayer;
+}
+
+
+SspiAuthenticator::SspiAuthenticator(Connection& c) : userToken(INVALID_HANDLE_VALUE), connection(c), client(c.getOutput())
+{
+}
+
+SspiAuthenticator::~SspiAuthenticator()
+{
+ if (INVALID_HANDLE_VALUE != userToken) {
+ CloseHandle(userToken);
+ userToken = INVALID_HANDLE_VALUE;
+ }
+}
+
+void SspiAuthenticator::getMechanisms(Array& mechanisms)
+{
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("ANONYMOUS"))));
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("PLAIN"))));
+ QPID_LOG(info, "SASL: Mechanism list: ANONYMOUS PLAIN");
+}
+
+void SspiAuthenticator::start(const string& mechanism, const string& response)
+{
+ QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism);
+ if (mechanism == "ANONYMOUS") {
+ connection.setUserId("anonymous");
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+ return;
+ }
+ if (mechanism != "PLAIN")
+ throw ConnectionForcedException("Unsupported mechanism");
+
+ // PLAIN's response is composed of 3 strings separated by 0 bytes:
+ // authorization id, authentication id (user), clear-text password.
+ if (response.size() == 0)
+ throw ConnectionForcedException("Authentication failed");
+
+ string::size_type i = response.find((char)0);
+ string auth = response.substr(0, i);
+ string::size_type j = response.find((char)0, i+1);
+ string uid = response.substr(i+1, j-1);
+ string pwd = response.substr(j+1);
+ string dot(".");
+ int error = 0;
+ if (!LogonUser(const_cast<char*>(uid.c_str()),
+ const_cast<char*>(dot.c_str()),
+ const_cast<char*>(pwd.c_str()),
+ LOGON32_LOGON_NETWORK,
+ LOGON32_PROVIDER_DEFAULT,
+ &userToken))
+ error = GetLastError();
+ pwd.replace(0, string::npos, 1, (char)0);
+ if (error != 0) {
+ QPID_LOG(info,
+ "SASL: Auth failed [" << error << "]: " << qpid::sys::strError(error));
+ throw ConnectionForcedException("Authentication failed");
+ }
+
+ connection.setUserId(uid);
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+}
+
+void SspiAuthenticator::step(const string& /*response*/)
+{
+ QPID_LOG(info, "SASL: Need another step!!!");
+}
+
+std::auto_ptr<SecurityLayer> SspiAuthenticator::getSecurityLayer(uint16_t)
+{
+ std::auto_ptr<SecurityLayer> securityLayer;
+ return securityLayer;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
new file mode 100644
index 0000000000..676074a590
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+#include <boost/bind.hpp>
+#include <memory>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+struct SslServerOptions : qpid::Options
+{
+ std::string certStore;
+ std::string certName;
+ uint16_t port;
+ bool clientAuth;
+
+ SslServerOptions() : qpid::Options("SSL Options"),
+ certStore("My"), port(5671), clientAuth(false)
+ {
+ qpid::Address me;
+ if (qpid::sys::SystemInfo::getLocalHostname(me))
+ certName = me.host;
+ else
+ certName = "localhost";
+
+ addOptions()
+ ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
+ ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
+ ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
+ ("ssl-require-client-authentication", optValue(clientAuth),
+ "Forces clients to authenticate in order to establish an SSL connection");
+ }
+};
+
+class SslProtocolFactory : public qpid::sys::ProtocolFactory {
+ qpid::sys::Socket listener;
+ const bool tcpNoDelay;
+ const uint16_t listeningPort;
+ std::string brokerHost;
+ const bool clientAuthSelected;
+ std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
+ ConnectFailedCallback connectFailedCallback;
+ CredHandle credHandle;
+
+ public:
+ SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+ ~SslProtocolFactory();
+ void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
+ void connect(sys::Poller::shared_ptr, const std::string& host, const std::string& port,
+ sys::ConnectionCodec::Factory*,
+ ConnectFailedCallback failed);
+
+ uint16_t getPort() const;
+ bool supports(const std::string& capability);
+
+ private:
+ void connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg);
+ void established(sys::Poller::shared_ptr,
+ const qpid::sys::Socket&,
+ sys::ConnectionCodec::Factory*,
+ bool isClient);
+};
+
+// Static instance to initialise plugin
+static struct SslPlugin : public Plugin {
+ SslServerOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ try {
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
+ opts.connectionBacklog,
+ opts.tcpNoDelay));
+ QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+ broker->registerProtocolFactory("ssl", protocol);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL listener: " << e.what());
+ }
+ }
+ }
+} sslPlugin;
+
+SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
+ int backlog,
+ bool nodelay)
+ : tcpNoDelay(nodelay),
+ listeningPort(listener.listen("", boost::lexical_cast<std::string>(options.port), backlog)),
+ clientAuthSelected(options.clientAuth) {
+
+ SecInvalidateHandle(&credHandle);
+
+ // Get the certificate for this server.
+ HCERTSTORE certStoreHandle;
+ certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ options.certStore.c_str());
+ if (!certStoreHandle)
+ throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+
+ PCCERT_CONTEXT certContext;
+ certContext = ::CertFindCertificateInStore(certStoreHandle,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_STR_A,
+ options.certName.c_str(),
+ NULL);
+ if (certContext == NULL) {
+ int err = ::GetLastError();
+ ::CertCloseStore(certStoreHandle, 0);
+ throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+ throw QPID_WINDOWS_ERROR(err);
+ }
+
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ cred.cCreds = 1;
+ cred.paCred = &certContext;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_INBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+ ::CertFreeCertificateContext(certContext);
+ ::CertCloseStore(certStoreHandle, 0);
+}
+
+SslProtocolFactory::~SslProtocolFactory() {
+ ::FreeCredentialsHandle(&credHandle);
+}
+
+void SslProtocolFactory::connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg) {
+ if (connectFailedCallback)
+ connectFailedCallback(err, msg);
+}
+
+void SslProtocolFactory::established(sys::Poller::shared_ptr poller,
+ const qpid::sys::Socket& s,
+ sys::ConnectionCodec::Factory* f,
+ bool isClient) {
+ sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getFullAddress(), f);
+
+ if (tcpNoDelay) {
+ s.setTcpNoDelay();
+ QPID_LOG(info,
+ "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ SslAsynchIO *aio;
+ if (isClient) {
+ async->setClient();
+ aio =
+ new qpid::sys::windows::ClientSslAsynchIO(brokerHost,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+ }
+ else {
+ aio =
+ new qpid::sys::windows::ServerSslAsynchIO(clientAuthSelected,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+ }
+
+ async->init(aio, 4);
+ aio->start(poller);
+}
+
+uint16_t SslProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
+ sys::ConnectionCodec::Factory* fact) {
+ acceptor.reset(
+ AsynchAcceptor::create(listener,
+ boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
+ acceptor->start(poller);
+}
+
+void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
+ const std::string& host,
+ const std::string& port,
+ sys::ConnectionCodec::Factory* fact,
+ ConnectFailedCallback failed)
+{
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+
+ brokerHost = host;
+ // Note that the following logic does not cause a memory leak.
+ // The allocated Socket is freed either by the AsynchConnector
+ // upon connection failure or by the AsynchIO upon connection
+ // shutdown. The allocated AsynchConnector frees itself when it
+ // is no longer needed.
+ qpid::sys::Socket* socket = new qpid::sys::Socket();
+ connectFailedCallback = failed;
+ AsynchConnector::create(*socket,
+ host,
+ port,
+ boost::bind(&SslProtocolFactory::established,
+ this, poller, _1, fact, true),
+ boost::bind(&SslProtocolFactory::connectFailed,
+ this, _1, _2, _3));
+}
+
+namespace
+{
+const std::string SSL = "ssl";
+}
+
+bool SslProtocolFactory::supports(const std::string& capability)
+{
+ std::string s = capability;
+ transform(s.begin(), s.end(), s.begin(), tolower);
+ return s == SSL;
+}
+
+}}} // namespace qpid::sys::windows
diff --git a/qpid/cpp/src/qpid/client/Bounds.cpp b/qpid/cpp/src/qpid/client/Bounds.cpp
new file mode 100644
index 0000000000..cc2577d5fc
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Bounds.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/Bounds.h"
+
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Waitable.h"
+
+namespace qpid {
+namespace client {
+
+using sys::Waitable;
+
+Bounds::Bounds(size_t maxSize) : max(maxSize), current(0) {}
+
+bool Bounds::expand(size_t sizeRequired, bool block) {
+ if (!max) return true;
+ Waitable::ScopedLock l(lock);
+ if (block) {
+ Waitable::ScopedWait w(lock);
+ while (current + sizeRequired > max)
+ lock.wait();
+ }
+ current += sizeRequired;
+ return current <= max;
+}
+
+void Bounds::reduce(size_t size) {
+ if (!max || size == 0) return;
+ Waitable::ScopedLock l(lock);
+ assert(current >= size);
+ current -= std::min(size, current);
+ if (current < max && lock.hasWaiters()) {
+ lock.notifyAll();
+ }
+}
+
+size_t Bounds::getCurrentSize() {
+ Waitable::ScopedLock l(lock);
+ return current;
+}
+
+std::ostream& operator<<(std::ostream& out, const Bounds& bounds) {
+ out << "current=" << bounds.current << ", max=" << bounds.max << " [" << &bounds << "]";
+ return out;
+}
+
+void Bounds::setException(const sys::ExceptionHolder& e) {
+ Waitable::ScopedLock l(lock);
+ lock.setException(e);
+ lock.waitWaiters(); // Wait for waiting threads to exit.
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/Bounds.h b/qpid/cpp/src/qpid/client/Bounds.h
new file mode 100644
index 0000000000..838fcb8368
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Bounds.h
@@ -0,0 +1,49 @@
+#ifndef QPID_CLIENT_BOUNDSCHECKING_H
+#define QPID_CLIENT_BOUNDSCHECKING_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Waitable.h"
+
+namespace qpid{
+namespace client{
+
+class Bounds
+{
+ public:
+ Bounds(size_t maxSize);
+ bool expand(size_t, bool block);
+ void reduce(size_t);
+ size_t getCurrentSize();
+ void setException(const sys::ExceptionHolder&);
+
+ private:
+ friend std::ostream& operator<<(std::ostream&, const Bounds&);
+ sys::Waitable lock;
+ const size_t max;
+ size_t current;
+};
+
+std::ostream& operator<<(std::ostream&, const Bounds&);
+
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/ChainableFrameHandler.h b/qpid/cpp/src/qpid/client/ChainableFrameHandler.h
new file mode 100644
index 0000000000..29e16d53dc
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ChainableFrameHandler.h
@@ -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.
+ *
+ */
+
+#ifndef _ChainableFrameHandler_
+#define _ChainableFrameHandler_
+
+#include <boost/function.hpp>
+#include "qpid/framing/AMQFrame.h"
+
+namespace qpid {
+namespace client {
+
+struct ChainableFrameHandler
+{
+ typedef boost::function<void(framing::AMQFrame&)> FrameDelegate;
+
+ FrameDelegate in;
+ FrameDelegate out;
+
+ ChainableFrameHandler() {}
+ ChainableFrameHandler(FrameDelegate i, FrameDelegate o): in(i), out(o) {}
+ virtual ~ChainableFrameHandler() {}
+};
+
+}}
+
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/Completion.cpp b/qpid/cpp/src/qpid/client/Completion.cpp
new file mode 100644
index 0000000000..a97c8c3534
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Completion.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Completion.h"
+#include "qpid/client/CompletionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<Completion> PI;
+Completion::Completion(CompletionImpl* p) { PI::ctor(*this, p); }
+Completion::Completion(const Completion& c) : Handle<CompletionImpl>() { PI::copy(*this, c); }
+Completion::~Completion() { PI::dtor(*this); }
+Completion& Completion::operator=(const Completion& c) { return PI::assign(*this, c); }
+
+
+void Completion::wait() { impl->wait(); }
+bool Completion::isComplete() { return impl->isComplete(); }
+std::string Completion::getResult() { return impl->getResult(); }
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/CompletionImpl.h b/qpid/cpp/src/qpid/client/CompletionImpl.h
new file mode 100644
index 0000000000..f180708316
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/CompletionImpl.h
@@ -0,0 +1,51 @@
+#ifndef QPID_CLIENT_COMPLETIONIMPL_H
+#define QPID_CLIENT_COMPLETIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include "qpid/client/Future.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace client {
+
+///@internal
+class CompletionImpl : public RefCounted
+{
+public:
+ CompletionImpl() {}
+ CompletionImpl(Future f, boost::shared_ptr<SessionImpl> s) : future(f), session(s) {}
+
+ bool isComplete() { return future.isComplete(*session); }
+ void wait() { future.wait(*session); }
+ std::string getResult() { return future.getResult(*session); }
+
+protected:
+ Future future;
+ boost::shared_ptr<SessionImpl> session;
+};
+
+}} // namespace qpid::client
+
+
+#endif /*!QPID_CLIENT_COMPLETIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/Connection.cpp b/qpid/cpp/src/qpid/client/Connection.cpp
new file mode 100644
index 0000000000..2882ef5d42
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Connection.cpp
@@ -0,0 +1,161 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/Url.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <functional>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+
+namespace qpid {
+namespace client {
+
+Connection::Connection() : version(framing::highestProtocolVersion)
+{
+ ConnectionImpl::init();
+}
+
+Connection::~Connection() {}
+
+void Connection::open(
+ const Url& url,
+ const std::string& uid, const std::string& pwd,
+ const std::string& vhost,
+ uint16_t maxFrameSize)
+{
+ ConnectionSettings settings;
+ settings.username = uid;
+ settings.password = pwd;
+ settings.virtualhost = vhost;
+ settings.maxFrameSize = maxFrameSize;
+ open(url, settings);
+}
+
+void Connection::open(const Url& url, const ConnectionSettings& settings) {
+ if (url.empty())
+ throw Exception(QPID_MSG("Attempt to open URL with no addresses."));
+ Url::const_iterator i = url.begin();
+ do {
+ const Address& addr = *i;
+ i++;
+ try {
+ ConnectionSettings cs(settings);
+ cs.protocol = addr.protocol;
+ cs.host = addr.host;
+ cs.port = addr.port;
+ open(cs);
+ break;
+ }
+ catch (const Exception& /*e*/) {
+ if (i == url.end()) throw;
+ }
+ } while (i != url.end());
+}
+
+void Connection::open(
+ const std::string& host, int port,
+ const std::string& uid, const std::string& pwd,
+ const std::string& vhost,
+ uint16_t maxFrameSize)
+{
+ ConnectionSettings settings;
+ settings.host = host;
+ settings.port = port;
+ settings.username = uid;
+ settings.password = pwd;
+ settings.virtualhost = vhost;
+ settings.maxFrameSize = maxFrameSize;
+ open(settings);
+}
+
+bool Connection::isOpen() const {
+ return impl && impl->isOpen();
+}
+
+void
+Connection::registerFailureCallback ( boost::function<void ()> fn ) {
+ failureCallback = fn;
+ if ( impl )
+ impl->registerFailureCallback ( fn );
+}
+
+
+
+void Connection::open(const ConnectionSettings& settings)
+{
+ if (isOpen())
+ throw Exception(QPID_MSG("Connection::open() was already called"));
+
+ impl = ConnectionImpl::create(version, settings);
+ impl->open();
+ if ( failureCallback )
+ impl->registerFailureCallback ( failureCallback );
+}
+
+const ConnectionSettings& Connection::getNegotiatedSettings()
+{
+ if (!isOpen())
+ throw Exception(QPID_MSG("Connection is not open."));
+ return impl->getNegotiatedSettings();
+}
+
+Session Connection::newSession(const std::string& name, uint32_t timeout) {
+ if (!isOpen())
+ throw Exception(QPID_MSG("Connection has not yet been opened"));
+ Session s;
+ SessionBase_0_10Access(s).set(impl->newSession(name, timeout));
+ return s;
+}
+
+void Connection::resume(Session& session) {
+ if (!isOpen())
+ throw Exception(QPID_MSG("Connection is not open."));
+ impl->addSession(session.impl);
+ session.impl->resume(impl);
+}
+
+void Connection::close() {
+ if ( impl )
+ impl->close();
+}
+
+std::vector<Url> Connection::getInitialBrokers() {
+ return impl ? impl->getInitialBrokers() : std::vector<Url>();
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/ConnectionAccess.h b/qpid/cpp/src/qpid/client/ConnectionAccess.h
new file mode 100644
index 0000000000..3a763f692f
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionAccess.h
@@ -0,0 +1,42 @@
+#ifndef QPID_CLIENT_CONNECTIONACCESS_H
+#define QPID_CLIENT_CONNECTIONACCESS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/Connection.h"
+
+/**@file @internal Internal use only */
+
+namespace qpid {
+namespace client {
+
+
+
+struct ConnectionAccess {
+ static void setVersion(Connection& c, const framing::ProtocolVersion& v) { c.version = v; }
+ static boost::shared_ptr<ConnectionImpl> getImpl(Connection& c) { return c.impl; }
+ static void setImpl(Connection& c, boost::shared_ptr<ConnectionImpl> i) { c.impl = i; }
+};
+
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_CONNECTIONACCESS_H*/
diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
new file mode 100644
index 0000000000..4fbf55aa60
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -0,0 +1,356 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/ConnectionHandler.h"
+
+#include "qpid/SaslFactory.h"
+#include "qpid/StringUtils.h"
+#include "qpid/client/Bounds.h"
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/all_method_bodies.h"
+#include "qpid/framing/ClientInvoker.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Helpers.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/SystemInfo.h"
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::framing::connection;
+using qpid::sys::SecurityLayer;
+using qpid::sys::Duration;
+using qpid::sys::TimerTask;
+using qpid::sys::Timer;
+using qpid::sys::AbsTime;
+using qpid::sys::TIME_SEC;
+using qpid::sys::ScopedLock;
+using qpid::sys::Mutex;
+
+namespace {
+const std::string OK("OK");
+const std::string PLAIN("PLAIN");
+const std::string en_US("en_US");
+
+const std::string INVALID_STATE_START("start received in invalid state");
+const std::string INVALID_STATE_TUNE("tune received in invalid state");
+const std::string INVALID_STATE_OPEN_OK("open-ok received in invalid state");
+const std::string INVALID_STATE_CLOSE_OK("close-ok received in invalid state");
+
+const std::string SESSION_FLOW_CONTROL("qpid.session_flow");
+const std::string CLIENT_PROCESS_NAME("qpid.client_process");
+const std::string CLIENT_PID("qpid.client_pid");
+const std::string CLIENT_PPID("qpid.client_ppid");
+const int SESSION_FLOW_CONTROL_VER = 1;
+}
+
+CloseCode ConnectionHandler::convert(uint16_t replyCode)
+{
+ switch (replyCode) {
+ case 200: return CLOSE_CODE_NORMAL;
+ case 320: return CLOSE_CODE_CONNECTION_FORCED;
+ case 402: return CLOSE_CODE_INVALID_PATH;
+ case 501: default:
+ return CLOSE_CODE_FRAMING_ERROR;
+ }
+}
+
+ConnectionHandler::Adapter::Adapter(ConnectionHandler& h, Bounds& b) : handler(h), bounds(b) {}
+void ConnectionHandler::Adapter::handle(qpid::framing::AMQFrame& f)
+{
+ bounds.expand(f.encodedSize(), false);
+ handler.out(f);
+}
+
+ConnectionHandler::ConnectionHandler(const ConnectionSettings& s, ProtocolVersion& v, Bounds& b)
+ : StateManager(NOT_STARTED), ConnectionSettings(s), outHandler(*this, b), proxy(outHandler),
+ errorCode(CLOSE_CODE_NORMAL), version(v)
+{
+ insist = true;
+
+ ESTABLISHED.insert(FAILED);
+ ESTABLISHED.insert(CLOSED);
+ ESTABLISHED.insert(OPEN);
+
+ FINISHED.insert(FAILED);
+ FINISHED.insert(CLOSED);
+
+ properties.setInt(SESSION_FLOW_CONTROL, SESSION_FLOW_CONTROL_VER);
+ properties.setString(CLIENT_PROCESS_NAME, sys::SystemInfo::getProcessName());
+ properties.setInt(CLIENT_PID, sys::SystemInfo::getProcessId());
+ properties.setInt(CLIENT_PPID, sys::SystemInfo::getParentProcessId());
+}
+
+void ConnectionHandler::incoming(AMQFrame& frame)
+{
+ if (getState() == CLOSED) {
+ throw Exception("Received frame on closed connection");
+ }
+
+ if (rcvTimeoutTask) {
+ // Received frame on connection so delay timeout
+ rcvTimeoutTask->restart();
+ }
+
+ AMQBody* body = frame.getBody();
+ try {
+ if (frame.getChannel() != 0 || !invoke(static_cast<ConnectionOperations&>(*this), *body)) {
+ switch(getState()) {
+ case OPEN:
+ in(frame);
+ break;
+ case CLOSING:
+ QPID_LOG(warning, "Ignoring frame while closing connection: " << frame);
+ break;
+ default:
+ throw Exception("Cannot receive frames on non-zero channel until connection is established.");
+ }
+ }
+ }catch(std::exception& e){
+ QPID_LOG(warning, "Closing connection due to " << e.what());
+ setState(CLOSING);
+ errorCode = CLOSE_CODE_FRAMING_ERROR;
+ errorText = e.what();
+ proxy.close(501, e.what());
+ }
+}
+
+void ConnectionHandler::outgoing(AMQFrame& frame)
+{
+ if (getState() == OPEN)
+ out(frame);
+ else
+ throw TransportFailure(errorText.empty() ? "Connection is not open." : errorText);
+}
+
+void ConnectionHandler::waitForOpen()
+{
+ waitFor(ESTABLISHED);
+ if (getState() == FAILED || getState() == CLOSED) {
+ throw ConnectionException(errorCode, errorText);
+ }
+}
+
+void ConnectionHandler::close()
+{
+ switch (getState()) {
+ case NEGOTIATING:
+ case OPENING:
+ fail("Connection closed before it was established");
+ break;
+ case OPEN:
+ if (setState(CLOSING, OPEN)) {
+ proxy.close(200, OK);
+ if (ConnectionSettings::heartbeat) {
+ //heartbeat timer is turned off at this stage, so don't wait indefinately
+ if (!waitFor(FINISHED, qpid::sys::Duration(ConnectionSettings::heartbeat * qpid::sys::TIME_SEC))) {
+ QPID_LOG(warning, "Connection close timed out");
+ }
+ } else {
+ waitFor(FINISHED);//FINISHED = CLOSED or FAILED
+ }
+ }
+ //else, state was changed from open after we checked, can only
+ //change to failed or closed, so nothing to do
+ break;
+
+ // Nothing to do if already CLOSING, CLOSED, FAILED or if NOT_STARTED
+ }
+}
+
+void ConnectionHandler::heartbeat()
+{
+ // Do nothing - the purpose of heartbeats is just to make sure that there is some
+ // traffic on the connection within the heart beat interval, we check for the
+ // traffic and don't need to do anything in response to heartbeats
+
+ // Although the above is still true we're now using a received heartbeat as a trigger
+ // to send out our own heartbeat
+ proxy.heartbeat();
+}
+
+void ConnectionHandler::checkState(STATES s, const std::string& msg)
+{
+ if (getState() != s) {
+ throw CommandInvalidException(msg);
+ }
+}
+
+void ConnectionHandler::fail(const std::string& message)
+{
+ errorCode = CLOSE_CODE_FRAMING_ERROR;
+ errorText = message;
+ QPID_LOG(warning, message);
+ setState(FAILED);
+}
+
+namespace {
+std::string SPACE(" ");
+
+std::string join(const std::vector<std::string>& in)
+{
+ std::string result;
+ for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) {
+ if (result.size()) result += SPACE;
+ result += *i;
+ }
+ return result;
+}
+
+void intersection(const std::vector<std::string>& a, const std::vector<std::string>& b, std::vector<std::string>& results)
+{
+ for (std::vector<std::string>::const_iterator i = a.begin(); i != a.end(); ++i) {
+ if (std::find(b.begin(), b.end(), *i) != b.end()) results.push_back(*i);
+ }
+}
+
+}
+
+void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& mechanisms, const Array& /*locales*/)
+{
+ checkState(NOT_STARTED, INVALID_STATE_START);
+ setState(NEGOTIATING);
+ sasl = SaslFactory::getInstance().create( username,
+ password,
+ service,
+ host,
+ minSsf,
+ maxSsf
+ );
+
+ std::vector<std::string> mechlist;
+ if (mechanism.empty()) {
+ //mechlist is simply what the server offers
+ mechanisms.collect(mechlist);
+ } else {
+ //mechlist is the intersection of those indicated by user and
+ //those supported by server, in the order listed by user
+ std::vector<std::string> allowed = split(mechanism, " ");
+ std::vector<std::string> supported;
+ mechanisms.collect(supported);
+ intersection(allowed, supported, mechlist);
+ if (mechlist.empty()) {
+ throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")"));
+ }
+ }
+
+ if (sasl.get()) {
+ string response = sasl->start(join(mechlist), getSecuritySettings ? getSecuritySettings() : 0);
+ proxy.startOk(properties, sasl->getMechanism(), response, locale);
+ } else {
+ //TODO: verify that desired mechanism and locale are supported
+ string response = ((char)0) + username + ((char)0) + password;
+ proxy.startOk(properties, mechanism, response, locale);
+ }
+}
+
+void ConnectionHandler::secure(const std::string& challenge)
+{
+ if (sasl.get()) {
+ string response = sasl->step(challenge);
+ proxy.secureOk(response);
+ } else {
+ throw NotImplementedException("Challenge-response cycle not yet implemented in client");
+ }
+}
+
+void ConnectionHandler::tune(uint16_t maxChannelsProposed, uint16_t maxFrameSizeProposed,
+ uint16_t heartbeatMin, uint16_t heartbeatMax)
+{
+ checkState(NEGOTIATING, INVALID_STATE_TUNE);
+ maxChannels = std::min(maxChannels, maxChannelsProposed);
+ maxFrameSize = std::min(maxFrameSize, maxFrameSizeProposed);
+ // Clip the requested heartbeat to the maximum/minimum offered
+ uint16_t heartbeat = ConnectionSettings::heartbeat;
+ heartbeat = heartbeat < heartbeatMin ? heartbeatMin :
+ heartbeat > heartbeatMax ? heartbeatMax :
+ heartbeat;
+ ConnectionSettings::heartbeat = heartbeat;
+ proxy.tuneOk(maxChannels, maxFrameSize, heartbeat);
+ setState(OPENING);
+ proxy.open(virtualhost, capabilities, insist);
+}
+
+void ConnectionHandler::openOk ( const Array& knownBrokers )
+{
+ checkState(OPENING, INVALID_STATE_OPEN_OK);
+ knownBrokersUrls.clear();
+ framing::Array::ValueVector::const_iterator i;
+ for ( i = knownBrokers.begin(); i != knownBrokers.end(); ++i )
+ knownBrokersUrls.push_back(Url((*i)->get<std::string>()));
+ if (sasl.get()) {
+ securityLayer = sasl->getSecurityLayer(maxFrameSize);
+ operUserId = sasl->getUserId();
+ }
+ setState(OPEN);
+ QPID_LOG(debug, "Known-brokers for connection: " << log::formatList(knownBrokersUrls));
+}
+
+
+void ConnectionHandler::redirect(const std::string& /*host*/, const Array& /*knownHosts*/)
+{
+ throw NotImplementedException("Redirection received from broker; not yet implemented in client");
+}
+
+void ConnectionHandler::close(uint16_t replyCode, const std::string& replyText)
+{
+ proxy.closeOk();
+ errorCode = convert(replyCode);
+ errorText = replyText;
+ setState(CLOSED);
+ QPID_LOG(warning, "Broker closed connection: " << replyCode << ", " << replyText);
+ if (onError) {
+ onError(replyCode, replyText);
+ }
+}
+
+void ConnectionHandler::closeOk()
+{
+ checkState(CLOSING, INVALID_STATE_CLOSE_OK);
+ if (onError && errorCode != CLOSE_CODE_NORMAL) {
+ onError(errorCode, errorText);
+ } else if (onClose) {
+ onClose();
+ }
+ setState(CLOSED);
+}
+
+bool ConnectionHandler::isOpen() const
+{
+ return getState() == OPEN;
+}
+
+bool ConnectionHandler::isClosed() const
+{
+ int s = getState();
+ return s == CLOSED || s == FAILED;
+}
+
+bool ConnectionHandler::isClosing() const { return getState() == CLOSING; }
+
+std::auto_ptr<qpid::sys::SecurityLayer> ConnectionHandler::getSecurityLayer()
+{
+ return securityLayer;
+}
+
+void ConnectionHandler::setRcvTimeoutTask(boost::intrusive_ptr<qpid::sys::TimerTask> t)
+{
+ rcvTimeoutTask = t;
+}
diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.h b/qpid/cpp/src/qpid/client/ConnectionHandler.h
new file mode 100644
index 0000000000..6af2e987fb
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionHandler.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionHandler_
+#define _ConnectionHandler_
+
+#include "qpid/client/ChainableFrameHandler.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/Sasl.h"
+#include "qpid/client/StateManager.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/framing/AMQP_ClientOperations.h"
+#include "qpid/framing/AMQP_ServerProxy.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/InputHandler.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/Url.h"
+#include <memory>
+
+namespace qpid {
+
+namespace sys {
+struct SecuritySettings;
+}
+
+namespace client {
+
+class Bounds;
+
+class ConnectionHandler : private StateManager,
+ public ConnectionSettings,
+ public ChainableFrameHandler,
+ public framing::InputHandler,
+ private framing::AMQP_ClientOperations::ConnectionHandler
+{
+ typedef framing::AMQP_ClientOperations::ConnectionHandler ConnectionOperations;
+ enum STATES {NOT_STARTED, NEGOTIATING, OPENING, OPEN, CLOSING, CLOSED, FAILED};
+ std::set<int> ESTABLISHED, FINISHED;
+
+ class Adapter : public framing::FrameHandler
+ {
+ ConnectionHandler& handler;
+ Bounds& bounds;
+ public:
+ Adapter(ConnectionHandler& h, Bounds& bounds);
+ void handle(framing::AMQFrame& f);
+ };
+
+ Adapter outHandler;
+ framing::AMQP_ServerProxy::Connection proxy;
+ framing::connection::CloseCode errorCode;
+ std::string errorText;
+ bool insist;
+ framing::ProtocolVersion version;
+ framing::Array capabilities;
+ framing::FieldTable properties;
+ std::auto_ptr<Sasl> sasl;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+ boost::intrusive_ptr<qpid::sys::TimerTask> rcvTimeoutTask;
+ std::string operUserId;
+
+ void checkState(STATES s, const std::string& msg);
+
+ //methods corresponding to connection controls:
+ void start(const framing::FieldTable& serverProperties,
+ const framing::Array& mechanisms,
+ const framing::Array& locales);
+ void secure(const std::string& challenge);
+ void tune(uint16_t channelMax,
+ uint16_t frameMax,
+ uint16_t heartbeatMin,
+ uint16_t heartbeatMax);
+ void openOk(const framing::Array& knownHosts);
+ void redirect(const std::string& host,
+ const framing::Array& knownHosts);
+ void close(uint16_t replyCode, const std::string& replyText);
+ void closeOk();
+ void heartbeat();
+
+public:
+ using InputHandler::handle;
+ typedef boost::function<void()> CloseListener;
+ typedef boost::function<void(uint16_t, const std::string&)> ErrorListener;
+ typedef boost::function<const qpid::sys::SecuritySettings*()> GetSecuritySettings;
+
+ ConnectionHandler(const ConnectionSettings&, framing::ProtocolVersion&, Bounds&);
+
+ void received(framing::AMQFrame& f) { incoming(f); }
+
+ void incoming(framing::AMQFrame& frame);
+ void outgoing(framing::AMQFrame& frame);
+
+ void waitForOpen();
+ void close();
+ void fail(const std::string& message);
+
+ // Note that open and closed aren't related by open = !closed
+ bool isOpen() const;
+ bool isClosed() const;
+ bool isClosing() const;
+
+ std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer();
+ void setRcvTimeoutTask(boost::intrusive_ptr<qpid::sys::TimerTask>);
+
+ CloseListener onClose;
+ ErrorListener onError;
+
+ std::vector<Url> knownBrokersUrls;
+
+ static framing::connection::CloseCode convert(uint16_t replyCode);
+ const std::string& getUserId() const { return operUserId; }
+ GetSecuritySettings getSecuritySettings; /** query the transport for its security details */
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp
new file mode 100644
index 0000000000..4b7aa07065
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/ConnectionImpl.h"
+
+#include "qpid/client/LoadPlugins.h"
+#include "qpid/client/Connector.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/SessionImpl.h"
+
+#include "qpid/log/Statement.h"
+#include "qpid/Url.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/Options.h"
+
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <limits>
+#include <vector>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::framing;
+using namespace qpid::framing::connection;
+using namespace qpid::sys;
+using namespace qpid::framing::connection;//for connection error codes
+
+namespace {
+// Maybe should amalgamate the singletons into a single client singleton
+
+// Get timer singleton
+Timer& theTimer() {
+ static Mutex timerInitLock;
+ ScopedLock<Mutex> l(timerInitLock);
+
+ static qpid::sys::Timer t;
+ return t;
+}
+
+struct IOThreadOptions : public qpid::Options {
+ int maxIOThreads;
+
+ IOThreadOptions(int c) :
+ Options("IO threading options"),
+ maxIOThreads(c)
+ {
+ addOptions()
+ ("max-iothreads", optValue(maxIOThreads, "N"), "Maximum number of io threads to use");
+ }
+};
+
+// IO threads
+class IOThread {
+ int maxIOThreads;
+ int ioThreads;
+ int connections;
+ Mutex threadLock;
+ std::vector<Thread> t;
+ Poller::shared_ptr poller_;
+
+public:
+ void add() {
+ ScopedLock<Mutex> l(threadLock);
+ ++connections;
+ if (!poller_)
+ poller_.reset(new Poller);
+ if (ioThreads < connections && ioThreads < maxIOThreads) {
+ QPID_LOG(debug, "Created IO thread: " << ioThreads);
+ ++ioThreads;
+ t.push_back( Thread(poller_.get()) );
+ }
+ }
+
+ void sub() {
+ ScopedLock<Mutex> l(threadLock);
+ --connections;
+ }
+
+ Poller::shared_ptr poller() const {
+ assert(poller_);
+ return poller_;
+ }
+
+ // Here is where the maximum number of threads is set
+ IOThread(int c) :
+ ioThreads(0),
+ connections(0)
+ {
+ IOThreadOptions options(c);
+ options.parse(0, 0, QPIDC_CONF_FILE, true);
+ maxIOThreads = (options.maxIOThreads != -1) ?
+ options.maxIOThreads : 1;
+ }
+
+ // We can't destroy threads one-by-one as the only
+ // control we have is to shutdown the whole lot
+ // and we can't do that before we're unloaded as we can't
+ // restart the Poller after shutting it down
+ ~IOThread() {
+ std::vector<Thread> threads;
+ {
+ ScopedLock<Mutex> l(threadLock);
+ if (poller_)
+ poller_->shutdown();
+ t.swap(threads);
+ }
+ for (std::vector<Thread>::iterator i = threads.begin(); i != threads.end(); ++i) {
+ i->join();
+ }
+ }
+};
+
+IOThread& theIO() {
+ static IOThread io(SystemInfo::concurrency());
+ return io;
+}
+
+class HeartbeatTask : public TimerTask {
+ TimeoutHandler& timeout;
+
+ void fire() {
+ // If we ever get here then we have timed out
+ QPID_LOG(debug, "Traffic timeout");
+ timeout.idleIn();
+ }
+
+public:
+ HeartbeatTask(Duration p, TimeoutHandler& t) :
+ TimerTask(p,"Heartbeat"),
+ timeout(t)
+ {}
+};
+
+}
+
+void ConnectionImpl::init() {
+ // Ensure that the plugin modules have been loaded
+ // This will make sure that any plugin protocols are available
+ theModuleLoader();
+
+ // Ensure the IO threads exist:
+ // This needs to be called in the Connection constructor
+ // so that they will still exist at last connection destruction
+ (void) theIO();
+}
+
+boost::shared_ptr<ConnectionImpl> ConnectionImpl::create(framing::ProtocolVersion version, const ConnectionSettings& settings)
+{
+ boost::shared_ptr<ConnectionImpl> instance(new ConnectionImpl(version, settings), boost::bind(&ConnectionImpl::release, _1));
+ return instance;
+}
+
+ConnectionImpl::ConnectionImpl(framing::ProtocolVersion v, const ConnectionSettings& settings)
+ : Bounds(settings.maxFrameSize * settings.bounds),
+ handler(settings, v, *this),
+ version(v),
+ nextChannel(1),
+ shutdownComplete(false),
+ released(false)
+{
+ handler.in = boost::bind(&ConnectionImpl::incoming, this, _1);
+ handler.out = boost::bind(&Connector::send, boost::ref(connector), _1);
+ handler.onClose = boost::bind(&ConnectionImpl::closed, this,
+ CLOSE_CODE_NORMAL, std::string());
+ //only set error handler once open
+ handler.onError = boost::bind(&ConnectionImpl::closed, this, _1, _2);
+ handler.getSecuritySettings = boost::bind(&Connector::getSecuritySettings, boost::ref(connector));
+}
+
+const uint16_t ConnectionImpl::NEXT_CHANNEL = std::numeric_limits<uint16_t>::max();
+
+ConnectionImpl::~ConnectionImpl() {
+ if (heartbeatTask) heartbeatTask->cancel();
+ theIO().sub();
+}
+
+void ConnectionImpl::addSession(const boost::shared_ptr<SessionImpl>& session, uint16_t channel)
+{
+ Mutex::ScopedLock l(lock);
+ for (uint16_t i = 0; i < NEXT_CHANNEL; i++) { //will at most search through channels once
+ uint16_t c = channel == NEXT_CHANNEL ? nextChannel++ : channel;
+ boost::weak_ptr<SessionImpl>& s = sessions[c];
+ boost::shared_ptr<SessionImpl> ss = s.lock();
+ if (!ss) {
+ //channel is free, we can assign it to this session
+ session->setChannel(c);
+ s = session;
+ return;
+ } else if (channel != NEXT_CHANNEL) {
+ //channel is taken and was requested explicitly so don't look for another
+ throw SessionBusyException(QPID_MSG("Channel " << ss->getChannel() << " attached to " << ss->getId()));
+ } //else channel is busy, but we can keep looking for a free one
+ }
+ // If we get here, we didn't find any available channel.
+ throw ResourceLimitExceededException("There are no channels available");
+}
+
+void ConnectionImpl::handle(framing::AMQFrame& frame)
+{
+ handler.outgoing(frame);
+}
+
+void ConnectionImpl::incoming(framing::AMQFrame& frame)
+{
+ boost::shared_ptr<SessionImpl> s;
+ {
+ Mutex::ScopedLock l(lock);
+ s = sessions[frame.getChannel()].lock();
+ }
+ if (!s) {
+ QPID_LOG(info, *this << " dropping frame received on invalid channel: " << frame);
+ } else {
+ s->in(frame);
+ }
+}
+
+bool ConnectionImpl::isOpen() const
+{
+ return handler.isOpen();
+}
+
+void ConnectionImpl::open()
+{
+ const std::string& protocol = handler.protocol;
+ const std::string& host = handler.host;
+ int port = handler.port;
+
+ theIO().add();
+ connector.reset(Connector::create(protocol, theIO().poller(), version, handler, this));
+ connector->setInputHandler(&handler);
+ connector->setShutdownHandler(this);
+ try {
+ std::string p = boost::lexical_cast<std::string>(port);
+ connector->connect(host, p);
+
+ } catch (const std::exception& e) {
+ QPID_LOG(debug, "Failed to connect to " << protocol << ":" << host << ":" << port << " " << e.what());
+ connector.reset();
+ throw;
+ }
+ connector->init();
+
+ // Enable heartbeat if requested
+ uint16_t heartbeat = static_cast<ConnectionSettings&>(handler).heartbeat;
+ if (heartbeat) {
+ // Set connection timeout to be 2x heart beat interval and setup timer
+ heartbeatTask = new HeartbeatTask(heartbeat * 2 * TIME_SEC, *this);
+ handler.setRcvTimeoutTask(heartbeatTask);
+ theTimer().add(heartbeatTask);
+ }
+
+ // If the connect fails then the connector is cleaned up either when we try to connect again
+ // - in that case in connector.reset() above;
+ // - or when we are deleted
+ handler.waitForOpen();
+ QPID_LOG(info, *this << " connected to " << protocol << ":" << host << ":" << port);
+
+ // If the SASL layer has provided an "operational" userId for the connection,
+ // put it in the negotiated settings.
+ const std::string& userId(handler.getUserId());
+ if (!userId.empty())
+ handler.username = userId;
+
+ //enable security layer if one has been negotiated:
+ std::auto_ptr<SecurityLayer> securityLayer = handler.getSecurityLayer();
+ if (securityLayer.get()) {
+ QPID_LOG(debug, *this << " activating security layer");
+ connector->activateSecurityLayer(securityLayer);
+ } else {
+ QPID_LOG(debug, *this << " no security layer in place");
+ }
+}
+
+void ConnectionImpl::idleIn()
+{
+ connector->abort();
+}
+
+void ConnectionImpl::idleOut()
+{
+ AMQFrame frame((AMQHeartbeatBody()));
+ connector->send(frame);
+}
+
+void ConnectionImpl::close()
+{
+ if (heartbeatTask)
+ heartbeatTask->cancel();
+ // close() must be idempotent and no-throw as it will often be called in destructors.
+ if (handler.isOpen()) {
+ try {
+ handler.close();
+ closed(CLOSE_CODE_NORMAL, "Closed by client");
+ } catch (...) {}
+ }
+ assert(!handler.isOpen());
+}
+
+
+template <class F> void ConnectionImpl::closeInternal(const F& f) {
+ if (heartbeatTask) {
+ heartbeatTask->cancel();
+ }
+ {
+ Mutex::ScopedUnlock u(lock);
+ connector->close();
+ }
+ //notifying sessions of failure can result in those session being
+ //deleted which in turn results in a call to erase(); this can
+ //even happen on this thread, when 's' goes out of scope
+ //below. Using a copy prevents the map being modified as we
+ //iterate through.
+ SessionMap copy;
+ sessions.swap(copy);
+ for (SessionMap::iterator i = copy.begin(); i != copy.end(); ++i) {
+ boost::shared_ptr<SessionImpl> s = i->second.lock();
+ if (s) f(s);
+ }
+}
+
+void ConnectionImpl::closed(uint16_t code, const std::string& text) {
+ Mutex::ScopedLock l(lock);
+ setException(new ConnectionException(ConnectionHandler::convert(code), text));
+ closeInternal(boost::bind(&SessionImpl::connectionClosed, _1, code, text));
+}
+
+void ConnectionImpl::shutdown() {
+ if (!handler.isClosed()) {
+ failedConnection();
+ }
+ bool canDelete;
+ {
+ Mutex::ScopedLock l(lock);
+ //association with IO thread is now ended
+ shutdownComplete = true;
+ //If we have already been released, we can now delete ourselves
+ canDelete = released;
+ }
+ if (canDelete) delete this;
+}
+
+void ConnectionImpl::release() {
+ bool isActive;
+ {
+ Mutex::ScopedLock l(lock);
+ isActive = connector && !shutdownComplete;
+ }
+ //If we are still active - i.e. associated with an IO thread -
+ //then we cannot delete ourselves yet, but must wait for the
+ //shutdown callback which we can trigger by calling
+ //connector.close()
+ if (isActive) {
+ connector->close();
+ bool canDelete;
+ {
+ Mutex::ScopedLock l(lock);
+ released = true;
+ canDelete = shutdownComplete;
+ }
+ if (canDelete) delete this;
+ } else {
+ delete this;
+ }
+}
+
+static const std::string CONN_CLOSED("Connection closed");
+
+void ConnectionImpl::failedConnection() {
+ if ( failureCallback )
+ failureCallback();
+
+ if (handler.isClosed()) return;
+
+ bool isClosing = handler.isClosing();
+ bool isOpen = handler.isOpen();
+
+ std::ostringstream msg;
+ msg << *this << " closed";
+
+ // FIXME aconway 2008-06-06: exception use, amqp0-10 does not seem to have
+ // an appropriate close-code. connection-forced is not right.
+ handler.fail(msg.str());//ensure connection is marked as failed before notifying sessions
+
+ // At this point if the object isn't open and isn't closing it must have failed to open
+ // so we can't do the rest of the cleanup
+ if (!isClosing && !isOpen) return;
+
+ Mutex::ScopedLock l(lock);
+ closeInternal(boost::bind(&SessionImpl::connectionBroke, _1, msg.str()));
+ setException(new TransportFailure(msg.str()));
+}
+
+void ConnectionImpl::erase(uint16_t ch) {
+ Mutex::ScopedLock l(lock);
+ sessions.erase(ch);
+}
+
+const ConnectionSettings& ConnectionImpl::getNegotiatedSettings()
+{
+ return handler;
+}
+
+std::vector<qpid::Url> ConnectionImpl::getInitialBrokers() {
+ return handler.knownBrokersUrls;
+}
+
+boost::shared_ptr<SessionImpl> ConnectionImpl::newSession(const std::string& name, uint32_t timeout, uint16_t channel) {
+ boost::shared_ptr<SessionImpl> simpl(new SessionImpl(name, shared_from_this()));
+ addSession(simpl, channel);
+ simpl->open(timeout);
+ return simpl;
+}
+
+std::ostream& operator<<(std::ostream& o, const ConnectionImpl& c) {
+ if (c.connector)
+ return o << "Connection " << c.connector->getIdentifier();
+ else
+ return o << "Connection <not connected>";
+}
+
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.h b/qpid/cpp/src/qpid/client/ConnectionImpl.h
new file mode 100644
index 0000000000..cc81500b18
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionImpl.h
@@ -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.
+ *
+ */
+
+#ifndef _ConnectionImpl_
+#define _ConnectionImpl_
+
+#include "qpid/client/Bounds.h"
+#include "qpid/client/ConnectionHandler.h"
+
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/ShutdownHandler.h"
+#include "qpid/sys/TimeoutHandler.h"
+
+#include <map>
+#include <iosfwd>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace qpid {
+namespace client {
+
+class Connector;
+struct ConnectionSettings;
+class SessionImpl;
+
+class ConnectionImpl : public Bounds,
+ public framing::FrameHandler,
+ public sys::TimeoutHandler,
+ public sys::ShutdownHandler,
+ public boost::enable_shared_from_this<ConnectionImpl>
+{
+ typedef std::map<uint16_t, boost::weak_ptr<SessionImpl> > SessionMap;
+
+ static const uint16_t NEXT_CHANNEL;
+
+ SessionMap sessions;
+ ConnectionHandler handler;
+ boost::scoped_ptr<Connector> connector;
+ framing::ProtocolVersion version;
+ uint16_t nextChannel;
+ sys::Mutex lock;
+ bool shutdownComplete;
+ bool released;
+
+ boost::intrusive_ptr<qpid::sys::TimerTask> heartbeatTask;
+
+ template <class F> void closeInternal(const F&);
+
+ void incoming(framing::AMQFrame& frame);
+ void closed(uint16_t, const std::string&);
+ void idleOut();
+ void idleIn();
+ void shutdown();
+ void failedConnection();
+ void release();
+ ConnectionImpl(framing::ProtocolVersion version, const ConnectionSettings& settings);
+
+ boost::function<void ()> failureCallback;
+
+ public:
+ static void init();
+ static boost::shared_ptr<ConnectionImpl> create(framing::ProtocolVersion version, const ConnectionSettings& settings);
+ ~ConnectionImpl();
+
+ void open();
+ bool isOpen() const;
+
+ boost::shared_ptr<SessionImpl> newSession(const std::string& name, uint32_t timeout, uint16_t channel=NEXT_CHANNEL);
+ void addSession(const boost::shared_ptr<SessionImpl>&, uint16_t channel=NEXT_CHANNEL);
+
+ void close();
+ void handle(framing::AMQFrame& frame);
+ void erase(uint16_t channel);
+ const ConnectionSettings& getNegotiatedSettings();
+
+ std::vector<Url> getInitialBrokers();
+ void registerFailureCallback ( boost::function<void ()> fn ) { failureCallback = fn; }
+ framing::ProtocolVersion getVersion() { return version; }
+
+ friend std::ostream& operator<<(std::ostream&, const ConnectionImpl&);
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/ConnectionSettings.cpp b/qpid/cpp/src/qpid/client/ConnectionSettings.cpp
new file mode 100644
index 0000000000..822e4af269
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/ConnectionSettings.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/ConnectionSettings.h"
+
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/Url.h"
+#include "qpid/Version.h"
+
+namespace qpid {
+namespace client {
+
+ConnectionSettings::ConnectionSettings() :
+ protocol("tcp"),
+ host("localhost"),
+ port(5672),
+ locale("en_US"),
+ heartbeat(0),
+ maxChannels(32767),
+ maxFrameSize(65535),
+ bounds(2),
+ tcpNoDelay(false),
+ service(qpid::saslName),
+ minSsf(0),
+ maxSsf(256),
+ sslCertName("")
+{}
+
+ConnectionSettings::~ConnectionSettings() {}
+
+void ConnectionSettings::configureSocket(qpid::sys::Socket& socket) const
+{
+ if (tcpNoDelay) {
+ socket.setTcpNoDelay();
+ QPID_LOG(info, "Set TCP_NODELAY");
+ }
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/Connector.cpp b/qpid/cpp/src/qpid/client/Connector.cpp
new file mode 100644
index 0000000000..c71dd9ecb6
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Connector.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Connector.h"
+#include "qpid/Url.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/SecurityLayer.h"
+
+#include <map>
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::sys;
+using namespace qpid::framing;
+
+namespace {
+ typedef std::map<std::string, Connector::Factory*> ProtocolRegistry;
+
+ ProtocolRegistry& theProtocolRegistry() {
+ static ProtocolRegistry protocolRegistry;
+
+ return protocolRegistry;
+ }
+}
+
+Connector* Connector::create(const std::string& proto,
+ boost::shared_ptr<Poller> p,
+ framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c)
+{
+ ProtocolRegistry::const_iterator i = theProtocolRegistry().find(proto);
+ if (i==theProtocolRegistry().end()) {
+ throw Exception(QPID_MSG("Unknown protocol: " << proto));
+ }
+ return (i->second)(p, v, s, c);
+}
+
+void Connector::registerFactory(const std::string& proto, Factory* connectorFactory)
+{
+ ProtocolRegistry::const_iterator i = theProtocolRegistry().find(proto);
+ if (i!=theProtocolRegistry().end()) {
+ QPID_LOG(error, "Tried to register protocol: " << proto << " more than once");
+ }
+ theProtocolRegistry()[proto] = connectorFactory;
+ Url::addProtocol(proto);
+}
+
+void Connector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>)
+{
+}
+
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/Connector.h b/qpid/cpp/src/qpid/client/Connector.h
new file mode 100644
index 0000000000..bc611ffe0d
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Connector.h
@@ -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.
+ *
+ */
+#ifndef _Connector_
+#define _Connector_
+
+
+#include "qpid/framing/OutputHandler.h"
+#include "qpid/framing/ProtocolVersion.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+
+namespace qpid {
+
+namespace sys {
+class ShutdownHandler;
+class SecurityLayer;
+class Poller;
+struct SecuritySettings;
+}
+
+namespace framing {
+class InputHandler;
+class AMQFrame;
+}
+
+namespace client {
+
+struct ConnectionSettings;
+class ConnectionImpl;
+
+///@internal
+class Connector : public framing::OutputHandler
+{
+ public:
+ // Protocol connector factory related stuff (it might be better to separate this code from the TCP Connector in the future)
+ typedef Connector* Factory(boost::shared_ptr<qpid::sys::Poller>,
+ framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*);
+ static Connector* create(const std::string& proto,
+ boost::shared_ptr<qpid::sys::Poller>,
+ framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*);
+ static void registerFactory(const std::string& proto, Factory* connectorFactory);
+
+ virtual ~Connector() {};
+ virtual void connect(const std::string& host, const std::string& port) = 0;
+ virtual void init() {};
+ virtual void close() = 0;
+ virtual void send(framing::AMQFrame& frame) = 0;
+ virtual void abort() = 0;
+
+ virtual void setInputHandler(framing::InputHandler* handler) = 0;
+ virtual void setShutdownHandler(sys::ShutdownHandler* handler) = 0;
+ virtual sys::ShutdownHandler* getShutdownHandler() const = 0;
+ virtual framing::OutputHandler* getOutputHandler() = 0;
+ virtual const std::string& getIdentifier() const = 0;
+
+ virtual void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>);
+
+ virtual const qpid::sys::SecuritySettings* getSecuritySettings() = 0;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/Demux.cpp b/qpid/cpp/src/qpid/client/Demux.cpp
new file mode 100644
index 0000000000..abc23c75df
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Demux.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Demux.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/MessageTransferBody.h"
+
+#include <iostream>
+
+namespace qpid {
+namespace client {
+
+ByTransferDest::ByTransferDest(const std::string& d) : dest(d) {}
+bool ByTransferDest::operator()(const framing::FrameSet& frameset) const
+{
+ return frameset.isA<framing::MessageTransferBody>() &&
+ frameset.as<framing::MessageTransferBody>()->getDestination() == dest;
+}
+
+ScopedDivert::ScopedDivert(const std::string& _dest, Demux& _demuxer) : dest(_dest), demuxer(_demuxer)
+{
+ queue = demuxer.add(dest, ByTransferDest(dest));
+}
+
+ScopedDivert::~ScopedDivert()
+{
+ demuxer.remove(dest);
+}
+
+Demux::Demux() : defaultQueue(new Queue()) {}
+
+Demux::~Demux() { close(sys::ExceptionHolder(new ClosedException())); }
+
+Demux::QueuePtr ScopedDivert::getQueue()
+{
+ return queue;
+}
+
+void Demux::handle(framing::FrameSet::shared_ptr frameset)
+{
+ sys::Mutex::ScopedLock l(lock);
+ bool matched = false;
+ for (iterator i = records.begin(); i != records.end() && !matched; i++) {
+ if (i->condition && i->condition(*frameset)) {
+ matched = true;
+ i->queue->push(frameset);
+ }
+ }
+ if (!matched) {
+ defaultQueue->push(frameset);
+ }
+}
+
+void Demux::close(const sys::ExceptionHolder& ex)
+{
+ sys::Mutex::ScopedLock l(lock);
+ for (iterator i = records.begin(); i != records.end(); i++) {
+ i->queue->close(ex);
+ }
+ defaultQueue->close(ex);
+}
+
+void Demux::open()
+{
+ sys::Mutex::ScopedLock l(lock);
+ for (iterator i = records.begin(); i != records.end(); i++) {
+ i->queue->open();
+ }
+ defaultQueue->open();
+}
+
+Demux::QueuePtr Demux::add(const std::string& name, Condition condition)
+{
+ sys::Mutex::ScopedLock l(lock);
+ iterator i = std::find_if(records.begin(), records.end(), Find(name));
+ if (i == records.end()) {
+ Record r(name, condition);
+ records.push_back(r);
+ return r.queue;
+ } else {
+ throw Exception("Queue already exists for " + name);
+ }
+}
+
+void Demux::remove(const std::string& name)
+{
+ sys::Mutex::ScopedLock l(lock);
+ records.remove_if(Find(name));
+}
+
+Demux::QueuePtr Demux::get(const std::string& name)
+{
+ sys::Mutex::ScopedLock l(lock);
+ iterator i = std::find_if(records.begin(), records.end(), Find(name));
+ if (i == records.end()) {
+ throw Exception("No queue for " + name);
+ }
+ return i->queue;
+}
+
+Demux::QueuePtr Demux::getDefault()
+{
+ return defaultQueue;
+}
+
+Demux::Find::Find(const std::string& n) : name(n) {}
+
+bool Demux::Find::operator()(const Record& record) const
+{
+ return record.name == name;
+}
+
+}}
+
diff --git a/qpid/cpp/src/qpid/client/Demux.h b/qpid/cpp/src/qpid/client/Demux.h
new file mode 100644
index 0000000000..31dc3f9c06
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Demux.h
@@ -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.
+ *
+ */
+
+#include <list>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include "qpid/framing/FrameSet.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/ClientImportExport.h"
+
+#ifndef _Demux_
+#define _Demux_
+
+namespace qpid {
+namespace client {
+
+///@internal
+class ByTransferDest
+{
+ const std::string dest;
+public:
+ ByTransferDest(const std::string& dest);
+ bool operator()(const framing::FrameSet& frameset) const;
+};
+
+///@internal
+class Demux
+{
+public:
+ typedef boost::function<bool(const framing::FrameSet&)> Condition;
+ typedef sys::BlockingQueue<framing::FrameSet::shared_ptr> Queue;
+ typedef boost::shared_ptr<Queue> QueuePtr;
+
+ QPID_CLIENT_EXTERN Demux();
+ QPID_CLIENT_EXTERN ~Demux();
+
+ QPID_CLIENT_EXTERN void handle(framing::FrameSet::shared_ptr);
+ QPID_CLIENT_EXTERN void close(const sys::ExceptionHolder& ex);
+ QPID_CLIENT_EXTERN void open();
+
+ QPID_CLIENT_EXTERN QueuePtr add(const std::string& name, Condition);
+ QPID_CLIENT_EXTERN void remove(const std::string& name);
+ QPID_CLIENT_EXTERN QueuePtr get(const std::string& name);
+ QPID_CLIENT_EXTERN QueuePtr getDefault();
+
+private:
+ struct Record
+ {
+ const std::string name;
+ Condition condition;
+ QueuePtr queue;
+
+ Record(const std::string& n, Condition c) : name(n), condition(c), queue(new Queue()) {}
+ };
+
+ sys::Mutex lock;
+ std::list<Record> records;
+ QueuePtr defaultQueue;
+
+ typedef std::list<Record>::iterator iterator;
+
+ struct Find
+ {
+ const std::string name;
+ Find(const std::string& name);
+ bool operator()(const Record& record) const;
+ };
+};
+
+class ScopedDivert
+{
+ const std::string dest;
+ Demux& demuxer;
+ Demux::QueuePtr queue;
+public:
+ ScopedDivert(const std::string& dest, Demux& demuxer);
+ ~ScopedDivert();
+ Demux::QueuePtr getQueue();
+};
+
+}} // namespace qpid::client
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/Dispatcher.cpp b/qpid/cpp/src/qpid/client/Dispatcher.cpp
new file mode 100644
index 0000000000..a715c623bf
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Dispatcher.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/Dispatcher.h"
+#include "qpid/client/SubscriptionImpl.h"
+#include "qpid/client/SessionImpl.h"
+
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/MessageImpl.h"
+
+#include <boost/version.hpp>
+#if (BOOST_VERSION >= 104000)
+# include <boost/serialization/state_saver.hpp>
+ using boost::serialization::state_saver;
+#else
+# include <boost/state_saver.hpp>
+ using boost::state_saver;
+#endif /* BOOST_VERSION */
+
+using qpid::framing::FrameSet;
+using qpid::framing::MessageTransferBody;
+using qpid::sys::Mutex;
+using qpid::sys::ScopedLock;
+using qpid::sys::Thread;
+
+namespace qpid {
+namespace client {
+
+Dispatcher::Dispatcher(const Session& s, const std::string& q)
+ : session(s),
+ running(false),
+ autoStop(true),
+ failoverHandler(0)
+{
+ Demux& demux = SessionBase_0_10Access(session).get()->getDemux();
+ queue = q.empty() ? demux.getDefault() : demux.get(q);
+}
+
+void Dispatcher::start()
+{
+ worker = Thread(this);
+}
+
+void Dispatcher::wait()
+{
+ worker.join();
+}
+
+void Dispatcher::run()
+{
+ Mutex::ScopedLock l(lock);
+ if (running)
+ throw Exception("Dispatcher is already running.");
+ state_saver<bool> reset(running); // Reset to false on exit.
+ running = true;
+ try {
+ while (!queue->isClosed()) {
+ Mutex::ScopedUnlock u(lock);
+ FrameSet::shared_ptr content = queue->pop();
+ if (content->isA<MessageTransferBody>()) {
+ Message msg(new MessageImpl(*content));
+ boost::intrusive_ptr<SubscriptionImpl> listener = find(msg.getDestination());
+ if (!listener) {
+ QPID_LOG(error, "No listener found for destination " << msg.getDestination());
+ } else {
+ assert(listener);
+ listener->received(msg);
+ }
+ } else {
+ if (handler.get()) {
+ handler->handle(*content);
+ } else {
+ QPID_LOG(warning, "No handler found for " << *(content->getMethod()));
+ }
+ }
+ }
+ session.sync(); // Make sure all our acks are received before returning.
+ }
+ catch (const ClosedException&) {
+ QPID_LOG(debug, QPID_MSG(session.getId() << ": closed by peer"));
+ }
+ catch (const TransportFailure&) {
+ QPID_LOG(info, QPID_MSG(session.getId() << ": transport failure"));
+ throw;
+ }
+ catch (const std::exception& e) {
+ if ( failoverHandler ) {
+ QPID_LOG(debug, QPID_MSG(session.getId() << " failover: " << e.what()));
+ failoverHandler();
+ } else {
+ QPID_LOG(error, session.getId() << " error: " << e.what());
+ throw;
+ }
+ }
+}
+
+void Dispatcher::stop()
+{
+ ScopedLock<Mutex> l(lock);
+ queue->close(); // Will interrupt thread blocked in pop()
+}
+
+void Dispatcher::setAutoStop(bool b)
+{
+ ScopedLock<Mutex> l(lock);
+ autoStop = b;
+}
+
+boost::intrusive_ptr<SubscriptionImpl> Dispatcher::find(const std::string& name)
+{
+ ScopedLock<Mutex> l(lock);
+ Listeners::iterator i = listeners.find(name);
+ if (i == listeners.end()) {
+ return defaultListener;
+ }
+ return i->second;
+}
+
+void Dispatcher::listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription) {
+ ScopedLock<Mutex> l(lock);
+ listeners[subscription->getName()] = subscription;
+}
+
+void Dispatcher::cancel(const std::string& destination) {
+ ScopedLock<Mutex> l(lock);
+ if (listeners.erase(destination) && running && autoStop && listeners.empty())
+ queue->close();
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/client/Dispatcher.h b/qpid/cpp/src/qpid/client/Dispatcher.h
new file mode 100644
index 0000000000..74fdb90103
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Dispatcher.h
@@ -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.
+ *
+ */
+#ifndef _Dispatcher_
+#define _Dispatcher_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "qpid/client/Session.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/client/ClientImportExport.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/SubscriptionImpl.h"
+
+namespace qpid {
+namespace client {
+
+class SubscriptionImpl;
+
+///@internal
+typedef framing::Handler<framing::FrameSet> FrameSetHandler;
+
+///@internal
+class Dispatcher : public sys::Runnable
+{
+ typedef std::map<std::string, boost::intrusive_ptr<SubscriptionImpl> >Listeners;
+ sys::Mutex lock;
+ sys::Thread worker;
+ Session session;
+ Demux::QueuePtr queue;
+ bool running;
+ bool autoStop;
+ Listeners listeners;
+ boost::intrusive_ptr<SubscriptionImpl> defaultListener;
+ std::auto_ptr<FrameSetHandler> handler;
+
+ boost::intrusive_ptr<SubscriptionImpl> find(const std::string& name);
+ bool isStopped();
+
+ boost::function<void ()> failoverHandler;
+
+public:
+ Dispatcher(const Session& session, const std::string& queue = "");
+ ~Dispatcher() {}
+
+ void start();
+ void wait();
+ // As this class is marked 'internal', no extern should be made here;
+ // however, some test programs rely on it.
+ QPID_CLIENT_EXTERN void run();
+ void stop();
+ void setAutoStop(bool b);
+
+ void registerFailoverHandler ( boost::function<void ()> fh )
+ {
+ failoverHandler = fh;
+ }
+
+ void listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription);
+ void cancel(const std::string& destination);
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/Execution.h b/qpid/cpp/src/qpid/client/Execution.h
new file mode 100644
index 0000000000..ad622af9c1
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Execution.h
@@ -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.
+ *
+ */
+#ifndef _Execution_
+#define _Execution_
+
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/client/Demux.h"
+
+namespace qpid {
+namespace client {
+
+/**@internal
+ *
+ * Provides access to more detailed aspects of the session
+ * implementation.
+ */
+class Execution
+{
+public:
+ virtual ~Execution() {}
+ /**
+ * Provides access to the demultiplexing function within the
+ * session implementation
+ */
+ virtual Demux& getDemux() = 0;
+ /**
+ * Wait until notification has been received of completion of the
+ * outgoing command with the specified id.
+ */
+ void waitForCompletion(const framing::SequenceNumber& id);
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/FailoverListener.cpp b/qpid/cpp/src/qpid/client/FailoverListener.cpp
new file mode 100644
index 0000000000..bf4fa91d49
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/FailoverListener.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/FailoverListener.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include "qpid/log/Helpers.h"
+
+namespace qpid {
+namespace client {
+
+const std::string FailoverListener::AMQ_FAILOVER("amq.failover");
+
+FailoverListener::FailoverListener(Connection c) :
+ connection(c),
+ session(c.newSession(AMQ_FAILOVER+"."+framing::Uuid(true).str())),
+ subscriptions(session)
+{ init(true); }
+
+FailoverListener::FailoverListener(Connection c, bool useInitial) :
+ connection(c),
+ session(c.newSession(AMQ_FAILOVER+"."+framing::Uuid(true).str())),
+ subscriptions(session)
+{ init(useInitial); }
+
+void FailoverListener::init(bool useInitial) {
+ if (useInitial) knownBrokers = connection.getInitialBrokers();
+ if (session.exchangeQuery(arg::name=AMQ_FAILOVER).getNotFound()) {
+ session.close();
+ return;
+ }
+ std::string qname=session.getId().getName();
+ session.queueDeclare(arg::queue=qname, arg::exclusive=true, arg::autoDelete=true);
+ session.exchangeBind(arg::queue=qname, arg::exchange=AMQ_FAILOVER);
+ subscriptions.subscribe(*this, qname, SubscriptionSettings(FlowControl::unlimited(),
+ ACCEPT_MODE_NONE));
+ thread = sys::Thread(*this);
+}
+
+void FailoverListener::run() {
+ try {
+ subscriptions.run();
+ } catch(...) {}
+}
+
+FailoverListener::~FailoverListener() {
+ try {
+ subscriptions.stop();
+ thread.join();
+ if (connection.isOpen()) {
+ session.sync();
+ session.close();
+ }
+ } catch (...) {}
+}
+
+void FailoverListener::received(Message& msg) {
+ sys::Mutex::ScopedLock l(lock);
+ knownBrokers = getKnownBrokers(msg);
+}
+
+std::vector<Url> FailoverListener::getKnownBrokers() const {
+ sys::Mutex::ScopedLock l(lock);
+ return knownBrokers;
+}
+
+std::vector<Url> FailoverListener::getKnownBrokers(const Message& msg) {
+ std::vector<Url> knownBrokers;
+ framing::Array urlArray;
+ msg.getHeaders().getArray("amq.failover", urlArray);
+ for (framing::Array::ValueVector::const_iterator i = urlArray.begin();
+ i != urlArray.end();
+ ++i )
+ knownBrokers.push_back(Url((*i)->get<std::string>()));
+ return knownBrokers;
+}
+
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/FailoverManager.cpp b/qpid/cpp/src/qpid/client/FailoverManager.cpp
new file mode 100644
index 0000000000..9405765b47
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/FailoverManager.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/FailoverManager.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+
+
+namespace qpid {
+namespace client {
+
+using qpid::sys::Monitor;
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+
+FailoverManager::FailoverManager(const ConnectionSettings& s,
+ ReconnectionStrategy* rs) : settings(s), strategy(rs), state(IDLE) {}
+
+void FailoverManager::execute(Command& c)
+{
+ bool retry = false;
+ bool completed = false;
+ AbsTime failed;
+ while (!completed) {
+ try {
+ AsyncSession session = connect().newSession();
+ if (retry) {
+ Duration failoverTime(failed, AbsTime::now());
+ QPID_LOG(info, "Failed over for " << &c << " in " << (failoverTime/qpid::sys::TIME_MSEC) << " milliseconds");
+ }
+ c.execute(session, retry);
+ session.sync();//TODO: shouldn't be required
+ session.close();
+ completed = true;
+ } catch(const TransportFailure&) {
+ retry = true;
+ failed = AbsTime::now();
+ }
+ }
+}
+
+void FailoverManager::close()
+{
+ Monitor::ScopedLock l(lock);
+ connection.close();
+}
+
+Connection& FailoverManager::connect(std::vector<Url> brokers)
+{
+ Monitor::ScopedLock l(lock);
+ if (state == CANT_CONNECT) {
+ state = IDLE;//retry
+ }
+ while (!connection.isOpen()) {
+ if (state == CONNECTING) {
+ lock.wait();
+ } else if (state == CANT_CONNECT) {
+ throw CannotConnectException("Cannot establish a connection");
+ } else {
+ state = CONNECTING;
+ Connection c;
+ if (brokers.empty() && failoverListener.get())
+ brokers = failoverListener->getKnownBrokers();
+ attempt(c, settings, brokers);
+ if (c.isOpen()) state = IDLE;
+ else state = CANT_CONNECT;
+ connection = c;
+ lock.notifyAll();
+ }
+ }
+ return connection;
+}
+
+Connection& FailoverManager::getConnection()
+{
+ Monitor::ScopedLock l(lock);
+ return connection;
+}
+
+void FailoverManager::attempt(Connection& c, ConnectionSettings s, std::vector<Url> urls)
+{
+ Monitor::ScopedUnlock u(lock);
+ if (strategy) strategy->editUrlList(urls);
+ if (urls.empty()) {
+ attempt(c, s);
+ } else {
+ for (std::vector<Url>::const_iterator i = urls.begin(); i != urls.end() && !c.isOpen(); ++i) {
+ for (Url::const_iterator j = i->begin(); j != i->end() && !c.isOpen(); ++j) {
+ const Address& addr = *j;
+ s.protocol = addr.protocol;
+ s.host = addr.host;
+ s.port = addr.port;
+ attempt(c, s);
+ }
+ }
+ }
+}
+
+void FailoverManager::attempt(Connection& c, ConnectionSettings s)
+{
+ try {
+ QPID_LOG(info, "Attempting to connect to " << s.host << " on " << s.port << "...");
+ c.open(s);
+ failoverListener.reset(new FailoverListener(c));
+ QPID_LOG(info, "Connected to " << s.host << " on " << s.port);
+ } catch (const Exception& e) {
+ QPID_LOG(info, "Could not connect to " << s.host << " on " << s.port << ": " << e.what());
+ }
+}
+
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/Future.cpp b/qpid/cpp/src/qpid/client/Future.cpp
new file mode 100644
index 0000000000..740cd3df59
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Future.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Future.h"
+#include "qpid/client/SessionImpl.h"
+
+namespace qpid {
+namespace client {
+
+void Future::wait(SessionImpl& session)
+{
+ if (!complete) {
+ session.waitForCompletion(command);
+ }
+ complete = true;
+}
+
+bool Future::isComplete(SessionImpl& session)
+{
+ return complete || session.isComplete(command);
+}
+
+void Future::setFutureResult(boost::shared_ptr<FutureResult> r)
+{
+ result = r;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/client/FutureCompletion.cpp b/qpid/cpp/src/qpid/client/FutureCompletion.cpp
new file mode 100644
index 0000000000..ccfb073855
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/FutureCompletion.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/FutureCompletion.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+FutureCompletion::FutureCompletion() : complete(false) {}
+
+bool FutureCompletion::isComplete() const
+{
+ Monitor::ScopedLock l(lock);
+ return complete;
+}
+
+void FutureCompletion::completed()
+{
+ Monitor::ScopedLock l(lock);
+ complete = true;
+ lock.notifyAll();
+}
+
+void FutureCompletion::waitForCompletion() const
+{
+ Monitor::ScopedLock l(lock);
+ while (!complete) {
+ lock.wait();
+ }
+}
diff --git a/qpid/cpp/src/qpid/client/FutureResult.cpp b/qpid/cpp/src/qpid/client/FutureResult.cpp
new file mode 100644
index 0000000000..0237eb1464
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/FutureResult.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/FutureResult.h"
+
+#include "qpid/client/SessionImpl.h"
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+const std::string& FutureResult::getResult(SessionImpl& session) const
+{
+ waitForCompletion();
+ session.assertOpen();
+ return result;
+}
+
+void FutureResult::received(const std::string& r)
+{
+ Monitor::ScopedLock l(lock);
+ result = r;
+ complete = true;
+ lock.notifyAll();
+}
diff --git a/qpid/cpp/src/qpid/client/LoadPlugins.cpp b/qpid/cpp/src/qpid/client/LoadPlugins.cpp
new file mode 100644
index 0000000000..246eb60c67
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/LoadPlugins.cpp
@@ -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.
+ *
+ */
+
+#include "LoadPlugins.h"
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "qpid/Modules.h"
+#include "qpid/sys/Shlib.h"
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+
+namespace qpid {
+namespace client {
+
+namespace {
+
+struct LoadtimeInitialise {
+ LoadtimeInitialise() {
+ qpid::ModuleOptions moduleOptions(QPIDC_MODULE_DIR);
+ string defaultPath (moduleOptions.loadDir);
+ moduleOptions.parse (0, 0, QPIDC_CONF_FILE, true);
+
+ for (vector<string>::iterator iter = moduleOptions.load.begin();
+ iter != moduleOptions.load.end();
+ iter++)
+ qpid::tryShlib (iter->data(), false);
+
+ if (!moduleOptions.noLoad) {
+ bool isDefault = defaultPath == moduleOptions.loadDir;
+ qpid::loadModuleDir (moduleOptions.loadDir, isDefault);
+ }
+ }
+};
+
+} // namespace
+
+void theModuleLoader() {
+ static LoadtimeInitialise l;
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/LoadPlugins.h b/qpid/cpp/src/qpid/client/LoadPlugins.h
new file mode 100644
index 0000000000..0be4ae9f0c
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/LoadPlugins.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef _LoadPlugins_
+#define _LoadPlugins_
+
+namespace qpid {
+namespace client {
+
+void theModuleLoader();
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/LocalQueue.cpp b/qpid/cpp/src/qpid/client/LocalQueue.cpp
new file mode 100644
index 0000000000..0019adabaf
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/LocalQueue.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/LocalQueue.h"
+#include "qpid/client/LocalQueueImpl.h"
+#include "qpid/client/MessageImpl.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/client/SubscriptionImpl.h"
+
+namespace qpid {
+namespace client {
+
+using namespace framing;
+
+typedef PrivateImplRef<LocalQueue> PI;
+
+LocalQueue::LocalQueue() { PI::ctor(*this, new LocalQueueImpl()); }
+LocalQueue::LocalQueue(const LocalQueue& x) : Handle<LocalQueueImpl>() { PI::copy(*this, x); }
+LocalQueue::~LocalQueue() { PI::dtor(*this); }
+LocalQueue& LocalQueue::operator=(const LocalQueue& x) { return PI::assign(*this, x); }
+
+Message LocalQueue::pop(sys::Duration timeout) { return impl->pop(timeout); }
+
+Message LocalQueue::get(sys::Duration timeout) { return impl->get(timeout); }
+
+bool LocalQueue::get(Message& result, sys::Duration timeout) { return impl->get(result, timeout); }
+
+bool LocalQueue::empty() const { return impl->empty(); }
+size_t LocalQueue::size() const { return impl->size(); }
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/LocalQueueImpl.cpp b/qpid/cpp/src/qpid/client/LocalQueueImpl.cpp
new file mode 100644
index 0000000000..8b191728f4
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/LocalQueueImpl.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/LocalQueueImpl.h"
+#include "qpid/client/MessageImpl.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/client/SubscriptionImpl.h"
+#include "qpid/client/CompletionImpl.h"
+
+namespace qpid {
+namespace client {
+
+using namespace framing;
+
+Message LocalQueueImpl::pop(sys::Duration timeout) { return get(timeout); }
+
+Message LocalQueueImpl::get(sys::Duration timeout) {
+ Message result;
+ bool ok = get(result, timeout);
+ if (!ok) throw Exception("Timed out waiting for a message");
+ return result;
+}
+
+bool LocalQueueImpl::get(Message& result, sys::Duration timeout) {
+ if (!queue)
+ throw ClosedException();
+ FrameSet::shared_ptr content;
+ bool ok = queue->pop(content, timeout);
+ if (!ok) return false;
+ if (content->isA<MessageTransferBody>()) {
+
+ *MessageImpl::get(result) = MessageImpl(*content);
+ boost::intrusive_ptr<SubscriptionImpl> si = PrivateImplRef<Subscription>::get(subscription);
+ assert(si);
+ if (si) si->received(result);
+ return true;
+ }
+ else
+ throw CommandInvalidException(
+ QPID_MSG("Unexpected method: " << content->getMethod()));
+}
+
+bool LocalQueueImpl::empty() const
+{
+ if (!queue)
+ throw ClosedException();
+ return queue->empty();
+}
+
+size_t LocalQueueImpl::size() const
+{
+ if (!queue)
+ throw ClosedException();
+ return queue->size();
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/LocalQueueImpl.h b/qpid/cpp/src/qpid/client/LocalQueueImpl.h
new file mode 100644
index 0000000000..75b62cf203
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/LocalQueueImpl.h
@@ -0,0 +1,108 @@
+#ifndef QPID_CLIENT_LOCALQUEUEIMPL_H
+#define QPID_CLIENT_LOCALQUEUEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/ClientImportExport.h"
+#include "qpid/client/Handle.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/Subscription.h"
+#include "qpid/client/Demux.h"
+#include "qpid/sys/Time.h"
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace client {
+
+/**
+ * A local queue to collect messages retrieved from a remote broker
+ * queue. Create a queue and subscribe it using the SubscriptionManager.
+ * Messages from the remote queue on the broker will be stored in the
+ * local queue until you retrieve them.
+ *
+ * \ingroup clientapi
+ *
+ * \details Using a Local Queue
+ *
+ * <pre>
+ * LocalQueue local_queue;
+ * subscriptions.subscribe(local_queue, string("message_queue"));
+ * for (int i=0; i&lt;10; i++) {
+ * Message message = local_queue.get();
+ * std::cout &lt;&lt; message.getData() &lt;&lt; std::endl;
+ * }
+ * </pre>
+ *
+ * <h2>Getting Messages</h2>
+ *
+ * <ul><li>
+ * <p>get()</p>
+ * <pre>Message message = local_queue.get();</pre>
+ * <pre>// Specifying timeouts (TIME_SEC, TIME_MSEC, TIME_USEC, TIME_NSEC)
+ *#include <qpid/sys/Time.h>
+ *Message message;
+ *local_queue.get(message, 5*sys::TIME_SEC);</pre></li></ul>
+ *
+ * <h2>Checking size</h2>
+ * <ul><li>
+ * <p>empty()</p>
+ * <pre>if (local_queue.empty()) { ... }</pre></li>
+ * <li><p>size()</p>
+ * <pre>std::cout &lt;&lt; local_queue.size();</pre></li>
+ * </ul>
+ */
+
+class LocalQueueImpl : public RefCounted {
+ public:
+ /** Wait up to timeout for the next message from the local queue.
+ *@param result Set to the message from the queue.
+ *@param timeout wait up this timeout for a message to appear.
+ *@return true if result was set, false if queue was empty after timeout.
+ */
+ bool get(Message& result, sys::Duration timeout=0);
+
+ /** Get the next message off the local queue, or wait up to the timeout
+ * for message from the broker queue.
+ *@param timeout wait up this timeout for a message to appear.
+ *@return message from the queue.
+ *@throw ClosedException if subscription is closed or timeout exceeded.
+ */
+ Message get(sys::Duration timeout=sys::TIME_INFINITE);
+
+ /** Synonym for get() */
+ Message pop(sys::Duration timeout=sys::TIME_INFINITE);
+
+ /** Return true if local queue is empty. */
+ bool empty() const;
+
+ /** Number of messages on the local queue */
+ size_t size() const;
+
+ private:
+ Demux::QueuePtr queue;
+ Subscription subscription;
+ friend class SubscriptionManagerImpl;
+};
+
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_LOCALQUEUEIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/Message.cpp b/qpid/cpp/src/qpid/client/Message.cpp
new file mode 100644
index 0000000000..00f911c57e
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Message.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Message.h"
+#include "qpid/client/MessageImpl.h"
+
+namespace qpid {
+namespace client {
+
+Message::Message(MessageImpl* mi) : impl(mi) {}
+
+Message::Message(const std::string& data, const std::string& routingKey)
+ : impl(new MessageImpl(data, routingKey)) {}
+
+Message::Message(const Message& m) : impl(new MessageImpl(*m.impl)) {}
+
+Message::~Message() { delete impl; }
+
+Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; }
+
+void Message::swap(Message& m) { std::swap(impl, m.impl); }
+
+std::string Message::getDestination() const { return impl->getDestination(); }
+bool Message::isRedelivered() const { return impl->isRedelivered(); }
+void Message::setRedelivered(bool redelivered) { impl->setRedelivered(redelivered); }
+framing::FieldTable& Message::getHeaders() { return impl->getHeaders(); }
+const framing::FieldTable& Message::getHeaders() const { return impl->getHeaders(); }
+const framing::SequenceNumber& Message::getId() const { return impl->getId(); }
+
+void Message::setData(const std::string& s) { impl->setData(s); }
+const std::string& Message::getData() const { return impl->getData(); }
+std::string& Message::getData() { return impl->getData(); }
+
+void Message::appendData(const std::string& s) { impl->appendData(s); }
+
+bool Message::hasMessageProperties() const { return impl->hasMessageProperties(); }
+framing::MessageProperties& Message::getMessageProperties() { return impl->getMessageProperties(); }
+const framing::MessageProperties& Message::getMessageProperties() const { return impl->getMessageProperties(); }
+
+bool Message::hasDeliveryProperties() const { return impl->hasDeliveryProperties(); }
+framing::DeliveryProperties& Message::getDeliveryProperties() { return impl->getDeliveryProperties(); }
+const framing::DeliveryProperties& Message::getDeliveryProperties() const { return impl->getDeliveryProperties(); }
+
+}}
diff --git a/qpid/cpp/src/qpid/client/MessageImpl.cpp b/qpid/cpp/src/qpid/client/MessageImpl.cpp
new file mode 100644
index 0000000000..865c462b15
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/MessageImpl.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/MessageImpl.h"
+
+namespace qpid {
+namespace client {
+
+MessageImpl::MessageImpl(const std::string& data, const std::string& routingKey) : TransferContent(data, routingKey) {}
+
+std::string MessageImpl::getDestination() const
+{
+ return method.getDestination();
+}
+
+bool MessageImpl::isRedelivered() const
+{
+ return hasDeliveryProperties() && getDeliveryProperties().getRedelivered();
+}
+
+void MessageImpl::setRedelivered(bool redelivered)
+{
+ getDeliveryProperties().setRedelivered(redelivered);
+}
+
+framing::FieldTable& MessageImpl::getHeaders()
+{
+ return getMessageProperties().getApplicationHeaders();
+}
+
+const framing::FieldTable& MessageImpl::getHeaders() const
+{
+ return getMessageProperties().getApplicationHeaders();
+}
+
+const framing::MessageTransferBody& MessageImpl::getMethod() const
+{
+ return method;
+}
+
+const framing::SequenceNumber& MessageImpl::getId() const
+{
+ return id;
+}
+
+/**@internal for incoming messages */
+MessageImpl::MessageImpl(const framing::FrameSet& frameset) :
+ method(*frameset.as<framing::MessageTransferBody>()), id(frameset.getId())
+{
+ populate(frameset);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/client/MessageImpl.h b/qpid/cpp/src/qpid/client/MessageImpl.h
new file mode 100644
index 0000000000..a64ddd20d8
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/MessageImpl.h
@@ -0,0 +1,80 @@
+#ifndef QPID_CLIENT_MESSAGEIMPL_H
+#define QPID_CLIENT_MESSAGEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Message.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/TransferContent.h"
+#include <string>
+
+namespace qpid {
+namespace client {
+
+class MessageImpl : public framing::TransferContent
+{
+public:
+ /** Create a Message.
+ *@param data Data for the message body.
+ *@param routingKey Passed to the exchange that routes the message.
+ */
+ MessageImpl(const std::string& data=std::string(),
+ const std::string& routingKey=std::string());
+
+ /** The destination of messages sent to the broker is the exchange
+ * name. The destination of messages received from the broker is
+ * the delivery tag identifyig the local subscription (often this
+ * is the name of the subscribed queue.)
+ */
+ std::string getDestination() const;
+
+ /** Check the redelivered flag. */
+ bool isRedelivered() const;
+ /** Set the redelivered flag. */
+ void setRedelivered(bool redelivered);
+
+ /** Get a modifyable reference to the message headers. */
+ framing::FieldTable& getHeaders();
+
+ /** Get a non-modifyable reference to the message headers. */
+ const framing::FieldTable& getHeaders() const;
+
+ ///@internal
+ const framing::MessageTransferBody& getMethod() const;
+ ///@internal
+ const framing::SequenceNumber& getId() const;
+
+ /**@internal for incoming messages */
+ MessageImpl(const framing::FrameSet& frameset);
+
+ static MessageImpl* get(Message& m) { return m.impl; }
+ static const MessageImpl* get(const Message& m) { return m.impl; }
+
+private:
+ //method and id are only set for received messages:
+ framing::MessageTransferBody method;
+ framing::SequenceNumber id;
+};
+
+}}
+
+#endif /*!QPID_CLIENT_MESSAGEIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/MessageListener.cpp b/qpid/cpp/src/qpid/client/MessageListener.cpp
new file mode 100644
index 0000000000..0f2a71287c
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/MessageListener.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/MessageListener.h"
+
+qpid::client::MessageListener::~MessageListener() {}
diff --git a/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp b/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp
new file mode 100644
index 0000000000..3afaae74e8
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/MessageReplayTracker.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/MessageReplayTracker.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace client {
+
+MessageReplayTracker::MessageReplayTracker(uint f) : flushInterval(f), count(0) {}
+
+void MessageReplayTracker::send(const Message& message, const std::string& destination)
+{
+ buffer.push_back(ReplayRecord(message, destination));
+ buffer.back().send(*this);
+ if (flushInterval && (++count % flushInterval == 0)) {
+ checkCompletion();
+ if (!buffer.empty()) session.flush();
+ }
+}
+void MessageReplayTracker::init(AsyncSession s)
+{
+ session = s;
+}
+
+void MessageReplayTracker::replay(AsyncSession s)
+{
+ session = s;
+ std::for_each(buffer.begin(), buffer.end(), boost::bind(&ReplayRecord::send, _1, boost::ref(*this)));
+ session.flush();
+ count = 0;
+}
+
+void MessageReplayTracker::setFlushInterval(uint f)
+{
+ flushInterval = f;
+}
+
+uint MessageReplayTracker::getFlushInterval()
+{
+ return flushInterval;
+}
+
+void MessageReplayTracker::checkCompletion()
+{
+ buffer.remove_if(boost::bind(&ReplayRecord::isComplete, _1));
+}
+
+MessageReplayTracker::ReplayRecord::ReplayRecord(const Message& m, const std::string& d) : message(m), destination(d) {}
+
+void MessageReplayTracker::ReplayRecord::send(MessageReplayTracker& tracker)
+{
+ status = tracker.session.messageTransfer(arg::destination=destination, arg::content=message);
+}
+
+bool MessageReplayTracker::ReplayRecord::isComplete()
+{
+ return status.isComplete();
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/PrivateImplRef.h b/qpid/cpp/src/qpid/client/PrivateImplRef.h
new file mode 100644
index 0000000000..503a383c31
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/PrivateImplRef.h
@@ -0,0 +1,94 @@
+#ifndef QPID_CLIENT_PRIVATEIMPL_H
+#define QPID_CLIENT_PRIVATEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/ClientImportExport.h"
+#include <boost/intrusive_ptr.hpp>
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace client {
+
+// FIXME aconway 2009-04-24: details!
+/** @file
+ *
+ * Helper class to implement a class with a private, reference counted
+ * implementation and reference semantics.
+ *
+ * Such classes are used in the public API to hide implementation, they
+ * should. Example of use:
+ *
+ * === Foo.h
+ *
+ * template <class T> PrivateImplRef;
+ * class FooImpl;
+ *
+ * Foo : public Handle<FooImpl> {
+ * public:
+ * Foo(FooImpl* = 0);
+ * Foo(const Foo&);
+ * ~Foo();
+ * Foo& operator=(const Foo&);
+ *
+ * int fooDo(); // and other Foo functions...
+ *
+ * private:
+ * typedef FooImpl Impl;
+ * Impl* impl;
+ * friend class PrivateImplRef<Foo>;
+ *
+ * === Foo.cpp
+ *
+ * typedef PrivateImplRef<Foo> PI;
+ * Foo::Foo(FooImpl* p) { PI::ctor(*this, p); }
+ * Foo::Foo(const Foo& c) : Handle<FooImpl>() { PI::copy(*this, c); }
+ * Foo::~Foo() { PI::dtor(*this); }
+ * Foo& Foo::operator=(const Foo& c) { return PI::assign(*this, c); }
+ *
+ * int foo::fooDo() { return impl->fooDo(); }
+ *
+ */
+template <class T> class PrivateImplRef {
+ public:
+ typedef typename T::Impl Impl;
+ typedef boost::intrusive_ptr<Impl> intrusive_ptr;
+
+ static intrusive_ptr get(const T& t) { return intrusive_ptr(t.impl); }
+
+ static void set(T& t, const intrusive_ptr& p) {
+ if (t.impl == p) return;
+ if (t.impl) boost::intrusive_ptr_release(t.impl);
+ t.impl = p.get();
+ if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ }
+
+ // Helper functions to implement the ctor, dtor, copy, assign
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
+ static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
+};
+
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_PRIVATEIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/QueueOptions.cpp b/qpid/cpp/src/qpid/client/QueueOptions.cpp
new file mode 100644
index 0000000000..f4c1483859
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/QueueOptions.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/QueueOptions.h"
+
+namespace qpid {
+namespace client {
+
+enum QueueEventGeneration {ENQUEUE_ONLY=1, ENQUEUE_AND_DEQUEUE=2};
+
+
+QueueOptions::QueueOptions()
+{}
+
+const std::string QueueOptions::strMaxCountKey("qpid.max_count");
+const std::string QueueOptions::strMaxSizeKey("qpid.max_size");
+const std::string QueueOptions::strTypeKey("qpid.policy_type");
+const std::string QueueOptions::strREJECT("reject");
+const std::string QueueOptions::strFLOW_TO_DISK("flow_to_disk");
+const std::string QueueOptions::strRING("ring");
+const std::string QueueOptions::strRING_STRICT("ring_strict");
+const std::string QueueOptions::strLastValueQueue("qpid.last_value_queue");
+const std::string QueueOptions::strPersistLastNode("qpid.persist_last_node");
+const std::string QueueOptions::strLVQMatchProperty("qpid.LVQ_key");
+const std::string QueueOptions::strLastValueQueueNoBrowse("qpid.last_value_queue_no_browse");
+const std::string QueueOptions::strQueueEventMode("qpid.queue_event_generation");
+
+
+QueueOptions::~QueueOptions()
+{}
+
+void QueueOptions::setSizePolicy(QueueSizePolicy sp, uint64_t maxSize, uint32_t maxCount)
+{
+ if (maxCount) setInt(strMaxCountKey, maxCount);
+ if (maxSize) setInt(strMaxSizeKey, maxSize);
+ if (maxSize || maxCount){
+ switch (sp)
+ {
+ case REJECT:
+ setString(strTypeKey, strREJECT);
+ break;
+ case FLOW_TO_DISK:
+ setString(strTypeKey, strFLOW_TO_DISK);
+ break;
+ case RING:
+ setString(strTypeKey, strRING);
+ break;
+ case RING_STRICT:
+ setString(strTypeKey, strRING_STRICT);
+ break;
+ case NONE:
+ clearSizePolicy();
+ break;
+ }
+ }
+}
+
+
+void QueueOptions::setPersistLastNode()
+{
+ setInt(strPersistLastNode, 1);
+}
+
+void QueueOptions::setOrdering(QueueOrderingPolicy op)
+{
+ if (op == LVQ){
+ setInt(strLastValueQueue, 1);
+ }else if (op == LVQ_NO_BROWSE){
+ setInt(strLastValueQueueNoBrowse, 1);
+ }else {
+ clearOrdering();
+ }
+}
+
+void QueueOptions::getLVQKey(std::string& key)
+{
+ key.assign(strLVQMatchProperty);
+}
+
+void QueueOptions::clearSizePolicy()
+{
+ erase(strMaxCountKey);
+ erase(strMaxSizeKey);
+ erase(strTypeKey);
+}
+
+void QueueOptions::clearPersistLastNode()
+{
+ erase(strPersistLastNode);
+}
+
+void QueueOptions::clearOrdering()
+{
+ erase(strLastValueQueue);
+}
+
+void QueueOptions::enableQueueEvents(bool enqueueOnly)
+{
+ setInt(strQueueEventMode, enqueueOnly ? ENQUEUE_ONLY : ENQUEUE_AND_DEQUEUE);
+}
+
+}
+}
+
+
diff --git a/qpid/cpp/src/qpid/client/RdmaConnector.cpp b/qpid/cpp/src/qpid/client/RdmaConnector.cpp
new file mode 100644
index 0000000000..664640f5e7
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/RdmaConnector.cpp
@@ -0,0 +1,431 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Connector.h"
+
+#include "qpid/client/Bounds.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/InitiationHandler.h"
+#include "qpid/sys/rdma/RdmaIO.h"
+#include "qpid/sys/rdma/rdma_exception.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/Msg.h"
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+// This stuff needs to abstracted out of here to a platform specific file
+#include <netdb.h>
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::sys;
+using namespace qpid::framing;
+using boost::format;
+using boost::str;
+
+class RdmaConnector : public Connector, public sys::Codec
+{
+ typedef std::deque<framing::AMQFrame> Frames;
+
+ const uint16_t maxFrameSize;
+ sys::Mutex lock;
+ Frames frames;
+ size_t lastEof; // Position after last EOF in frames
+ uint64_t currentSize;
+ Bounds* bounds;
+
+ framing::ProtocolVersion version;
+ bool initiated;
+
+ sys::Mutex dataConnectedLock;
+ bool dataConnected;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ Rdma::AsynchIO* aio;
+ Rdma::Connector* acon;
+ sys::Poller::shared_ptr poller;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+
+ ~RdmaConnector();
+
+ // Callbacks
+ void connected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&);
+ void connectionError(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr, Rdma::ErrorType);
+ void disconnected();
+ void rejected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&);
+
+ void readbuff(Rdma::AsynchIO&, Rdma::Buffer*);
+ void writebuff(Rdma::AsynchIO&);
+ void writeDataBlock(const framing::AMQDataBlock& data);
+ void dataError(Rdma::AsynchIO&);
+ void drained();
+ void connectionStopped(Rdma::Connector* acon, Rdma::AsynchIO* aio);
+ void dataStopped(Rdma::AsynchIO* aio);
+
+ std::string identifier;
+
+ void connect(const std::string& host, const std::string& port);
+ void close();
+ void send(framing::AMQFrame& frame);
+ void abort() {} // TODO: need to fix this for heartbeat timeouts to work
+
+ void setInputHandler(framing::InputHandler* handler);
+ void setShutdownHandler(sys::ShutdownHandler* handler);
+ sys::ShutdownHandler* getShutdownHandler() const;
+ framing::OutputHandler* getOutputHandler();
+ const std::string& getIdentifier() const;
+ void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>);
+ const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; }
+
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+
+public:
+ RdmaConnector(Poller::shared_ptr,
+ framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(Poller::shared_ptr p, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new RdmaConnector(p, v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ Connector::registerFactory("rdma", &create);
+ Connector::registerFactory("ib", &create);
+ };
+ } init;
+}
+
+
+RdmaConnector::RdmaConnector(Poller::shared_ptr p,
+ ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ lastEof(0),
+ currentSize(0),
+ bounds(cimpl),
+ version(ver),
+ initiated(false),
+ dataConnected(false),
+ shutdownHandler(0),
+ aio(0),
+ acon(0),
+ poller(p)
+{
+ QPID_LOG(debug, "RdmaConnector created for " << version);
+}
+
+namespace {
+ void deleteAsynchIO(Rdma::AsynchIO& aio) {
+ delete &aio;
+ }
+
+ void deleteConnector(Rdma::ConnectionManager& con) {
+ delete &con;
+ }
+}
+
+RdmaConnector::~RdmaConnector() {
+ QPID_LOG(debug, "~RdmaConnector " << identifier);
+ if (aio) {
+ aio->stop(deleteAsynchIO);
+ }
+ if (acon) {
+ acon->stop(deleteConnector);
+ }
+}
+
+void RdmaConnector::connect(const std::string& host, const std::string& port){
+ Mutex::ScopedLock l(dataConnectedLock);
+ assert(!dataConnected);
+
+ acon = new Rdma::Connector(
+ Rdma::ConnectionParams(maxFrameSize, Rdma::DEFAULT_WR_ENTRIES),
+ boost::bind(&RdmaConnector::connected, this, poller, _1, _2),
+ boost::bind(&RdmaConnector::connectionError, this, poller, _1, _2),
+ boost::bind(&RdmaConnector::disconnected, this),
+ boost::bind(&RdmaConnector::rejected, this, poller, _1, _2));
+
+ SocketAddress sa(host, port);
+ acon->start(poller, sa);
+}
+
+// The following only gets run when connected
+void RdmaConnector::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr ci, const Rdma::ConnectionParams& cp) {
+ try {
+ Mutex::ScopedLock l(dataConnectedLock);
+ assert(!dataConnected);
+ Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair();
+
+ aio = new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.rdmaProtocolVersion,
+ cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES,
+ boost::bind(&RdmaConnector::readbuff, this, _1, _2),
+ boost::bind(&RdmaConnector::writebuff, this, _1),
+ 0, // write buffers full
+ boost::bind(&RdmaConnector::dataError, this, _1));
+
+ identifier = str(format("[%1% %2%]") % ci->getLocalName() % ci->getPeerName());
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+
+ aio->start(poller);
+
+ dataConnected = true;
+
+ return;
+ } catch (const Rdma::Exception& e) {
+ QPID_LOG(error, "Rdma: Cannot create new connection (Rdma exception): " << e.what());
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Rdma: Cannot create new connection (unknown exception): " << e.what());
+ }
+ dataConnected = false;
+ connectionStopped(acon, aio);
+}
+
+void RdmaConnector::connectionError(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr, Rdma::ErrorType) {
+ QPID_LOG(debug, "Connection Error " << identifier);
+ connectionStopped(acon, aio);
+}
+
+// Bizarrely we seem to get rejected events *after* we've already got a connected event for some peer disconnects
+// so we need to check whether the data connection is started or not in here
+void RdmaConnector::rejected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams& cp) {
+ QPID_LOG(debug, "Connection Rejected " << identifier << ": " << cp.maxRecvBufferSize);
+ if (dataConnected) {
+ disconnected();
+ } else {
+ connectionStopped(acon, aio);
+ }
+}
+
+void RdmaConnector::disconnected() {
+ QPID_LOG(debug, "Connection disconnected " << identifier);
+ {
+ Mutex::ScopedLock l(dataConnectedLock);
+ // If we're closed already then we'll get to drained() anyway
+ if (!dataConnected) return;
+ dataConnected = false;
+ }
+ // Make sure that all the disconnected actions take place on the data "thread"
+ aio->requestCallback(boost::bind(&RdmaConnector::drained, this));
+}
+
+void RdmaConnector::dataError(Rdma::AsynchIO&) {
+ QPID_LOG(debug, "Data Error " << identifier);
+ {
+ Mutex::ScopedLock l(dataConnectedLock);
+ // If we're closed already then we'll get to drained() anyway
+ if (!dataConnected) return;
+ dataConnected = false;
+ }
+ drained();
+}
+
+void RdmaConnector::close() {
+ QPID_LOG(debug, "RdmaConnector::close " << identifier);
+ {
+ Mutex::ScopedLock l(dataConnectedLock);
+ if (!dataConnected) return;
+ dataConnected = false;
+ }
+ aio->drainWriteQueue(boost::bind(&RdmaConnector::drained, this));
+}
+
+void RdmaConnector::drained() {
+ QPID_LOG(debug, "RdmaConnector::drained " << identifier);
+ assert(!dataConnected);
+ assert(aio);
+ Rdma::AsynchIO* a = aio;
+ aio = 0;
+ a->stop(boost::bind(&RdmaConnector::dataStopped, this, a));
+}
+
+void RdmaConnector::dataStopped(Rdma::AsynchIO* a) {
+ QPID_LOG(debug, "RdmaConnector::dataStopped " << identifier);
+ assert(!dataConnected);
+ assert(acon);
+ Rdma::Connector* c = acon;
+ acon = 0;
+ c->stop(boost::bind(&RdmaConnector::connectionStopped, this, c, a));
+}
+
+void RdmaConnector::connectionStopped(Rdma::Connector* c, Rdma::AsynchIO* a) {
+ QPID_LOG(debug, "RdmaConnector::connectionStopped " << identifier);
+ assert(!dataConnected);
+ aio = 0;
+ acon = 0;
+ delete a;
+ delete c;
+ if (shutdownHandler) {
+ ShutdownHandler* s = shutdownHandler;
+ shutdownHandler = 0;
+ s->shutdown();
+ }
+}
+
+void RdmaConnector::setInputHandler(InputHandler* handler){
+ input = handler;
+}
+
+void RdmaConnector::setShutdownHandler(ShutdownHandler* handler){
+ shutdownHandler = handler;
+}
+
+OutputHandler* RdmaConnector::getOutputHandler(){
+ return this;
+}
+
+sys::ShutdownHandler* RdmaConnector::getShutdownHandler() const {
+ return shutdownHandler;
+}
+
+const std::string& RdmaConnector::getIdentifier() const {
+ return identifier;
+}
+
+void RdmaConnector::send(AMQFrame& frame) {
+ // It is possible that we are called to write after we are already shutting down
+ Mutex::ScopedLock l(dataConnectedLock);
+ if (!dataConnected) return;
+
+ bool notifyWrite = false;
+ {
+ Mutex::ScopedLock l(lock);
+ frames.push_back(frame);
+ //only ask to write if this is the end of a frameset or if we
+ //already have a buffers worth of data
+ currentSize += frame.encodedSize();
+ if (frame.getEof()) {
+ lastEof = frames.size();
+ notifyWrite = true;
+ } else {
+ notifyWrite = (currentSize >= maxFrameSize);
+ }
+ }
+ if (notifyWrite) aio->notifyPendingWrite();
+}
+
+// Called in IO thread. (write idle routine)
+// This is NOT only called in response to previously calling notifyPendingWrite
+void RdmaConnector::writebuff(Rdma::AsynchIO&) {
+ // It's possible to be disconnected and be writable
+ Mutex::ScopedLock l(dataConnectedLock);
+ if (!dataConnected) {
+ return;
+ }
+ Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
+ if (!codec->canEncode()) {
+ return;
+ }
+ Rdma::Buffer* buffer = aio->getSendBuffer();
+ if (buffer) {
+ size_t encoded = codec->encode(buffer->bytes(), buffer->byteCount());
+ buffer->dataCount(encoded);
+ aio->queueWrite(buffer);
+ }
+}
+
+bool RdmaConnector::canEncode()
+{
+ Mutex::ScopedLock l(lock);
+ //have at least one full frameset or a whole buffers worth of data
+ return aio->writable() && (lastEof || currentSize >= maxFrameSize);
+}
+
+size_t RdmaConnector::encode(const char* buffer, size_t size)
+{
+ framing::Buffer out(const_cast<char*>(buffer), size);
+ size_t bytesWritten(0);
+ {
+ Mutex::ScopedLock l(lock);
+ while (!frames.empty() && out.available() >= frames.front().encodedSize() ) {
+ frames.front().encode(out);
+ QPID_LOG(trace, "SENT " << identifier << ": " << frames.front());
+ frames.pop_front();
+ if (lastEof) --lastEof;
+ }
+ bytesWritten = size - out.available();
+ currentSize -= bytesWritten;
+ }
+ if (bounds) bounds->reduce(bytesWritten);
+ return bytesWritten;
+}
+
+void RdmaConnector::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) {
+ Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
+ codec->decode(buff->bytes(), buff->dataCount());
+}
+
+size_t RdmaConnector::decode(const char* buffer, size_t size)
+{
+ framing::Buffer in(const_cast<char*>(buffer), size);
+ if (!initiated) {
+ framing::ProtocolInitiation protocolInit;
+ if (protocolInit.decode(in)) {
+ //TODO: check the version is correct
+ QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")");
+ }
+ initiated = true;
+ }
+ AMQFrame frame;
+ while(frame.decode(in)){
+ QPID_LOG(trace, "RECV " << identifier << ": " << frame);
+ input->received(frame);
+ }
+ return size - in.available();
+}
+
+void RdmaConnector::writeDataBlock(const AMQDataBlock& data) {
+ Rdma::Buffer* buff = aio->getSendBuffer();
+ framing::Buffer out(buff->bytes(), buff->byteCount());
+ data.encode(out);
+ buff->dataCount(data.encodedSize());
+ aio->queueWrite(buff);
+}
+
+void RdmaConnector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer> sl)
+{
+ securityLayer = sl;
+ securityLayer->init(this);
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/Results.cpp b/qpid/cpp/src/qpid/client/Results.cpp
new file mode 100644
index 0000000000..0de3e8bd04
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Results.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Results.h"
+#include "qpid/client/FutureResult.h"
+#include "qpid/framing/SequenceSet.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace client {
+
+Results::Results() {}
+
+Results::~Results() {
+ try { close(); } catch (const std::exception& /*e*/) { assert(0); }
+}
+
+void Results::close()
+{
+ for (Listeners::iterator i = listeners.begin(); i != listeners.end(); i++) {
+ i->second->completed();
+ }
+ listeners.clear();
+}
+
+void Results::completed(const SequenceSet& set)
+{
+ //call complete on those listeners whose ids fall within the set
+ Listeners::iterator i = listeners.begin();
+ while (i != listeners.end()) {
+ if (set.contains(i->first)) {
+ i->second->completed();
+ listeners.erase(i++);
+ } else {
+ i++;
+ }
+ }
+}
+
+void Results::received(const SequenceNumber& id, const std::string& result)
+{
+ Listeners::iterator i = listeners.find(id);
+ if (i != listeners.end()) {
+ i->second->received(result);
+ listeners.erase(i);
+ }
+}
+
+Results::FutureResultPtr Results::listenForResult(const SequenceNumber& id)
+{
+ FutureResultPtr l(new FutureResult());
+ listeners[id] = l;
+ return l;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/client/Results.h b/qpid/cpp/src/qpid/client/Results.h
new file mode 100644
index 0000000000..4c49f6b05b
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Results.h
@@ -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.
+ *
+ */
+
+#include "qpid/framing/SequenceNumber.h"
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+#ifndef _Results_
+#define _Results_
+
+namespace qpid {
+namespace client {
+
+class FutureResult;
+
+///@internal
+class Results
+{
+public:
+ typedef boost::shared_ptr<FutureResult> FutureResultPtr;
+
+ Results();
+ ~Results();
+ void completed(const framing::SequenceSet& set);
+ void received(const framing::SequenceNumber& id, const std::string& result);
+ FutureResultPtr listenForResult(const framing::SequenceNumber& point);
+ void close();
+
+private:
+ typedef std::map<framing::SequenceNumber, FutureResultPtr> Listeners;
+ Listeners listeners;
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp b/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp
new file mode 100644
index 0000000000..e114b7aacc
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SessionBase_0_10.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/SessionBase_0_10.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/Future.h"
+#include "qpid/framing/all_method_bodies.h"
+
+namespace qpid {
+namespace client {
+
+using namespace framing;
+
+SessionBase_0_10::SessionBase_0_10() {}
+SessionBase_0_10::~SessionBase_0_10() {}
+
+void SessionBase_0_10::close()
+{
+ if (impl) impl->close();
+}
+
+void SessionBase_0_10::flush()
+{
+ impl->sendFlush();
+}
+
+void SessionBase_0_10::sync()
+{
+ ExecutionSyncBody b;
+ b.setSync(true);
+ impl->send(b).wait(*impl);
+}
+
+void SessionBase_0_10::markCompleted(const framing::SequenceSet& ids, bool notifyPeer)
+{
+ impl->markCompleted(ids, notifyPeer);
+}
+
+void SessionBase_0_10::markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer)
+{
+ impl->markCompleted(id, cumulative, notifyPeer);
+}
+
+void SessionBase_0_10::sendCompletion()
+{
+ impl->sendCompletion();
+}
+
+uint16_t SessionBase_0_10::getChannel() const { return impl->getChannel(); }
+
+void SessionBase_0_10::suspend() { impl->suspend(); }
+void SessionBase_0_10::resume(Connection c) { impl->resume(c.impl); }
+uint32_t SessionBase_0_10::timeout(uint32_t seconds) { return impl->setTimeout(seconds); }
+
+SessionId SessionBase_0_10::getId() const { return impl->getId(); }
+
+bool SessionBase_0_10::isValid() const { return impl; }
+
+Connection SessionBase_0_10::getConnection()
+{
+ Connection c;
+ ConnectionAccess::setImpl(c, impl->getConnection());
+ return c;
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h b/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h
new file mode 100644
index 0000000000..4d08a7ceaf
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SessionBase_0_10Access.h
@@ -0,0 +1,42 @@
+#ifndef QPID_CLIENT_SESSIONBASEACCESS_H
+#define QPID_CLIENT_SESSIONBASEACCESS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/SessionBase_0_10.h"
+
+/**@file @internal Internal use only */
+
+namespace qpid {
+namespace client {
+
+class SessionBase_0_10Access {
+ public:
+ SessionBase_0_10Access(SessionBase_0_10& sb_) : sb(sb_) {}
+ void set(const boost::shared_ptr<SessionImpl>& si) { sb.impl = si; }
+ boost::shared_ptr<SessionImpl> get() const { return sb.impl; }
+ private:
+ SessionBase_0_10& sb;
+};
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_SESSIONBASEACCESS_H*/
diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp
new file mode 100644
index 0000000000..b507625b11
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp
@@ -0,0 +1,824 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/SessionImpl.h"
+
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Future.h"
+
+#include "qpid/framing/all_method_bodies.h"
+#include "qpid/framing/ClientInvoker.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MethodContent.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/IntegerTypes.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace { const std::string EMPTY; }
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::framing;
+using namespace qpid::framing::session; //for detach codes
+
+typedef sys::Monitor::ScopedLock Lock;
+typedef sys::Monitor::ScopedUnlock UnLock;
+typedef sys::ScopedLock<sys::Semaphore> Acquire;
+
+
+SessionImpl::SessionImpl(const std::string& name, boost::shared_ptr<ConnectionImpl> conn)
+ : state(INACTIVE),
+ detachedLifetime(0),
+ maxFrameSize(conn->getNegotiatedSettings().maxFrameSize),
+ id(conn->getNegotiatedSettings().username, name.empty() ? Uuid(true).str() : name),
+ connection(conn),
+ ioHandler(*this),
+ proxy(ioHandler),
+ nextIn(0),
+ nextOut(0),
+ sendMsgCredit(0),
+ doClearDeliveryPropertiesExchange(true),
+ autoDetach(true)
+{
+ channel.next = connection.get();
+}
+
+SessionImpl::~SessionImpl() {
+ {
+ Lock l(state);
+ if (state != DETACHED && state != DETACHING) {
+ if (autoDetach) {
+ QPID_LOG(warning, "Session was not closed cleanly: " << id);
+ // Inform broker but don't wait for detached as that deadlocks.
+ // The detached will be ignored as the channel will be invalid.
+ try { detach(); } catch (...) {} // ignore errors.
+ }
+ setState(DETACHED);
+ handleClosed();
+ state.waitWaiters();
+ }
+ delete sendMsgCredit;
+ }
+ connection->erase(channel);
+}
+
+
+FrameSet::shared_ptr SessionImpl::get() // user thread
+{
+ // No lock here: pop does a blocking wait.
+ return demux.getDefault()->pop();
+}
+
+const SessionId SessionImpl::getId() const //user thread
+{
+ return id; //id is immutable
+}
+
+void SessionImpl::open(uint32_t timeout) // user thread
+{
+ Lock l(state);
+ if (state == INACTIVE) {
+ setState(ATTACHING);
+ proxy.attach(id.getName(), false);
+ waitFor(ATTACHED);
+ //TODO: timeout will not be set locally until get response to
+ //confirm, should we wait for that?
+ setTimeout(timeout);
+ proxy.commandPoint(nextOut, 0);
+ } else {
+ throw Exception("Open already called for this session");
+ }
+}
+
+void SessionImpl::close() //user thread
+{
+ Lock l(state);
+ // close() must be idempotent and no-throw as it will often be called in destructors.
+ if (state != DETACHED && state != DETACHING) {
+ try {
+ if (detachedLifetime) setTimeout(0);
+ detach();
+ waitFor(DETACHED);
+ } catch (...) {}
+ setState(DETACHED);
+ }
+}
+
+void SessionImpl::resume(boost::shared_ptr<ConnectionImpl>) // user thread
+{
+ throw NotImplementedException("Resume not yet implemented by client!");
+}
+
+void SessionImpl::suspend() //user thread
+{
+ Lock l(state);
+ detach();
+}
+
+void SessionImpl::detach() //call with lock held
+{
+ if (state == ATTACHED) {
+ setState(DETACHING);
+ proxy.detach(id.getName());
+ }
+}
+
+
+uint16_t SessionImpl::getChannel() const // user thread
+{
+ return channel;
+}
+
+void SessionImpl::setChannel(uint16_t c) // user thread
+{
+ //channel will only ever be set when session is detached (and
+ //about to be resumed)
+ channel = c;
+}
+
+Demux& SessionImpl::getDemux()
+{
+ return demux;
+}
+
+void SessionImpl::waitForCompletion(const SequenceNumber& id)
+{
+ Lock l(state);
+ waitForCompletionImpl(id);
+}
+
+void SessionImpl::waitForCompletionImpl(const SequenceNumber& id) //call with lock held
+{
+ while (incompleteOut.contains(id)) {
+ checkOpen();
+ state.wait();
+ }
+}
+
+bool SessionImpl::isComplete(const SequenceNumber& id)
+{
+ Lock l(state);
+ return !incompleteOut.contains(id);
+}
+
+struct IsCompleteUpTo
+{
+ const SequenceNumber& id;
+ bool result;
+
+ IsCompleteUpTo(const SequenceNumber& _id) : id(_id), result(true) {}
+ void operator()(const SequenceNumber& start, const SequenceNumber&)
+ {
+ if (start <= id) result = false;
+ }
+
+};
+
+bool SessionImpl::isCompleteUpTo(const SequenceNumber& id)
+{
+ Lock l(state);
+ //return false if incompleteOut contains anything less than id,
+ //true otherwise
+ IsCompleteUpTo f(id);
+ incompleteIn.for_each(f);
+ return f.result;
+}
+
+framing::SequenceNumber SessionImpl::getCompleteUpTo()
+{
+ SequenceNumber firstIncomplete;
+ {
+ Lock l(state);
+ firstIncomplete = incompleteIn.front();
+ }
+ return --firstIncomplete;
+}
+
+struct MarkCompleted
+{
+ const SequenceNumber& id;
+ SequenceSet& completedIn;
+
+ MarkCompleted(const SequenceNumber& _id, SequenceSet& set) : id(_id), completedIn(set) {}
+
+ void operator()(const SequenceNumber& start, const SequenceNumber& end)
+ {
+ if (id >= end) {
+ completedIn.add(start, end);
+ } else if (id >= start) {
+ completedIn.add(start, id);
+ }
+ }
+
+};
+
+void SessionImpl::markCompleted(const SequenceSet& ids, bool notifyPeer)
+{
+ Lock l(state);
+ incompleteIn.remove(ids);
+ completedIn.add(ids);
+ if (notifyPeer) {
+ sendCompletion();
+ }
+}
+
+void SessionImpl::markCompleted(const SequenceNumber& id, bool cumulative, bool notifyPeer)
+{
+ Lock l(state);
+ if (cumulative) {
+ //everything in incompleteIn less than or equal to id is now complete
+ MarkCompleted f(id, completedIn);
+ incompleteIn.for_each(f);
+ //make sure id itself is in
+ completedIn.add(id);
+ //then remove anything thats completed from the incomplete set
+ incompleteIn.remove(completedIn);
+ } else if (incompleteIn.contains(id)) {
+ incompleteIn.remove(id);
+ completedIn.add(id);
+ }
+ if (notifyPeer) {
+ sendCompletion();
+ }
+}
+
+void SessionImpl::setException(const sys::ExceptionHolder& ex) {
+ Lock l(state);
+ setExceptionLH(ex);
+}
+
+void SessionImpl::setExceptionLH(const sys::ExceptionHolder& ex) { // Call with lock held.
+ exceptionHolder = ex;
+ setState(DETACHED);
+}
+
+/**
+ * Called by ConnectionImpl to notify active sessions when connection
+ * is explictly closed
+ */
+void SessionImpl::connectionClosed(uint16_t code, const std::string& text) {
+ setException(createConnectionException(code, text));
+ handleClosed();
+}
+
+/**
+ * Called by ConnectionImpl to notify active sessions when connection
+ * is disconnected
+ */
+void SessionImpl::connectionBroke(const std::string& _text) {
+ setException(sys::ExceptionHolder(new TransportFailure(_text)));
+ handleClosed();
+}
+
+Future SessionImpl::send(const AMQBody& command)
+{
+ return sendCommand(command);
+}
+
+Future SessionImpl::send(const AMQBody& command, const MethodContent& content)
+{
+ return sendCommand(command, &content);
+}
+
+namespace {
+// Functor for FrameSet::map to send header + content frames but, not method frames.
+struct SendContentFn {
+ FrameHandler& handler;
+ void operator()(const AMQFrame& f) {
+ if (!f.getMethod())
+ handler(const_cast<AMQFrame&>(f));
+ }
+ SendContentFn(FrameHandler& h) : handler(h) {}
+};
+
+// Adaptor to make FrameSet look like MethodContent; used in cluster update client
+struct MethodContentAdaptor : MethodContent
+{
+ AMQHeaderBody header;
+ const std::string content;
+
+ MethodContentAdaptor(const FrameSet& f) : header(*f.getHeaders()), content(f.getContent()) {}
+
+ AMQHeaderBody getHeader() const
+ {
+ return header;
+ }
+ const std::string& getData() const
+ {
+ return content;
+ }
+};
+
+}
+
+Future SessionImpl::send(const AMQBody& command, const FrameSet& content, bool reframe) {
+ Acquire a(sendLock);
+ SequenceNumber id = nextOut++;
+ {
+ Lock l(state);
+ checkOpen();
+ incompleteOut.add(id);
+ }
+ Future f(id);
+ if (command.getMethod()->resultExpected()) {
+ Lock l(state);
+ //result listener must be set before the command is sent
+ f.setFutureResult(results.listenForResult(id));
+ }
+ AMQFrame frame(command);
+ frame.setEof(false);
+ handleOut(frame);
+
+ if (reframe) {
+ MethodContentAdaptor c(content);
+ sendContent(c);
+ } else {
+ SendContentFn send(out);
+ content.map(send);
+ }
+ return f;
+}
+
+void SessionImpl::sendRawFrame(AMQFrame& frame) {
+ Acquire a(sendLock);
+ handleOut(frame);
+}
+
+Future SessionImpl::sendCommand(const AMQBody& command, const MethodContent* content)
+{
+ // Only message transfers have content
+ if (content && sendMsgCredit) {
+ sendMsgCredit->acquire();
+ }
+ Acquire a(sendLock);
+ SequenceNumber id = nextOut++;
+ {
+ Lock l(state);
+ checkOpen();
+ incompleteOut.add(id);
+ }
+ Future f(id);
+ if (command.getMethod()->resultExpected()) {
+ Lock l(state);
+ //result listener must be set before the command is sent
+ f.setFutureResult(results.listenForResult(id));
+ }
+ AMQFrame frame(command);
+ if (content) {
+ frame.setEof(false);
+ }
+ handleOut(frame);
+ if (content) {
+ sendContent(*content);
+ }
+ return f;
+}
+
+void SessionImpl::sendContent(const MethodContent& content)
+{
+ AMQFrame header(content.getHeader());
+
+ // doClearDeliveryPropertiesExchange is set by cluster update client so
+ // it can send messages with delivery-properties.exchange set.
+ //
+ if (doClearDeliveryPropertiesExchange) {
+ // Normal client is not allowed to set the delivery-properties.exchange
+ // so clear it here.
+ AMQHeaderBody* headerp = static_cast<AMQHeaderBody*>(header.getBody());
+ if (headerp && headerp->get<DeliveryProperties>())
+ headerp->get<DeliveryProperties>(true)->clearExchangeFlag();
+ }
+ header.setFirstSegment(false);
+ uint64_t data_length = content.getData().length();
+ if(data_length > 0){
+ header.setLastSegment(false);
+ handleOut(header);
+ /*Note: end of frame marker included in overhead but not in size*/
+ const uint32_t frag_size = maxFrameSize - AMQFrame::frameOverhead();
+
+ if(data_length < frag_size){
+ AMQFrame frame((AMQContentBody(content.getData())));
+ frame.setFirstSegment(false);
+ handleOut(frame);
+ }else{
+ uint32_t offset = 0;
+ uint32_t remaining = data_length - offset;
+ while (remaining > 0) {
+ uint32_t length = remaining > frag_size ? frag_size : remaining;
+ string frag(content.getData().substr(offset, length));
+ AMQFrame frame((AMQContentBody(frag)));
+ frame.setFirstSegment(false);
+ frame.setLastSegment(true);
+ if (offset > 0) {
+ frame.setFirstFrame(false);
+ }
+ offset += length;
+ remaining = data_length - offset;
+ if (remaining) {
+ frame.setLastFrame(false);
+ }
+ handleOut(frame);
+ }
+ }
+ } else {
+ handleOut(header);
+ }
+}
+
+
+bool isMessageMethod(AMQMethodBody* method)
+{
+ return method->isA<MessageTransferBody>();
+}
+
+bool isMessageMethod(AMQBody* body)
+{
+ AMQMethodBody* method=body->getMethod();
+ return method && isMessageMethod(method);
+}
+
+bool isContentFrame(AMQFrame& frame)
+{
+ AMQBody* body = frame.getBody();
+ uint8_t type = body->type();
+ return type == HEADER_BODY || type == CONTENT_BODY || isMessageMethod(body);
+}
+
+void SessionImpl::handleIn(AMQFrame& frame) // network thread
+{
+ try {
+ if (invoke(static_cast<SessionHandler&>(*this), *frame.getBody())) {
+ ;
+ } else if (invoke(static_cast<ExecutionHandler&>(*this), *frame.getBody())) {
+ //make sure the command id sequence and completion
+ //tracking takes account of execution commands
+ Lock l(state);
+ completedIn.add(nextIn++);
+ } else if (invoke(static_cast<MessageHandler&>(*this), *frame.getBody())) {
+ ;
+ } else {
+ //if not handled by this class, its for the application:
+ deliver(frame);
+ }
+ }
+ catch (const SessionException& e) {
+ setException(createSessionException(e.code, e.getMessage()));
+ }
+ catch (const ChannelException& e) {
+ setException(createChannelException(e.code, e.getMessage()));
+ }
+}
+
+void SessionImpl::handleOut(AMQFrame& frame) // user thread
+{
+ sendFrame(frame, true);
+}
+
+void SessionImpl::proxyOut(AMQFrame& frame) // network thread
+{
+ //Note: this case is treated slightly differently that command
+ //frames sent by application; session controls should not be
+ //blocked by bounds checking on the outgoing frame queue.
+ sendFrame(frame, false);
+}
+
+void SessionImpl::sendFrame(AMQFrame& frame, bool canBlock)
+{
+ connection->expand(frame.encodedSize(), canBlock);
+ channel.handle(frame);
+}
+
+void SessionImpl::deliver(AMQFrame& frame) // network thread
+{
+ if (!arriving) {
+ arriving = FrameSet::shared_ptr(new FrameSet(nextIn++));
+ }
+ arriving->append(frame);
+ if (arriving->isComplete()) {
+ //message.transfers will be marked completed only when 'acked'
+ //as completion affects flow control; other commands will be
+ //considered completed as soon as processed here
+ if (arriving->isA<MessageTransferBody>()) {
+ Lock l(state);
+ incompleteIn.add(arriving->getId());
+ } else {
+ Lock l(state);
+ completedIn.add(arriving->getId());
+ }
+ demux.handle(arriving);
+ arriving.reset();
+ }
+}
+
+//control handler methods (called by network thread when controls are
+//received from peer):
+
+void SessionImpl::attach(const std::string& /*name*/, bool /*force*/)
+{
+ throw NotImplementedException("Client does not support attach");
+}
+
+void SessionImpl::attached(const std::string& _name)
+{
+ Lock l(state);
+ if (id.getName() != _name) throw InternalErrorException("Incorrect session name");
+ setState(ATTACHED);
+}
+
+void SessionImpl::detach(const std::string& _name)
+{
+ Lock l(state);
+ if (id.getName() != _name) throw InternalErrorException("Incorrect session name");
+ setState(DETACHED);
+ QPID_LOG(info, "Session detached by peer: " << id);
+ proxy.detached(_name, DETACH_CODE_NORMAL);
+ handleClosed();
+}
+
+void SessionImpl::detached(const std::string& _name, uint8_t _code) {
+ Lock l(state);
+ if (id.getName() != _name) throw InternalErrorException("Incorrect session name");
+ setState(DETACHED);
+ if (_code) {
+ //TODO: make sure this works with execution.exception - don't
+ //want to overwrite the code from that
+ setExceptionLH(createChannelException(_code, "Session detached by peer"));
+ QPID_LOG(error, exceptionHolder.what());
+ }
+ if (detachedLifetime == 0) {
+ handleClosed();
+}
+}
+
+void SessionImpl::requestTimeout(uint32_t t)
+{
+ Lock l(state);
+ detachedLifetime = t;
+ proxy.timeout(t);
+}
+
+void SessionImpl::timeout(uint32_t t)
+{
+ Lock l(state);
+ detachedLifetime = t;
+}
+
+void SessionImpl::commandPoint(const framing::SequenceNumber& id, uint64_t offset)
+{
+ if (offset) throw NotImplementedException("Non-zero byte offset not yet supported for command-point");
+
+ Lock l(state);
+ nextIn = id;
+}
+
+void SessionImpl::expected(const framing::SequenceSet& commands, const framing::Array& fragments)
+{
+ if (!commands.empty() || fragments.encodedSize()) {
+ throw NotImplementedException("Session resumption not yet supported");
+ }
+}
+
+void SessionImpl::confirmed(const framing::SequenceSet& /*commands*/, const framing::Array& /*fragments*/)
+{
+ //don't really care too much about this yet
+}
+
+void SessionImpl::completed(const framing::SequenceSet& commands, bool timelyReply)
+{
+ Lock l(state);
+ incompleteOut.remove(commands);
+ state.notifyAll();//notify any waiters of completion
+ completedOut.add(commands);
+ //notify any waiting results of completion
+ results.completed(commands);
+
+ if (timelyReply) {
+ proxy.knownCompleted(completedOut);
+ completedOut.clear();
+ }
+}
+
+void SessionImpl::knownCompleted(const framing::SequenceSet& commands)
+{
+ Lock l(state);
+ completedIn.remove(commands);
+}
+
+void SessionImpl::flush(bool expected, bool confirmed, bool completed)
+{
+ Lock l(state);
+ if (expected) {
+ proxy.expected(SequenceSet(nextIn), Array());
+ }
+ if (confirmed) {
+ proxy.confirmed(completedIn, Array());
+ }
+ if (completed) {
+ proxy.completed(completedIn, true);
+ }
+}
+
+void SessionImpl::sendCompletion()
+{
+ Lock l(state);
+ sendCompletionImpl();
+}
+
+void SessionImpl::sendFlush()
+{
+ Lock l(state);
+ proxy.flush(false, false, true);
+}
+
+void SessionImpl::sendCompletionImpl()
+{
+ proxy.completed(completedIn, completedIn.span() > 1000);
+}
+
+void SessionImpl::gap(const framing::SequenceSet& /*commands*/)
+{
+ throw NotImplementedException("gap not yet supported");
+}
+
+void SessionImpl::sync() {}
+
+void SessionImpl::result(const framing::SequenceNumber& commandId, const std::string& value)
+{
+ Lock l(state);
+ results.received(commandId, value);
+}
+
+void SessionImpl::exception(uint16_t errorCode,
+ const framing::SequenceNumber& commandId,
+ uint8_t classCode,
+ uint8_t commandCode,
+ uint8_t /*fieldIndex*/,
+ const std::string& description,
+ const framing::FieldTable& /*errorInfo*/)
+{
+ Lock l(state);
+ setExceptionLH(createSessionException(errorCode, description));
+ QPID_LOG(warning, "Exception received from broker: " << exceptionHolder.what()
+ << " [caused by " << commandId << " " << classCode << ":" << commandCode << "]");
+
+ if (detachedLifetime)
+ setTimeout(0);
+}
+
+// Message methods:
+void SessionImpl::accept(const qpid::framing::SequenceSet&)
+{
+}
+
+void SessionImpl::reject(const qpid::framing::SequenceSet&, uint16_t, const std::string&)
+{
+}
+
+void SessionImpl::release(const qpid::framing::SequenceSet&, bool)
+{
+}
+
+MessageResumeResult SessionImpl::resume(const std::string&, const std::string&)
+{
+ throw NotImplementedException("resuming transfers not yet supported");
+}
+
+namespace {
+ const std::string QPID_SESSION_DEST = "";
+ const uint8_t FLOW_MODE_CREDIT = 0;
+ const uint8_t CREDIT_MODE_MSG = 0;
+}
+
+void SessionImpl::setFlowMode(const std::string& dest, uint8_t flowMode)
+{
+ if ( dest != QPID_SESSION_DEST ) {
+ QPID_LOG(warning, "Ignoring flow control for unknown destination: " << dest);
+ return;
+ }
+
+ if ( flowMode != FLOW_MODE_CREDIT ) {
+ throw NotImplementedException("window flow control mode not supported by producer");
+ }
+ Lock l(state);
+ sendMsgCredit = new sys::Semaphore(0);
+}
+
+void SessionImpl::flow(const std::string& dest, uint8_t mode, uint32_t credit)
+{
+ if ( dest != QPID_SESSION_DEST ) {
+ QPID_LOG(warning, "Ignoring flow control for unknown destination: " << dest);
+ return;
+ }
+
+ if ( mode != CREDIT_MODE_MSG ) {
+ return;
+ }
+ if (sendMsgCredit) {
+ sendMsgCredit->release(credit);
+ }
+}
+
+void SessionImpl::stop(const std::string& dest)
+{
+ if ( dest != QPID_SESSION_DEST ) {
+ QPID_LOG(warning, "Ignoring flow control for unknown destination: " << dest);
+ return;
+ }
+ if (sendMsgCredit) {
+ sendMsgCredit->forceLock();
+ }
+}
+
+//private utility methods:
+
+inline void SessionImpl::setState(State s) //call with lock held
+{
+ state = s;
+}
+
+inline void SessionImpl::waitFor(State s) //call with lock held
+{
+ // We can be DETACHED at any time
+ if (s == DETACHED) state.waitFor(DETACHED);
+ else state.waitFor(States(s, DETACHED));
+ check();
+}
+
+void SessionImpl::check() const //call with lock held.
+{
+ exceptionHolder.raise();
+}
+
+void SessionImpl::checkOpen() const //call with lock held.
+{
+ check();
+ if (state != ATTACHED) {
+ throw NotAttachedException(QPID_MSG("Session " << getId() << " isn't attached"));
+ }
+}
+
+void SessionImpl::assertOpen() const
+{
+ Lock l(state);
+ checkOpen();
+}
+
+bool SessionImpl::hasError() const
+{
+ Lock l(state);
+ return !exceptionHolder.empty();
+}
+
+void SessionImpl::handleClosed()
+{
+ demux.close(exceptionHolder.empty() ?
+ sys::ExceptionHolder(new ClosedException()) : exceptionHolder);
+ results.close();
+}
+
+uint32_t SessionImpl::setTimeout(uint32_t seconds) {
+ proxy.requestTimeout(seconds);
+ // FIXME aconway 2008-10-07: wait for timeout response from broker
+ // and use value retured by broker.
+ detachedLifetime = seconds;
+ return detachedLifetime;
+}
+
+uint32_t SessionImpl::getTimeout() const {
+ return detachedLifetime;
+}
+
+boost::shared_ptr<ConnectionImpl> SessionImpl::getConnection()
+{
+ return connection;
+}
+
+void SessionImpl::disableAutoDetach() { autoDetach = false; }
+
+}}
diff --git a/qpid/cpp/src/qpid/client/SessionImpl.h b/qpid/cpp/src/qpid/client/SessionImpl.h
new file mode 100644
index 0000000000..cd7b2c123d
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SessionImpl.h
@@ -0,0 +1,254 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef _SessionImpl_
+#define _SessionImpl_
+
+#include "qpid/client/Demux.h"
+#include "qpid/client/Execution.h"
+#include "qpid/client/Results.h"
+#include "qpid/client/ClientImportExport.h"
+
+#include "qpid/SessionId.h"
+#include "qpid/SessionState.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/ChannelHandler.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/AMQP_ClientOperations.h"
+#include "qpid/framing/AMQP_ServerProxy.h"
+#include "qpid/sys/Semaphore.h"
+#include "qpid/sys/StateMonitor.h"
+#include "qpid/sys/ExceptionHolder.h"
+
+#include <boost/weak_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace qpid {
+
+namespace framing {
+
+class FrameSet;
+class MethodContent;
+class SequenceSet;
+
+}
+
+namespace client {
+
+class Future;
+class ConnectionImpl;
+class SessionHandler;
+
+///@internal
+class SessionImpl : public framing::FrameHandler::InOutHandler,
+ public Execution,
+ private framing::AMQP_ClientOperations::SessionHandler,
+ private framing::AMQP_ClientOperations::ExecutionHandler,
+ private framing::AMQP_ClientOperations::MessageHandler
+{
+public:
+ SessionImpl(const std::string& name, boost::shared_ptr<ConnectionImpl>);
+ ~SessionImpl();
+
+
+ //NOTE: Public functions called in user thread.
+ framing::FrameSet::shared_ptr get();
+
+ const SessionId getId() const;
+
+ uint16_t getChannel() const;
+ void setChannel(uint16_t channel);
+
+ void open(uint32_t detachedLifetime);
+ void close();
+ void resume(boost::shared_ptr<ConnectionImpl>);
+ void suspend();
+
+ QPID_CLIENT_EXTERN void assertOpen() const;
+ QPID_CLIENT_EXTERN bool hasError() const;
+
+ Future send(const framing::AMQBody& command);
+ Future send(const framing::AMQBody& command, const framing::MethodContent& content);
+ /**
+ * This method takes the content as a FrameSet; if reframe=false,
+ * the caller is resposnible for ensuring that the header and
+ * content frames in that set are correct for this connection
+ * (right flags, right fragmentation etc). If reframe=true, then
+ * the header and content from the frameset will be copied and
+ * reframed correctly for the connection.
+ */
+ QPID_CLIENT_EXTERN Future send(const framing::AMQBody& command, const framing::FrameSet& content, bool reframe=false);
+ void sendRawFrame(framing::AMQFrame& frame);
+
+ Demux& getDemux();
+ void markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer);
+ void markCompleted(const framing::SequenceSet& ids, bool notifyPeer);
+ bool isComplete(const framing::SequenceNumber& id);
+ bool isCompleteUpTo(const framing::SequenceNumber& id);
+ framing::SequenceNumber getCompleteUpTo();
+ void waitForCompletion(const framing::SequenceNumber& id);
+ void sendCompletion();
+ void sendFlush();
+
+ void setException(const sys::ExceptionHolder&);
+
+ //NOTE: these are called by the network thread when the connection is closed or dies
+ void connectionClosed(uint16_t code, const std::string& text);
+ void connectionBroke(const std::string& text);
+
+ /** Set timeout in seconds, returns actual timeout allowed by broker */
+ uint32_t setTimeout(uint32_t requestedSeconds);
+
+ /** Get timeout in seconds. */
+ uint32_t getTimeout() const;
+
+ /**
+ * get the Connection associated with this connection
+ */
+ boost::shared_ptr<ConnectionImpl> getConnection();
+
+ void setDoClearDeliveryPropertiesExchange(bool b=true) { doClearDeliveryPropertiesExchange = b; }
+
+ /** Suppress sending detach in destructor. Used by cluster to build session state */
+ void disableAutoDetach();
+
+private:
+ enum State {
+ INACTIVE,
+ ATTACHING,
+ ATTACHED,
+ DETACHING,
+ DETACHED
+ };
+ typedef framing::AMQP_ClientOperations::SessionHandler SessionHandler;
+ typedef framing::AMQP_ClientOperations::ExecutionHandler ExecutionHandler;
+ typedef framing::AMQP_ClientOperations::MessageHandler MessageHandler;
+ typedef sys::StateMonitor<State, DETACHED> StateMonitor;
+ typedef StateMonitor::Set States;
+
+ inline void setState(State s);
+ inline void waitFor(State);
+
+ void setExceptionLH(const sys::ExceptionHolder&); // LH = lock held when called.
+ void detach();
+
+ void check() const;
+ void checkOpen() const;
+ void handleClosed();
+
+ void handleIn(framing::AMQFrame& frame);
+ void handleOut(framing::AMQFrame& frame);
+ /**
+ * Sends session controls. This case is treated slightly
+ * differently than command frames sent by the application via
+ * handleOut(); session controlsare not subject to bounds checking
+ * on the outgoing frame queue.
+ */
+ void proxyOut(framing::AMQFrame& frame);
+ void sendFrame(framing::AMQFrame& frame, bool canBlock);
+ void deliver(framing::AMQFrame& frame);
+
+ Future sendCommand(const framing::AMQBody&, const framing::MethodContent* = 0);
+ void sendContent(const framing::MethodContent&);
+ void waitForCompletionImpl(const framing::SequenceNumber& id);
+
+ void sendCompletionImpl();
+
+ // Note: Following methods are called by network thread in
+ // response to session controls from the broker
+ void attach(const std::string& name, bool force);
+ void attached(const std::string& name);
+ void detach(const std::string& name);
+ void detached(const std::string& name, uint8_t detachCode);
+ void requestTimeout(uint32_t timeout);
+ void timeout(uint32_t timeout);
+ void commandPoint(const framing::SequenceNumber& commandId, uint64_t commandOffset);
+ void expected(const framing::SequenceSet& commands, const framing::Array& fragments);
+ void confirmed(const framing::SequenceSet& commands, const framing::Array& fragments);
+ void completed(const framing::SequenceSet& commands, bool timelyReply);
+ void knownCompleted(const framing::SequenceSet& commands);
+ void flush(bool expected, bool confirmed, bool completed);
+ void gap(const framing::SequenceSet& commands);
+
+ // Note: Following methods are called by network thread in
+ // response to execution commands from the broker
+ void sync();
+ void result(const framing::SequenceNumber& commandId, const std::string& value);
+ void exception(uint16_t errorCode,
+ const framing::SequenceNumber& commandId,
+ uint8_t classCode,
+ uint8_t commandCode,
+ uint8_t fieldIndex,
+ const std::string& description,
+ const framing::FieldTable& errorInfo);
+
+ // Note: Following methods are called by network thread in
+ // response to message commands from the broker
+ // EXCEPT Message.Transfer
+ void accept(const qpid::framing::SequenceSet&);
+ void reject(const qpid::framing::SequenceSet&, uint16_t, const std::string&);
+ void release(const qpid::framing::SequenceSet&, bool);
+ qpid::framing::MessageResumeResult resume(const std::string&, const std::string&);
+ void setFlowMode(const std::string&, uint8_t);
+ void flow(const std::string&, uint8_t, uint32_t);
+ void stop(const std::string&);
+
+
+ sys::ExceptionHolder exceptionHolder;
+ mutable StateMonitor state;
+ mutable sys::Semaphore sendLock;
+ uint32_t detachedLifetime;
+ const uint64_t maxFrameSize;
+ const SessionId id;
+
+ boost::shared_ptr<ConnectionImpl> connection;
+
+ framing::FrameHandler::MemFunRef<SessionImpl, &SessionImpl::proxyOut> ioHandler;
+ framing::ChannelHandler channel;
+ framing::AMQP_ServerProxy::Session proxy;
+
+ Results results;
+ Demux demux;
+ framing::FrameSet::shared_ptr arriving;
+
+ framing::SequenceSet incompleteIn;//incoming commands that are as yet incomplete
+ framing::SequenceSet completedIn;//incoming commands that are have completed
+ framing::SequenceSet incompleteOut;//outgoing commands not yet known to be complete
+ framing::SequenceSet completedOut;//outgoing commands that we know to be completed
+ framing::SequenceNumber nextIn;
+ framing::SequenceNumber nextOut;
+
+ SessionState sessionState;
+
+ // Only keep track of message credit
+ sys::Semaphore* sendMsgCredit;
+
+ bool doClearDeliveryPropertiesExchange;
+
+ bool autoDetach;
+
+ friend class client::SessionHandler;
+};
+
+}} // namespace qpid::client
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/SslConnector.cpp b/qpid/cpp/src/qpid/client/SslConnector.cpp
new file mode 100644
index 0000000000..f121cfb1ab
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SslConnector.cpp
@@ -0,0 +1,381 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Connector.h"
+
+#include "config.h"
+#include "qpid/client/Bounds.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/Options.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/InitiationHandler.h"
+#include "qpid/sys/ssl/util.h"
+#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/Msg.h"
+
+#include <iostream>
+#include <map>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::sys;
+using namespace qpid::sys::ssl;
+using namespace qpid::framing;
+using boost::format;
+using boost::str;
+
+
+class SslConnector : public Connector
+{
+ struct Buff;
+
+ /** Batch up frames for writing to aio. */
+ class Writer : public framing::FrameHandler {
+ typedef sys::ssl::SslIOBufferBase BufferBase;
+ typedef std::vector<framing::AMQFrame> Frames;
+
+ const uint16_t maxFrameSize;
+ sys::Mutex lock;
+ sys::ssl::SslIO* aio;
+ BufferBase* buffer;
+ Frames frames;
+ size_t lastEof; // Position after last EOF in frames
+ framing::Buffer encode;
+ size_t framesEncoded;
+ std::string identifier;
+ Bounds* bounds;
+
+ void writeOne();
+ void newBuffer();
+
+ public:
+
+ Writer(uint16_t maxFrameSize, Bounds*);
+ ~Writer();
+ void init(std::string id, sys::ssl::SslIO*);
+ void handle(framing::AMQFrame&);
+ void write(sys::ssl::SslIO&);
+ };
+
+ const uint16_t maxFrameSize;
+ framing::ProtocolVersion version;
+ bool initiated;
+ SecuritySettings securitySettings;
+
+ sys::Mutex closedLock;
+ bool closed;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ Writer writer;
+
+ sys::ssl::SslSocket socket;
+
+ sys::ssl::SslIO* aio;
+ Poller::shared_ptr poller;
+
+ ~SslConnector();
+
+ void readbuff(qpid::sys::ssl::SslIO&, qpid::sys::ssl::SslIOBufferBase*);
+ void writebuff(qpid::sys::ssl::SslIO&);
+ void writeDataBlock(const framing::AMQDataBlock& data);
+ void eof(qpid::sys::ssl::SslIO&);
+ void disconnected(qpid::sys::ssl::SslIO&);
+
+ std::string identifier;
+
+ void connect(const std::string& host, const std::string& port);
+ void init();
+ void close();
+ void send(framing::AMQFrame& frame);
+ void abort() {} // TODO: Need to fix for heartbeat timeouts to work
+
+ void setInputHandler(framing::InputHandler* handler);
+ void setShutdownHandler(sys::ShutdownHandler* handler);
+ sys::ShutdownHandler* getShutdownHandler() const;
+ framing::OutputHandler* getOutputHandler();
+ const std::string& getIdentifier() const;
+ const SecuritySettings* getSecuritySettings();
+ void socketClosed(qpid::sys::ssl::SslIO&, const qpid::sys::ssl::SslSocket&);
+
+public:
+ SslConnector(Poller::shared_ptr p, framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+};
+
+struct SslConnector::Buff : public SslIO::BufferBase {
+ Buff(size_t size) : SslIO::BufferBase(new char[size], size) {}
+ ~Buff() { delete [] bytes;}
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(Poller::shared_ptr p, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new SslConnector(p, v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ try {
+ SslOptions options;
+ options.parse (0, 0, QPIDC_CONF_FILE, true);
+ if (options.certDbPath.empty()) {
+ QPID_LOG(info, "SSL connector not enabled, you must set QPID_SSL_CERT_DB to enable it.");
+ } else {
+ initNSS(options);
+ Connector::registerFactory("ssl", &create);
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL connector: " << e.what());
+ }
+ };
+
+ ~StaticInit() { shutdownNSS(); }
+ } init;
+}
+
+SslConnector::SslConnector(Poller::shared_ptr p,
+ ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ version(ver),
+ initiated(false),
+ closed(true),
+ shutdownHandler(0),
+ writer(maxFrameSize, cimpl),
+ aio(0),
+ poller(p)
+{
+ QPID_LOG(debug, "SslConnector created for " << version.toString());
+
+ if (settings.sslCertName != "") {
+ QPID_LOG(debug, "ssl-cert-name = " << settings.sslCertName);
+ socket.setCertName(settings.sslCertName);
+ }
+}
+
+SslConnector::~SslConnector() {
+ close();
+}
+
+void SslConnector::connect(const std::string& host, const std::string& port){
+ Mutex::ScopedLock l(closedLock);
+ assert(closed);
+ try {
+ socket.connect(host, port);
+ } catch (const std::exception& e) {
+ socket.close();
+ throw ConnectionException(framing::connection::CLOSE_CODE_FRAMING_ERROR, e.what());
+ }
+
+ identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+ closed = false;
+ aio = new SslIO(socket,
+ boost::bind(&SslConnector::readbuff, this, _1, _2),
+ boost::bind(&SslConnector::eof, this, _1),
+ boost::bind(&SslConnector::disconnected, this, _1),
+ boost::bind(&SslConnector::socketClosed, this, _1, _2),
+ 0, // nobuffs
+ boost::bind(&SslConnector::writebuff, this, _1));
+ writer.init(identifier, aio);
+}
+
+void SslConnector::init(){
+ Mutex::ScopedLock l(closedLock);
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+ for (int i = 0; i < 32; i++) {
+ aio->queueReadBuffer(new Buff(maxFrameSize));
+ }
+ aio->start(poller);
+}
+
+void SslConnector::close() {
+ Mutex::ScopedLock l(closedLock);
+ if (!closed) {
+ closed = true;
+ if (aio)
+ aio->queueWriteClose();
+ }
+}
+
+void SslConnector::socketClosed(SslIO&, const SslSocket&) {
+ if (aio)
+ aio->queueForDeletion();
+ if (shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+void SslConnector::setInputHandler(InputHandler* handler){
+ input = handler;
+}
+
+void SslConnector::setShutdownHandler(ShutdownHandler* handler){
+ shutdownHandler = handler;
+}
+
+OutputHandler* SslConnector::getOutputHandler() {
+ return this;
+}
+
+sys::ShutdownHandler* SslConnector::getShutdownHandler() const {
+ return shutdownHandler;
+}
+
+const std::string& SslConnector::getIdentifier() const {
+ return identifier;
+}
+
+void SslConnector::send(AMQFrame& frame) {
+ writer.handle(frame);
+}
+
+SslConnector::Writer::Writer(uint16_t s, Bounds* b) : maxFrameSize(s), aio(0), buffer(0), lastEof(0), bounds(b)
+{
+}
+
+SslConnector::Writer::~Writer() { delete buffer; }
+
+void SslConnector::Writer::init(std::string id, sys::ssl::SslIO* a) {
+ Mutex::ScopedLock l(lock);
+ identifier = id;
+ aio = a;
+ newBuffer();
+}
+void SslConnector::Writer::handle(framing::AMQFrame& frame) {
+ Mutex::ScopedLock l(lock);
+ frames.push_back(frame);
+ if (frame.getEof() || (bounds && bounds->getCurrentSize() >= maxFrameSize)) {
+ lastEof = frames.size();
+ aio->notifyPendingWrite();
+ }
+ QPID_LOG(trace, "SENT " << identifier << ": " << frame);
+}
+
+void SslConnector::Writer::writeOne() {
+ assert(buffer);
+ framesEncoded = 0;
+
+ buffer->dataStart = 0;
+ buffer->dataCount = encode.getPosition();
+ aio->queueWrite(buffer);
+ newBuffer();
+}
+
+void SslConnector::Writer::newBuffer() {
+ buffer = aio->getQueuedBuffer();
+ if (!buffer) buffer = new Buff(maxFrameSize);
+ encode = framing::Buffer(buffer->bytes, buffer->byteCount);
+ framesEncoded = 0;
+}
+
+// Called in IO thread.
+void SslConnector::Writer::write(sys::ssl::SslIO&) {
+ Mutex::ScopedLock l(lock);
+ assert(buffer);
+ size_t bytesWritten(0);
+ for (size_t i = 0; i < lastEof; ++i) {
+ AMQFrame& frame = frames[i];
+ uint32_t size = frame.encodedSize();
+ if (size > encode.available()) writeOne();
+ assert(size <= encode.available());
+ frame.encode(encode);
+ ++framesEncoded;
+ bytesWritten += size;
+ }
+ frames.erase(frames.begin(), frames.begin()+lastEof);
+ lastEof = 0;
+ if (bounds) bounds->reduce(bytesWritten);
+ if (encode.getPosition() > 0) writeOne();
+}
+
+void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff) {
+ framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount);
+
+ if (!initiated) {
+ framing::ProtocolInitiation protocolInit;
+ if (protocolInit.decode(in)) {
+ //TODO: check the version is correct
+ QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")");
+ }
+ initiated = true;
+ }
+ AMQFrame frame;
+ while(frame.decode(in)){
+ QPID_LOG(trace, "RECV " << identifier << ": " << frame);
+ input->received(frame);
+ }
+ // TODO: unreading needs to go away, and when we can cope
+ // with multiple sub-buffers in the general buffer scheme, it will
+ if (in.available() != 0) {
+ // Adjust buffer for used bytes and then "unread them"
+ buff->dataStart += buff->dataCount-in.available();
+ buff->dataCount = in.available();
+ aio.unread(buff);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio.queueReadBuffer(buff);
+ }
+}
+
+void SslConnector::writebuff(SslIO& aio_) {
+ writer.write(aio_);
+}
+
+void SslConnector::writeDataBlock(const AMQDataBlock& data) {
+ SslIO::BufferBase* buff = new Buff(maxFrameSize);
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void SslConnector::eof(SslIO&) {
+ close();
+}
+
+void SslConnector::disconnected(SslIO&) {
+ close();
+ socketClosed(*aio, socket);
+}
+
+const SecuritySettings* SslConnector::getSecuritySettings()
+{
+ securitySettings.ssf = socket.getKeyLen();
+ securitySettings.authid = "dummy";//set to non-empty string to enable external authentication
+ return &securitySettings;
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/StateManager.cpp b/qpid/cpp/src/qpid/client/StateManager.cpp
new file mode 100644
index 0000000000..839d92abdc
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/StateManager.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/StateManager.h"
+#include "qpid/framing/amqp_framing.h"
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+StateManager::StateManager(int s) : state(s) {}
+
+void StateManager::waitForStateChange(int current)
+{
+ Monitor::ScopedLock l(stateLock);
+ while (state == current) {
+ stateLock.wait();
+ }
+}
+
+void StateManager::waitFor(int desired)
+{
+ Monitor::ScopedLock l(stateLock);
+ while (state != desired) {
+ stateLock.wait();
+ }
+}
+
+void StateManager::waitFor(std::set<int> desired)
+{
+ Monitor::ScopedLock l(stateLock);
+ while (desired.find(state) == desired.end()) {
+ stateLock.wait();
+ }
+}
+
+bool StateManager::waitFor(int desired, qpid::sys::Duration timeout)
+{
+ AbsTime end(now(), timeout);
+ Monitor::ScopedLock l(stateLock);
+ while (state != desired && now() < end) {
+ stateLock.wait(end);
+ }
+ return state == desired;
+}
+
+bool StateManager::waitFor(std::set<int> desired, qpid::sys::Duration timeout)
+{
+ AbsTime end(now(), timeout);
+ Monitor::ScopedLock l(stateLock);
+ while (desired.find(state) == desired.end() && now() < end) {
+ stateLock.wait(end);
+ }
+ return desired.find(state) != desired.end();
+}
+
+
+void StateManager::setState(int s)
+{
+ Monitor::ScopedLock l(stateLock);
+ state = s;
+ stateLock.notifyAll();
+}
+
+bool StateManager::setState(int s, int expected)
+{
+ Monitor::ScopedLock l(stateLock);
+ if (state == expected) {
+ state = s;
+ stateLock.notifyAll();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int StateManager::getState() const
+{
+ Monitor::ScopedLock l(stateLock);
+ return state;
+}
+
diff --git a/qpid/cpp/src/qpid/client/StateManager.h b/qpid/cpp/src/qpid/client/StateManager.h
new file mode 100644
index 0000000000..f06dbc493c
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/StateManager.h
@@ -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.
+ *
+ */
+#ifndef _StateManager_
+#define _StateManager_
+
+#include <set>
+#include "qpid/sys/Monitor.h"
+
+namespace qpid {
+namespace client {
+
+///@internal
+class StateManager
+{
+ int state;
+ mutable sys::Monitor stateLock;
+
+public:
+ StateManager(int initial);
+ void setState(int state);
+ bool setState(int state, int expected);
+ int getState() const ;
+ void waitForStateChange(int current);
+ void waitFor(std::set<int> states);
+ void waitFor(int state);
+ bool waitFor(std::set<int> states, qpid::sys::Duration);
+ bool waitFor(int state, qpid::sys::Duration);
+};
+
+}}
+
+#endif
diff --git a/qpid/cpp/src/qpid/client/Subscription.cpp b/qpid/cpp/src/qpid/client/Subscription.cpp
new file mode 100644
index 0000000000..988f372604
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/Subscription.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/Subscription.h"
+#include "qpid/client/SubscriptionImpl.h"
+#include "qpid/client/CompletionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/framing/enum.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<Subscription> PI;
+Subscription::Subscription(SubscriptionImpl* p) { PI::ctor(*this, p); }
+Subscription::~Subscription() { PI::dtor(*this); }
+Subscription::Subscription(const Subscription& c) : Handle<SubscriptionImpl>() { PI::copy(*this, c); }
+Subscription& Subscription::operator=(const Subscription& c) { return PI::assign(*this, c); }
+
+
+std::string Subscription::getName() const { return impl->getName(); }
+std::string Subscription::getQueue() const { return impl->getQueue(); }
+const SubscriptionSettings& Subscription::getSettings() const { return impl->getSettings(); }
+void Subscription::setFlowControl(const FlowControl& f) { impl->setFlowControl(f); }
+void Subscription::setAutoAck(unsigned int n) { impl->setAutoAck(n); }
+SequenceSet Subscription::getUnacquired() const { return impl->getUnacquired(); }
+SequenceSet Subscription::getUnaccepted() const { return impl->getUnaccepted(); }
+void Subscription::acquire(const SequenceSet& messageIds) { impl->acquire(messageIds); }
+void Subscription::accept(const SequenceSet& messageIds) { impl->accept(messageIds); }
+void Subscription::release(const SequenceSet& messageIds) { impl->release(messageIds); }
+Session Subscription::getSession() const { return impl->getSession(); }
+SubscriptionManager Subscription::getSubscriptionManager() { return impl->getSubscriptionManager(); }
+void Subscription::cancel() { impl->cancel(); }
+void Subscription::grantMessageCredit(uint32_t value) { impl->grantCredit(framing::message::CREDIT_UNIT_MESSAGE, value); }
+void Subscription::grantByteCredit(uint32_t value) { impl->grantCredit(framing::message::CREDIT_UNIT_BYTE, value); }
+}} // namespace qpid::client
+
+
diff --git a/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp b/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp
new file mode 100644
index 0000000000..a8a0b47d94
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SubscriptionImpl.cpp
@@ -0,0 +1,169 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionImpl.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/SubscriptionManagerImpl.h"
+#include "qpid/client/MessageImpl.h"
+#include "qpid/client/CompletionImpl.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/SubscriptionSettings.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+using sys::Mutex;
+using framing::MessageAcquireResult;
+
+SubscriptionImpl::SubscriptionImpl(SubscriptionManager m, const std::string& q, const SubscriptionSettings& s, const std::string& n, MessageListener* l)
+ : manager(*PrivateImplRef<SubscriptionManager>::get(m)), name(n), queue(q), settings(s), listener(l)
+{}
+
+void SubscriptionImpl::subscribe()
+{
+ async(manager.getSession()).messageSubscribe(
+ arg::queue=queue,
+ arg::destination=name,
+ arg::acceptMode=settings.acceptMode,
+ arg::acquireMode=settings.acquireMode,
+ arg::exclusive=settings.exclusive);
+ setFlowControl(settings.flowControl);
+}
+
+std::string SubscriptionImpl::getName() const { return name; }
+
+std::string SubscriptionImpl::getQueue() const { return queue; }
+
+const SubscriptionSettings& SubscriptionImpl::getSettings() const {
+ Mutex::ScopedLock l(lock);
+ return settings;
+}
+
+void SubscriptionImpl::setFlowControl(const FlowControl& f) {
+ Mutex::ScopedLock l(lock);
+ AsyncSession s=manager.getSession();
+ if (&settings.flowControl != &f) settings.flowControl = f;
+ s.messageSetFlowMode(name, f.window);
+ s.messageFlow(name, CREDIT_UNIT_MESSAGE, f.messages);
+ s.messageFlow(name, CREDIT_UNIT_BYTE, f.bytes);
+ s.sync();
+}
+
+void SubscriptionImpl::grantCredit(framing::message::CreditUnit unit, uint32_t value) {
+ async(manager.getSession()).messageFlow(name, unit, value);
+}
+
+void SubscriptionImpl::setAutoAck(size_t n) {
+ Mutex::ScopedLock l(lock);
+ settings.autoAck = n;
+}
+
+SequenceSet SubscriptionImpl::getUnacquired() const { Mutex::ScopedLock l(lock); return unacquired; }
+SequenceSet SubscriptionImpl::getUnaccepted() const { Mutex::ScopedLock l(lock); return unaccepted; }
+
+void SubscriptionImpl::acquire(const SequenceSet& messageIds) {
+ Mutex::ScopedLock l(lock);
+ MessageAcquireResult result = manager.getSession().messageAcquire(messageIds);
+ unacquired.remove(result.getTransfers());
+ if (settings.acceptMode == ACCEPT_MODE_EXPLICIT)
+ unaccepted.add(result.getTransfers());
+}
+
+void SubscriptionImpl::accept(const SequenceSet& messageIds) {
+ Mutex::ScopedLock l(lock);
+ manager.getSession().messageAccept(messageIds);
+ unaccepted.remove(messageIds);
+ switch (settings.completionMode) {
+ case COMPLETE_ON_ACCEPT:
+ manager.getSession().markCompleted(messageIds, true);
+ break;
+ case COMPLETE_ON_DELIVERY:
+ manager.getSession().sendCompletion();
+ break;
+ default://do nothing
+ break;
+ }
+}
+
+void SubscriptionImpl::release(const SequenceSet& messageIds) {
+ Mutex::ScopedLock l(lock);
+ manager.getSession().messageRelease(messageIds);
+ if (settings.acceptMode == ACCEPT_MODE_EXPLICIT)
+ unaccepted.remove(messageIds);
+}
+
+Session SubscriptionImpl::getSession() const { return manager.getSession(); }
+
+SubscriptionManager SubscriptionImpl::getSubscriptionManager() { return SubscriptionManager(&manager); }
+
+void SubscriptionImpl::cancel() { manager.cancel(name); }
+
+void SubscriptionImpl::received(Message& m) {
+ Mutex::ScopedLock l(lock);
+ MessageImpl& mi = *MessageImpl::get(m);
+ if (mi.getMethod().getAcquireMode() == ACQUIRE_MODE_NOT_ACQUIRED)
+ unacquired.add(m.getId());
+ else if (mi.getMethod().getAcceptMode() == ACCEPT_MODE_EXPLICIT)
+ unaccepted.add(m.getId());
+
+ if (listener) {
+ Mutex::ScopedUnlock u(lock);
+ listener->received(m);
+ }
+
+ if (settings.completionMode == COMPLETE_ON_DELIVERY) {
+ manager.getSession().markCompleted(m.getId(), false, false);
+ }
+ if (settings.autoAck) {
+ if (unaccepted.size() >= settings.autoAck) {
+ async(manager.getSession()).messageAccept(unaccepted);
+ switch (settings.completionMode) {
+ case COMPLETE_ON_ACCEPT:
+ manager.getSession().markCompleted(unaccepted, true);
+ break;
+ case COMPLETE_ON_DELIVERY:
+ manager.getSession().sendCompletion();
+ break;
+ default://do nothing
+ break;
+ }
+ unaccepted.clear();
+ }
+ }
+}
+
+Demux::QueuePtr SubscriptionImpl::divert()
+{
+ Session session(manager.getSession());
+ Demux& demux = SessionBase_0_10Access(session).get()->getDemux();
+ demuxRule = std::auto_ptr<ScopedDivert>(new ScopedDivert(name, demux));
+ return demuxRule->getQueue();
+}
+
+void SubscriptionImpl::cancelDiversion() {
+ demuxRule.reset();
+}
+
+}} // namespace qpid::client
+
diff --git a/qpid/cpp/src/qpid/client/SubscriptionImpl.h b/qpid/cpp/src/qpid/client/SubscriptionImpl.h
new file mode 100644
index 0000000000..da77213423
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SubscriptionImpl.h
@@ -0,0 +1,125 @@
+#ifndef QPID_CLIENT_SUBSCRIPTIONIMPL_H
+#define QPID_CLIENT_SUBSCRIPTIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/SubscriptionSettings.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/Demux.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/RefCounted.h"
+#include "qpid/client/ClientImportExport.h"
+#include <memory>
+
+namespace qpid {
+namespace client {
+
+class SubscriptionManager;
+class SubscriptionManagerImpl;
+
+class SubscriptionImpl : public RefCounted, public MessageListener {
+ public:
+ QPID_CLIENT_EXTERN SubscriptionImpl(SubscriptionManager, const std::string& queue,
+ const SubscriptionSettings&, const std::string& name, MessageListener* =0);
+
+ /** The name of the subsctription, used as the "destination" for messages from the broker.
+ * Usually the same as the queue name but can be set differently.
+ */
+ QPID_CLIENT_EXTERN std::string getName() const;
+
+ /** Name of the queue this subscription subscribes to */
+ QPID_CLIENT_EXTERN std::string getQueue() const;
+
+ /** Get the flow control and acknowledgement settings for this subscription */
+ QPID_CLIENT_EXTERN const SubscriptionSettings& getSettings() const;
+
+ /** Set the flow control parameters */
+ QPID_CLIENT_EXTERN void setFlowControl(const FlowControl&);
+
+ /** Automatically acknowledge (acquire and accept) batches of n messages.
+ * You can disable auto-acknowledgement by setting n=0, and use acquire() and accept()
+ * to manually acquire and accept messages.
+ */
+ QPID_CLIENT_EXTERN void setAutoAck(size_t n);
+
+ /** Get the set of ID's for messages received by this subscription but not yet acquired.
+ * This will always be empty if acquireMode=ACQUIRE_MODE_PRE_ACQUIRED
+ */
+ QPID_CLIENT_EXTERN SequenceSet getUnacquired() const;
+
+ /** Get the set of ID's for messages acquired by this subscription but not yet accepted. */
+ QPID_CLIENT_EXTERN SequenceSet getUnaccepted() const;
+
+ /** Acquire messageIds and remove them from the un-acquired set for the session. */
+ QPID_CLIENT_EXTERN void acquire(const SequenceSet& messageIds);
+
+ /** Accept messageIds and remove them from the un-accepted set for the session. */
+ QPID_CLIENT_EXTERN void accept(const SequenceSet& messageIds);
+
+ /** Release messageIds and remove them from the un-accepted set for the session. */
+ QPID_CLIENT_EXTERN void release(const SequenceSet& messageIds);
+
+ /** Get the session associated with this subscription */
+ QPID_CLIENT_EXTERN Session getSession() const;
+
+ /** Get the subscription manager associated with this subscription */
+ QPID_CLIENT_EXTERN SubscriptionManager getSubscriptionManager();
+
+ /** Send subscription request and issue appropriate flow control commands. */
+ QPID_CLIENT_EXTERN void subscribe();
+
+ /** Cancel the subscription. */
+ QPID_CLIENT_EXTERN void cancel();
+
+ /** Grant specified credit for this subscription **/
+ QPID_CLIENT_EXTERN void grantCredit(framing::message::CreditUnit unit, uint32_t value);
+
+ QPID_CLIENT_EXTERN void received(Message&);
+
+ /**
+ * Set up demux diversion for messages sent to this subscription
+ */
+ Demux::QueuePtr divert();
+ /**
+ * Cancel any demux diversion that may have been setup for this
+ * subscription
+ */
+ QPID_CLIENT_EXTERN void cancelDiversion();
+
+ private:
+
+ mutable sys::Mutex lock;
+ SubscriptionManagerImpl& manager;
+ std::string name, queue;
+ SubscriptionSettings settings;
+ framing::SequenceSet unacquired, unaccepted;
+ MessageListener* listener;
+ std::auto_ptr<ScopedDivert> demuxRule;
+};
+
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_SUBSCRIPTIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/SubscriptionManager.cpp b/qpid/cpp/src/qpid/client/SubscriptionManager.cpp
new file mode 100644
index 0000000000..485361d577
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SubscriptionManager.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/SubscriptionManagerImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<SubscriptionManager> PI;
+
+SubscriptionManager::SubscriptionManager(const Session& s) { PI::ctor(*this, new SubscriptionManagerImpl(s)); }
+SubscriptionManager::SubscriptionManager(SubscriptionManagerImpl* i) { PI::ctor(*this, i); }
+SubscriptionManager::SubscriptionManager(const SubscriptionManager& x) : Runnable(), Handle<SubscriptionManagerImpl>() { PI::copy(*this, x); }
+SubscriptionManager::~SubscriptionManager() { PI::dtor(*this); }
+SubscriptionManager& SubscriptionManager::operator=(const SubscriptionManager& x) { return PI::assign(*this, x); }
+
+Subscription SubscriptionManager::subscribe(
+ MessageListener& listener, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{ return impl->subscribe(listener, q, ss, n); }
+
+Subscription SubscriptionManager::subscribe(
+ LocalQueue& lq, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{ return impl->subscribe(lq, q, ss, n); }
+
+
+Subscription SubscriptionManager::subscribe(
+ MessageListener& listener, const std::string& q, const std::string& n)
+{ return impl->subscribe(listener, q, n); }
+
+
+Subscription SubscriptionManager::subscribe(
+ LocalQueue& lq, const std::string& q, const std::string& n)
+{ return impl->subscribe(lq, q, n); }
+
+void SubscriptionManager::cancel(const std::string& dest) { return impl->cancel(dest); }
+
+void SubscriptionManager::setAutoStop(bool set) { impl->setAutoStop(set); }
+
+void SubscriptionManager::run() { impl->run(); }
+
+void SubscriptionManager::start() { impl->start(); }
+
+void SubscriptionManager::wait() { impl->wait(); }
+
+void SubscriptionManager::stop() { impl->stop(); }
+
+bool SubscriptionManager::get(Message& result, const std::string& queue, sys::Duration timeout) {
+ return impl->get(result, queue, timeout);
+}
+
+Message SubscriptionManager::get(const std::string& queue, sys::Duration timeout) {
+ return impl->get(queue, timeout);
+}
+
+Session SubscriptionManager::getSession() const { return impl->getSession(); }
+
+Subscription SubscriptionManager::getSubscription(const std::string& name) const {
+ return impl->getSubscription(name);
+}
+void SubscriptionManager::registerFailoverHandler (boost::function<void ()> fh) {
+ impl->registerFailoverHandler(fh);
+}
+
+void SubscriptionManager::setFlowControl(const std::string& name, const FlowControl& flow) {
+ impl->setFlowControl(name, flow);
+}
+
+void SubscriptionManager::setDefaultSettings(const SubscriptionSettings& s){
+ impl->setDefaultSettings(s);
+}
+
+void SubscriptionManager::setFlowControl(const std::string& name, uint32_t messages, uint32_t bytes, bool window) {
+ impl->setFlowControl(name, FlowControl(messages, bytes, window));
+}
+
+void SubscriptionManager::setFlowControl(uint32_t messages, uint32_t bytes, bool window) {
+ impl->setFlowControl(messages, bytes, window);
+}
+
+void SubscriptionManager::setAcceptMode(AcceptMode mode) { impl->setAcceptMode(mode); }
+void SubscriptionManager::setAcquireMode(AcquireMode mode) { impl->setAcquireMode(mode); }
+
+}} // namespace qpid::client
+
+
diff --git a/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.cpp b/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
new file mode 100644
index 0000000000..a558d90be8
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/SubscriptionManagerImpl.h"
+#include "qpid/client/SubscriptionImpl.h"
+#include "qpid/client/LocalQueueImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+#include <qpid/client/Dispatcher.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/framing/Uuid.h>
+#include <set>
+#include <sstream>
+
+
+namespace qpid {
+namespace client {
+
+SubscriptionManagerImpl::SubscriptionManagerImpl(const Session& s)
+ : dispatcher(s), session(s), autoStop(true)
+{}
+
+Subscription SubscriptionManagerImpl::subscribe(
+ MessageListener& listener, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{
+ sys::Mutex::ScopedLock l(lock);
+ std::string name=n.empty() ? q:n;
+ boost::intrusive_ptr<SubscriptionImpl> si = new SubscriptionImpl(SubscriptionManager(this), q, ss, name, &listener);
+ dispatcher.listen(si);
+ //issue subscription request after listener is registered with dispatcher
+ si->subscribe();
+ return subscriptions[name] = Subscription(si.get());
+}
+
+Subscription SubscriptionManagerImpl::subscribe(
+ LocalQueue& lq, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{
+ sys::Mutex::ScopedLock l(lock);
+ std::string name=n.empty() ? q:n;
+ boost::intrusive_ptr<SubscriptionImpl> si = new SubscriptionImpl(SubscriptionManager(this), q, ss, name, 0);
+ boost::intrusive_ptr<LocalQueueImpl> lqi = PrivateImplRef<LocalQueue>::get(lq);
+ lqi->queue=si->divert();
+ si->subscribe();
+ lqi->subscription = Subscription(si.get());
+ return subscriptions[name] = lqi->subscription;
+}
+
+Subscription SubscriptionManagerImpl::subscribe(
+ MessageListener& listener, const std::string& q, const std::string& n)
+{
+ return subscribe(listener, q, defaultSettings, n);
+}
+
+Subscription SubscriptionManagerImpl::subscribe(
+ LocalQueue& lq, const std::string& q, const std::string& n)
+{
+ return subscribe(lq, q, defaultSettings, n);
+}
+
+void SubscriptionManagerImpl::cancel(const std::string& dest)
+{
+ sys::Mutex::ScopedLock l(lock);
+ std::map<std::string, Subscription>::iterator i = subscriptions.find(dest);
+ if (i != subscriptions.end()) {
+ sync(session).messageCancel(dest);
+ dispatcher.cancel(dest);
+ Subscription s = i->second;
+ if (s.isValid())
+ PrivateImplRef<Subscription>::get(s)->cancelDiversion();
+ subscriptions.erase(i);
+ }
+}
+
+void SubscriptionManagerImpl::setAutoStop(bool set) { autoStop=set; }
+
+void SubscriptionManagerImpl::run()
+{
+ dispatcher.setAutoStop(autoStop);
+ dispatcher.run();
+}
+
+void SubscriptionManagerImpl::start()
+{
+ dispatcher.setAutoStop(autoStop);
+ dispatcher.start();
+}
+
+void SubscriptionManagerImpl::wait()
+{
+ dispatcher.wait();
+}
+
+void SubscriptionManagerImpl::stop()
+{
+ dispatcher.stop();
+}
+
+bool SubscriptionManagerImpl::get(Message& result, const std::string& queue, sys::Duration timeout) {
+ LocalQueue lq;
+ std::string unique = framing::Uuid(true).str();
+ subscribe(lq, queue, SubscriptionSettings(FlowControl::messageCredit(1)), unique);
+ SubscriptionManager sm(this);
+ AutoCancel ac(sm, unique);
+ //first wait for message to be delivered if a timeout has been specified
+ if (timeout && lq.get(result, timeout))
+ return true;
+ //make sure message is not on queue before final check
+ sync(session).messageFlush(unique);
+ return lq.get(result, 0);
+}
+
+Message SubscriptionManagerImpl::get(const std::string& queue, sys::Duration timeout) {
+ Message result;
+ if (!get(result, queue, timeout))
+ throw Exception("Timed out waiting for a message");
+ return result;
+}
+
+Session SubscriptionManagerImpl::getSession() const { return session; }
+
+Subscription SubscriptionManagerImpl::getSubscription(const std::string& name) const {
+ sys::Mutex::ScopedLock l(lock);
+ std::map<std::string, Subscription>::const_iterator i = subscriptions.find(name);
+ if (i == subscriptions.end())
+ throw Exception(QPID_MSG("Subscription not found: " << name));
+ return i->second;
+}
+
+void SubscriptionManagerImpl::registerFailoverHandler (boost::function<void ()> fh) {
+ dispatcher.registerFailoverHandler(fh);
+}
+
+void SubscriptionManagerImpl::setFlowControl(const std::string& name, const FlowControl& flow) {
+ getSubscription(name).setFlowControl(flow);
+}
+
+void SubscriptionManagerImpl::setFlowControl(const std::string& name, uint32_t messages, uint32_t bytes, bool window) {
+ setFlowControl(name, FlowControl(messages, bytes, window));
+}
+
+}} // namespace qpid::client
+
+
diff --git a/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.h b/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.h
new file mode 100644
index 0000000000..6376a05c45
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/SubscriptionManagerImpl.h
@@ -0,0 +1,278 @@
+#ifndef QPID_CLIENT_SUBSCRIPTIONMANAGERIMPL_H
+#define QPID_CLIENT_SUBSCRIPTIONMANAGERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Mutex.h"
+#include <qpid/client/Dispatcher.h>
+#include <qpid/client/Completion.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/client/LocalQueue.h>
+#include <qpid/client/Subscription.h>
+#include <qpid/sys/Runnable.h>
+#include <qpid/RefCounted.h>
+#include <set>
+#include <sstream>
+
+namespace qpid {
+namespace client {
+
+/**
+ * A class to help create and manage subscriptions.
+ *
+ * Set up your subscriptions, then call run() to have messages
+ * delivered.
+ *
+ * \ingroup clientapi
+ *
+ * \details
+ *
+ * <h2>Subscribing and canceling subscriptions</h2>
+ *
+ * <ul>
+ * <li>
+ * <p>subscribe()</p>
+ * <pre> SubscriptionManager subscriptions(session);
+ * Listener listener(subscriptions);
+ * subscriptions.subscribe(listener, myQueue);</pre>
+ * <pre> SubscriptionManager subscriptions(session);
+ * LocalQueue local_queue;
+ * subscriptions.subscribe(local_queue, string("message_queue"));</pre></li>
+ * <li>
+ * <p>cancel()</p>
+ * <pre>subscriptions.cancel();</pre></li>
+ * </ul>
+ *
+ * <h2>Waiting for messages (and returning)</h2>
+ *
+ * <ul>
+ * <li>
+ * <p>run()</p>
+ * <pre> // Give up control to receive messages
+ * subscriptions.run();</pre></li>
+ * <li>
+ * <p>stop()</p>
+ * <pre>.// Use this code in a listener to return from run()
+ * subscriptions.stop();</pre></li>
+ * <li>
+ * <p>setAutoStop()</p>
+ * <pre>.// Return from subscriptions.run() when last subscription is cancelled
+ *.subscriptions.setAutoStop(true);
+ *.subscriptons.run();
+ * </pre></li>
+ * <li>
+ * <p>Ending a subscription in a listener</p>
+ * <pre>
+ * void Listener::received(Message&amp; message) {
+ *
+ * if (message.getData() == "That's all, folks!") {
+ * subscriptions.cancel(message.getDestination());
+ * }
+ * }
+ * </pre>
+ * </li>
+ * </ul>
+ *
+ */
+class SubscriptionManagerImpl : public sys::Runnable, public RefCounted
+{
+ public:
+ /** Create a new SubscriptionManagerImpl associated with a session */
+ SubscriptionManagerImpl(const Session& session);
+
+ /**
+ * Subscribe a MessagesListener to receive messages from queue.
+ *
+ * Provide your own subclass of MessagesListener to process
+ * incoming messages. It will be called for each message received.
+ *
+ *@param listener Listener object to receive messages.
+ *@param queue Name of the queue to subscribe to.
+ *@param settings settings for the subscription.
+ *@param name unique destination name for the subscription, defaults to queue name.
+ */
+ Subscription subscribe(MessageListener& listener,
+ const std::string& queue,
+ const SubscriptionSettings& settings,
+ const std::string& name=std::string());
+
+ /**
+ * Subscribe a LocalQueue to receive messages from queue.
+ *
+ * Incoming messages are stored in the queue for you to retrieve.
+ *
+ *@param queue Name of the queue to subscribe to.
+ *@param flow initial FlowControl for the subscription.
+ *@param name unique destination name for the subscription, defaults to queue name.
+ * If not specified, the queue name is used.
+ */
+ Subscription subscribe(LocalQueue& localQueue,
+ const std::string& queue,
+ const SubscriptionSettings& settings,
+ const std::string& name=std::string());
+
+ /**
+ * Subscribe a MessagesListener to receive messages from queue.
+ *
+ * Provide your own subclass of MessagesListener to process
+ * incoming messages. It will be called for each message received.
+ *
+ *@param listener Listener object to receive messages.
+ *@param queue Name of the queue to subscribe to.
+ *@param name unique destination name for the subscription, defaults to queue name.
+ * If not specified, the queue name is used.
+ */
+ Subscription subscribe(MessageListener& listener,
+ const std::string& queue,
+ const std::string& name=std::string());
+
+ /**
+ * Subscribe a LocalQueue to receive messages from queue.
+ *
+ * Incoming messages are stored in the queue for you to retrieve.
+ *
+ *@param queue Name of the queue to subscribe to.
+ *@param name unique destination name for the subscription, defaults to queue name.
+ * If not specified, the queue name is used.
+ */
+ Subscription subscribe(LocalQueue& localQueue,
+ const std::string& queue,
+ const std::string& name=std::string());
+
+
+ /** Get a single message from a queue.
+ *@param result is set to the message from the queue.
+ *@param timeout wait up this timeout for a message to appear.
+ *@return true if result was set, false if no message available after timeout.
+ */
+ bool get(Message& result, const std::string& queue, sys::Duration timeout=0);
+
+ /** Get a single message from a queue.
+ *@param timeout wait up this timeout for a message to appear.
+ *@return message from the queue.
+ *@throw Exception if the timeout is exceeded.
+ */
+ Message get(const std::string& queue, sys::Duration timeout=sys::TIME_INFINITE);
+
+ /** Get a subscription by name.
+ *@throw Exception if not found.
+ */
+ Subscription getSubscription(const std::string& name) const;
+
+ /** Cancel a subscription. See also: Subscription.cancel() */
+ void cancel(const std::string& name);
+
+ /** Deliver messages in the current thread until stop() is called.
+ * Only one thread may be running in a SubscriptionManager at a time.
+ * @see run
+ */
+ void run();
+
+ /** Start a new thread to deliver messages.
+ * Only one thread may be running in a SubscriptionManager at a time.
+ * @see start
+ */
+ void start();
+
+ /**
+ * Wait for the thread started by a call to start() to complete.
+ */
+ void wait();
+
+ /** If set true, run() will stop when all subscriptions
+ * are cancelled. If false, run will only stop when stop()
+ * is called. True by default.
+ */
+ void setAutoStop(bool set=true);
+
+ /** Stop delivery. Causes run() to return, or the thread started with start() to exit. */
+ void stop();
+
+ static const uint32_t UNLIMITED=0xFFFFFFFF;
+
+ /** Set the flow control for a subscription. */
+ void setFlowControl(const std::string& name, const FlowControl& flow);
+
+ /** Set the flow control for a subscription.
+ *@param name: name of the subscription.
+ *@param messages: message credit.
+ *@param bytes: byte credit.
+ *@param window: if true use window-based flow control.
+ */
+ void setFlowControl(const std::string& name, uint32_t messages, uint32_t bytes, bool window=true);
+
+ /** Set the default settings for subscribe() calls that don't
+ * include a SubscriptionSettings parameter.
+ */
+ void setDefaultSettings(const SubscriptionSettings& s) { defaultSettings = s; }
+
+ /** Get the default settings for subscribe() calls that don't
+ * include a SubscriptionSettings parameter.
+ */
+ const SubscriptionSettings& getDefaultSettings() const { return defaultSettings; }
+
+ /** Get the default settings for subscribe() calls that don't
+ * include a SubscriptionSettings parameter.
+ */
+ SubscriptionSettings& getDefaultSettings() { return defaultSettings; }
+
+ /**
+ * Set the default flow control settings for subscribe() calls
+ * that don't include a SubscriptionSettings parameter.
+ *
+ *@param messages: message credit.
+ *@param bytes: byte credit.
+ *@param window: if true use window-based flow control.
+ */
+ void setFlowControl(uint32_t messages, uint32_t bytes, bool window=true) {
+ defaultSettings.flowControl = FlowControl(messages, bytes, window);
+ }
+
+ /**
+ *Set the default accept-mode for subscribe() calls that don't
+ *include a SubscriptionSettings parameter.
+ */
+ void setAcceptMode(AcceptMode mode) { defaultSettings.acceptMode = mode; }
+
+ /**
+ * Set the default acquire-mode subscribe()s that don't specify SubscriptionSettings.
+ */
+ void setAcquireMode(AcquireMode mode) { defaultSettings.acquireMode = mode; }
+
+ void registerFailoverHandler ( boost::function<void ()> fh );
+
+ Session getSession() const;
+
+ private:
+ mutable sys::Mutex lock;
+ qpid::client::Dispatcher dispatcher;
+ qpid::client::AsyncSession session;
+ bool autoStop;
+ SubscriptionSettings defaultSettings;
+ std::map<std::string, Subscription> subscriptions;
+};
+
+
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_SUBSCRIPTIONMANAGERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/TCPConnector.cpp b/qpid/cpp/src/qpid/client/TCPConnector.cpp
new file mode 100644
index 0000000000..0070b24ec0
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/TCPConnector.cpp
@@ -0,0 +1,331 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/TCPConnector.h"
+
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Codec.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/InitiationHandler.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/Msg.h"
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace client {
+
+using namespace qpid::sys;
+using namespace qpid::framing;
+using boost::format;
+using boost::str;
+
+struct TCPConnector::Buff : public AsynchIO::BufferBase {
+ Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {}
+ ~Buff() { delete [] bytes;}
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(Poller::shared_ptr p, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new TCPConnector(p, v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ Connector::registerFactory("tcp", &create);
+ };
+ } init;
+}
+
+TCPConnector::TCPConnector(Poller::shared_ptr p,
+ ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ lastEof(0),
+ currentSize(0),
+ bounds(cimpl),
+ version(ver),
+ initiated(false),
+ closed(true),
+ shutdownHandler(0),
+ connector(0),
+ aio(0),
+ poller(p)
+{
+ QPID_LOG(debug, "TCPConnector created for " << version);
+ settings.configureSocket(socket);
+}
+
+TCPConnector::~TCPConnector() {
+ close();
+}
+
+void TCPConnector::connect(const std::string& host, const std::string& port) {
+ Mutex::ScopedLock l(lock);
+ assert(closed);
+ connector = AsynchConnector::create(
+ socket,
+ host, port,
+ boost::bind(&TCPConnector::connected, this, _1),
+ boost::bind(&TCPConnector::connectFailed, this, _3));
+ closed = false;
+
+ connector->start(poller);
+}
+
+void TCPConnector::connected(const Socket&) {
+ connector = 0;
+ aio = AsynchIO::create(socket,
+ boost::bind(&TCPConnector::readbuff, this, _1, _2),
+ boost::bind(&TCPConnector::eof, this, _1),
+ boost::bind(&TCPConnector::disconnected, this, _1),
+ boost::bind(&TCPConnector::socketClosed, this, _1, _2),
+ 0, // nobuffs
+ boost::bind(&TCPConnector::writebuff, this, _1));
+ start(aio);
+ initAmqp();
+ aio->start(poller);
+}
+
+void TCPConnector::start(sys::AsynchIO* aio_) {
+ aio = aio_;
+ for (int i = 0; i < 4; i++) {
+ aio->queueReadBuffer(new Buff(maxFrameSize));
+ }
+
+ identifier = str(format("[%1%]") % socket.getFullAddress());
+}
+
+void TCPConnector::initAmqp() {
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+}
+
+void TCPConnector::connectFailed(const std::string& msg) {
+ connector = 0;
+ QPID_LOG(warning, "Connect failed: " << msg);
+ socket.close();
+ if (!closed)
+ closed = true;
+ if (shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+void TCPConnector::close() {
+ Mutex::ScopedLock l(lock);
+ if (!closed) {
+ closed = true;
+ if (aio)
+ aio->queueWriteClose();
+ }
+}
+
+void TCPConnector::socketClosed(AsynchIO&, const Socket&) {
+ if (aio)
+ aio->queueForDeletion();
+ if (shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+void TCPConnector::abort() {
+ // Can't abort a closed connection
+ if (!closed) {
+ if (aio) {
+ // Established connection
+ aio->requestCallback(boost::bind(&TCPConnector::eof, this, _1));
+ } else if (connector) {
+ // We're still connecting
+ connector->stop();
+ connectFailed("Connection timedout");
+ }
+ }
+}
+
+void TCPConnector::setInputHandler(InputHandler* handler){
+ input = handler;
+}
+
+void TCPConnector::setShutdownHandler(ShutdownHandler* handler){
+ shutdownHandler = handler;
+}
+
+OutputHandler* TCPConnector::getOutputHandler() {
+ return this;
+}
+
+sys::ShutdownHandler* TCPConnector::getShutdownHandler() const {
+ return shutdownHandler;
+}
+
+const std::string& TCPConnector::getIdentifier() const {
+ return identifier;
+}
+
+void TCPConnector::send(AMQFrame& frame) {
+ bool notifyWrite = false;
+ {
+ Mutex::ScopedLock l(lock);
+ frames.push_back(frame);
+ //only ask to write if this is the end of a frameset or if we
+ //already have a buffers worth of data
+ currentSize += frame.encodedSize();
+ if (frame.getEof()) {
+ lastEof = frames.size();
+ notifyWrite = true;
+ } else {
+ notifyWrite = (currentSize >= maxFrameSize);
+ }
+ /*
+ NOTE: Moving the following line into this mutex block
+ is a workaround for BZ 570168, in which the test
+ testConcurrentSenders causes a hang about 1.5%
+ of the time. ( To see the hang much more frequently
+ leave this line out of the mutex block, and put a
+ small usleep just before it.)
+
+ TODO mgoulish - fix the underlying cause and then
+ move this call back outside the mutex.
+ */
+ if (notifyWrite && !closed) aio->notifyPendingWrite();
+ }
+}
+
+void TCPConnector::writebuff(AsynchIO& /*aio*/)
+{
+ // It's possible to be disconnected and be writable
+ if (closed)
+ return;
+
+ Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
+ if (codec->canEncode()) {
+ std::auto_ptr<AsynchIO::BufferBase> buffer = std::auto_ptr<AsynchIO::BufferBase>(aio->getQueuedBuffer());
+ if (!buffer.get()) buffer = std::auto_ptr<AsynchIO::BufferBase>(new Buff(maxFrameSize));
+
+ size_t encoded = codec->encode(buffer->bytes, buffer->byteCount);
+
+ buffer->dataStart = 0;
+ buffer->dataCount = encoded;
+ aio->queueWrite(buffer.release());
+ }
+}
+
+// Called in IO thread.
+bool TCPConnector::canEncode()
+{
+ Mutex::ScopedLock l(lock);
+ //have at least one full frameset or a whole buffers worth of data
+ return lastEof || currentSize >= maxFrameSize;
+}
+
+// Called in IO thread.
+size_t TCPConnector::encode(const char* buffer, size_t size)
+{
+ framing::Buffer out(const_cast<char*>(buffer), size);
+ size_t bytesWritten(0);
+ {
+ Mutex::ScopedLock l(lock);
+ while (!frames.empty() && out.available() >= frames.front().encodedSize() ) {
+ frames.front().encode(out);
+ QPID_LOG(trace, "SENT " << identifier << ": " << frames.front());
+ frames.pop_front();
+ if (lastEof) --lastEof;
+ }
+ bytesWritten = size - out.available();
+ currentSize -= bytesWritten;
+ }
+ if (bounds) bounds->reduce(bytesWritten);
+ return bytesWritten;
+}
+
+bool TCPConnector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff)
+{
+ Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
+ int32_t decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount);
+ // TODO: unreading needs to go away, and when we can cope
+ // with multiple sub-buffers in the general buffer scheme, it will
+ if (decoded < buff->dataCount) {
+ // Adjust buffer for used bytes and then "unread them"
+ buff->dataStart += decoded;
+ buff->dataCount -= decoded;
+ aio.unread(buff);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio.queueReadBuffer(buff);
+ }
+ return true;
+}
+
+size_t TCPConnector::decode(const char* buffer, size_t size)
+{
+ framing::Buffer in(const_cast<char*>(buffer), size);
+ if (!initiated) {
+ framing::ProtocolInitiation protocolInit;
+ if (protocolInit.decode(in)) {
+ QPID_LOG(debug, "RECV " << identifier << " INIT(" << protocolInit << ")");
+ if(!(protocolInit==version)){
+ throw Exception(QPID_MSG("Unsupported version: " << protocolInit
+ << " supported version " << version));
+ }
+ }
+ initiated = true;
+ }
+ AMQFrame frame;
+ while(frame.decode(in)){
+ QPID_LOG(trace, "RECV " << identifier << ": " << frame);
+ input->received(frame);
+ }
+ return size - in.available();
+}
+
+void TCPConnector::writeDataBlock(const AMQDataBlock& data) {
+ AsynchIO::BufferBase* buff = aio->getQueuedBuffer();
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void TCPConnector::eof(AsynchIO&) {
+ close();
+}
+
+void TCPConnector::disconnected(AsynchIO&) {
+ close();
+ socketClosed(*aio, socket);
+}
+
+void TCPConnector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer> sl)
+{
+ securityLayer = sl;
+ securityLayer->init(this);
+}
+
+}} // namespace qpid::client
diff --git a/qpid/cpp/src/qpid/client/TCPConnector.h b/qpid/cpp/src/qpid/client/TCPConnector.h
new file mode 100644
index 0000000000..eb3f696013
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/TCPConnector.h
@@ -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.
+ *
+ */
+
+#ifndef _TCPConnector_
+#define _TCPConnector_
+
+#include "Connector.h"
+#include "qpid/client/Bounds.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Codec.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Thread.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <deque>
+#include <string>
+
+namespace qpid {
+
+namespace framing {
+ class InitiationHandler;
+}
+
+namespace client {
+
+class TCPConnector : public Connector, public sys::Codec
+{
+ typedef std::deque<framing::AMQFrame> Frames;
+ struct Buff;
+
+ const uint16_t maxFrameSize;
+
+ sys::Mutex lock;
+ Frames frames; // Outgoing frame queue
+ size_t lastEof; // Position after last EOF in frames
+ uint64_t currentSize;
+ Bounds* bounds;
+
+ framing::ProtocolVersion version;
+ bool initiated;
+ bool closed;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ sys::Socket socket;
+
+ sys::AsynchConnector* connector;
+ sys::AsynchIO* aio;
+ std::string identifier;
+ boost::shared_ptr<sys::Poller> poller;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+
+ virtual void connected(const sys::Socket&);
+ void writeDataBlock(const framing::AMQDataBlock& data);
+
+ void close();
+ void send(framing::AMQFrame& frame);
+ void abort();
+
+ void setInputHandler(framing::InputHandler* handler);
+ void setShutdownHandler(sys::ShutdownHandler* handler);
+ sys::ShutdownHandler* getShutdownHandler() const;
+ framing::OutputHandler* getOutputHandler();
+ const std::string& getIdentifier() const;
+ void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>);
+ const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; }
+
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+
+protected:
+ virtual ~TCPConnector();
+ void connect(const std::string& host, const std::string& port);
+ void start(sys::AsynchIO* aio_);
+ void initAmqp();
+ virtual void connectFailed(const std::string& msg);
+ bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void writebuff(qpid::sys::AsynchIO&);
+ void eof(qpid::sys::AsynchIO&);
+ void disconnected(qpid::sys::AsynchIO&);
+ void socketClosed(qpid::sys::AsynchIO&, const qpid::sys::Socket&);
+
+public:
+ TCPConnector(boost::shared_ptr<sys::Poller>,
+ framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+};
+
+}} // namespace qpid::client
+
+#endif /* _TCPConnector_ */
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
new file mode 100644
index 0000000000..bfb20118b5
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
@@ -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.
+ *
+ */
+#include "AcceptTracker.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+void AcceptTracker::State::accept()
+{
+ unconfirmed.add(unaccepted);
+ unaccepted.clear();
+}
+
+void AcceptTracker::State::accept(qpid::framing::SequenceNumber id)
+{
+ if (unaccepted.contains(id)) {
+ unaccepted.remove(id);
+ unconfirmed.add(id);
+ }
+}
+
+void AcceptTracker::State::release()
+{
+ unaccepted.clear();
+}
+
+uint32_t AcceptTracker::State::acceptsPending()
+{
+ return unconfirmed.size();
+}
+
+void AcceptTracker::State::completed(qpid::framing::SequenceSet& set)
+{
+ unconfirmed.remove(set);
+}
+
+void AcceptTracker::delivered(const std::string& destination, const qpid::framing::SequenceNumber& id)
+{
+ aggregateState.unaccepted.add(id);
+ destinationState[destination].unaccepted.add(id);
+}
+
+void AcceptTracker::accept(qpid::client::AsyncSession& session)
+{
+ for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
+ i->second.accept();
+ }
+ Record record;
+ record.status = session.messageAccept(aggregateState.unaccepted);
+ record.accepted = aggregateState.unaccepted;
+ pending.push_back(record);
+ aggregateState.accept();
+}
+
+void AcceptTracker::accept(qpid::framing::SequenceNumber id, qpid::client::AsyncSession& session)
+{
+ for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
+ i->second.accept(id);
+ }
+ Record record;
+ record.accepted.add(id);
+ record.status = session.messageAccept(record.accepted);
+ pending.push_back(record);
+ aggregateState.accept(id);
+}
+
+void AcceptTracker::release(qpid::client::AsyncSession& session)
+{
+ session.messageRelease(aggregateState.unaccepted);
+ for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
+ i->second.release();
+ }
+ aggregateState.release();
+}
+
+uint32_t AcceptTracker::acceptsPending()
+{
+ checkPending();
+ return aggregateState.acceptsPending();
+}
+
+uint32_t AcceptTracker::acceptsPending(const std::string& destination)
+{
+ checkPending();
+ return destinationState[destination].acceptsPending();
+}
+
+void AcceptTracker::reset()
+{
+ destinationState.clear();
+ aggregateState.unaccepted.clear();
+ aggregateState.unconfirmed.clear();
+ pending.clear();
+}
+
+void AcceptTracker::checkPending()
+{
+ while (!pending.empty() && pending.front().status.isComplete()) {
+ completed(pending.front().accepted);
+ pending.pop_front();
+ }
+}
+
+void AcceptTracker::completed(qpid::framing::SequenceSet& set)
+{
+ for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
+ i->second.completed(set);
+ }
+ aggregateState.completed(set);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
new file mode 100644
index 0000000000..87890e41cc
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
@@ -0,0 +1,87 @@
+#ifndef QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H
+#define QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/Completion.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceSet.h"
+#include <deque>
+#include <map>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Tracks the set of messages requiring acceptance, and those for
+ * which an accept has been issued but is yet to be confirmed
+ * complete.
+ */
+class AcceptTracker
+{
+ public:
+ void delivered(const std::string& destination, const qpid::framing::SequenceNumber& id);
+ void accept(qpid::client::AsyncSession&);
+ void accept(qpid::framing::SequenceNumber, qpid::client::AsyncSession&);
+ void release(qpid::client::AsyncSession&);
+ uint32_t acceptsPending();
+ uint32_t acceptsPending(const std::string& destination);
+ void reset();
+ private:
+ struct State
+ {
+ /**
+ * ids of messages that have been delivered but not yet
+ * accepted
+ */
+ qpid::framing::SequenceSet unaccepted;
+ /**
+ * ids of messages for which an accept has been issued but not
+ * yet confirmed as completed
+ */
+ qpid::framing::SequenceSet unconfirmed;
+
+ void accept();
+ void accept(qpid::framing::SequenceNumber);
+ void release();
+ uint32_t acceptsPending();
+ void completed(qpid::framing::SequenceSet&);
+ };
+ typedef std::map<std::string, State> StateMap;
+ struct Record
+ {
+ qpid::client::Completion status;
+ qpid::framing::SequenceSet accepted;
+ };
+ typedef std::deque<Record> Records;
+
+ State aggregateState;
+ StateMap destinationState;
+ Records pending;
+
+ void checkPending();
+ void completed(qpid::framing::SequenceSet&);
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_ACCEPTTRACKER_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
new file mode 100644
index 0000000000..f1295a3b66
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -0,0 +1,966 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/client/amqp0_10/MessageSource.h"
+#include "qpid/client/amqp0_10/MessageSink.h"
+#include "qpid/client/amqp0_10/OutgoingMessage.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/types/Variant.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/ExchangeBoundResult.h"
+#include "qpid/framing/ExchangeQueryResult.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/QueueQueryResult.h"
+#include "qpid/framing/ReplyTo.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::Exception;
+using qpid::messaging::Address;
+using qpid::messaging::AddressError;
+using qpid::messaging::MalformedAddress;
+using qpid::messaging::ResolutionError;
+using qpid::messaging::NotFound;
+using qpid::messaging::AssertionFailed;
+using qpid::framing::ExchangeBoundResult;
+using qpid::framing::ExchangeQueryResult;
+using qpid::framing::FieldTable;
+using qpid::framing::QueueQueryResult;
+using qpid::framing::ReplyTo;
+using qpid::framing::Uuid;
+using namespace qpid::types;
+using namespace qpid::framing::message;
+using namespace qpid::amqp_0_10;
+using namespace boost::assign;
+
+class Verifier
+{
+ public:
+ Verifier();
+ void verify(const Address& address) const;
+ private:
+ Variant::Map defined;
+ void verify(const Variant::Map& allowed, const Variant::Map& actual) const;
+};
+
+namespace{
+const Variant EMPTY_VARIANT;
+const FieldTable EMPTY_FIELD_TABLE;
+const Variant::List EMPTY_LIST;
+const std::string EMPTY_STRING;
+
+//policy types
+const std::string CREATE("create");
+const std::string ASSERT("assert");
+const std::string DELETE("delete");
+
+//option names
+const std::string NODE("node");
+const std::string LINK("link");
+const std::string MODE("mode");
+const std::string RELIABILITY("reliability");
+const std::string NAME("name");
+const std::string DURABLE("durable");
+const std::string X_DECLARE("x-declare");
+const std::string X_SUBSCRIBE("x-subscribe");
+const std::string X_BINDINGS("x-bindings");
+const std::string EXCHANGE("exchange");
+const std::string QUEUE("queue");
+const std::string KEY("key");
+const std::string ARGUMENTS("arguments");
+const std::string ALTERNATE_EXCHANGE("alternate-exchange");
+const std::string TYPE("type");
+const std::string EXCLUSIVE("exclusive");
+const std::string AUTO_DELETE("auto-delete");
+
+//policy values
+const std::string ALWAYS("always");
+const std::string NEVER("never");
+const std::string RECEIVER("receiver");
+const std::string SENDER("sender");
+
+//address types
+const std::string QUEUE_ADDRESS("queue");
+const std::string TOPIC_ADDRESS("topic");
+
+//reliability options:
+const std::string UNRELIABLE("unreliable");
+const std::string AT_MOST_ONCE("at-most-once");
+const std::string AT_LEAST_ONCE("at-least-once");
+const std::string EXACTLY_ONCE("exactly-once");
+
+//receiver modes:
+const std::string BROWSE("browse");
+const std::string CONSUME("consume");
+
+//0-10 exchange types:
+const std::string TOPIC_EXCHANGE("topic");
+const std::string FANOUT_EXCHANGE("fanout");
+const std::string DIRECT_EXCHANGE("direct");
+const std::string HEADERS_EXCHANGE("headers");
+const std::string XML_EXCHANGE("xml");
+const std::string WILDCARD_ANY("#");
+
+const Verifier verifier;
+}
+
+struct Binding
+{
+ Binding(const Variant::Map&);
+ Binding(const std::string& exchange, const std::string& queue, const std::string& key);
+
+ std::string exchange;
+ std::string queue;
+ std::string key;
+ FieldTable arguments;
+};
+
+struct Bindings : std::vector<Binding>
+{
+ void add(const Variant::List& bindings);
+ void setDefaultExchange(const std::string&);
+ void setDefaultQueue(const std::string&);
+ void bind(qpid::client::AsyncSession& session);
+ void unbind(qpid::client::AsyncSession& session);
+ void check(qpid::client::AsyncSession& session);
+};
+
+class Node
+{
+ protected:
+ enum CheckMode {FOR_RECEIVER, FOR_SENDER};
+
+ Node(const Address& address);
+
+ const std::string name;
+ Variant createPolicy;
+ Variant assertPolicy;
+ Variant deletePolicy;
+ Bindings nodeBindings;
+ Bindings linkBindings;
+
+ static bool enabled(const Variant& policy, CheckMode mode);
+ static bool createEnabled(const Address& address, CheckMode mode);
+ static void convert(const Variant& option, FieldTable& arguments);
+ static std::vector<std::string> RECEIVER_MODES;
+ static std::vector<std::string> SENDER_MODES;
+};
+
+
+class Queue : protected Node
+{
+ public:
+ Queue(const Address& address);
+ protected:
+ void checkCreate(qpid::client::AsyncSession&, CheckMode);
+ void checkAssert(qpid::client::AsyncSession&, CheckMode);
+ void checkDelete(qpid::client::AsyncSession&, CheckMode);
+ private:
+ const bool durable;
+ const bool autoDelete;
+ const bool exclusive;
+ const std::string alternateExchange;
+ FieldTable arguments;
+};
+
+class Exchange : protected Node
+{
+ public:
+ Exchange(const Address& address);
+ protected:
+ void checkCreate(qpid::client::AsyncSession&, CheckMode);
+ void checkAssert(qpid::client::AsyncSession&, CheckMode);
+ void checkDelete(qpid::client::AsyncSession&, CheckMode);
+
+ protected:
+ const std::string specifiedType;
+ private:
+ const bool durable;
+ const bool autoDelete;
+ const std::string alternateExchange;
+ FieldTable arguments;
+};
+
+class QueueSource : public Queue, public MessageSource
+{
+ public:
+ QueueSource(const Address& address);
+ void subscribe(qpid::client::AsyncSession& session, const std::string& destination);
+ void cancel(qpid::client::AsyncSession& session, const std::string& destination);
+ private:
+ const AcceptMode acceptMode;
+ const AcquireMode acquireMode;
+ bool exclusive;
+ FieldTable options;
+};
+
+class Subscription : public Exchange, public MessageSource
+{
+ public:
+ Subscription(const Address&, const std::string& actualType);
+ void subscribe(qpid::client::AsyncSession& session, const std::string& destination);
+ void cancel(qpid::client::AsyncSession& session, const std::string& destination);
+ private:
+ const std::string queue;
+ const bool reliable;
+ const bool durable;
+ const std::string actualType;
+ FieldTable queueOptions;
+ FieldTable subscriptionOptions;
+ Bindings bindings;
+
+ void bindSubject(const std::string& subject);
+ void bindAll();
+ void add(const std::string& exchange, const std::string& key);
+ static std::string getSubscriptionName(const std::string& base, const std::string& name);
+};
+
+class ExchangeSink : public Exchange, public MessageSink
+{
+ public:
+ ExchangeSink(const Address& name);
+ void declare(qpid::client::AsyncSession& session, const std::string& name);
+ void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message);
+ void cancel(qpid::client::AsyncSession& session, const std::string& name);
+ private:
+};
+
+class QueueSink : public Queue, public MessageSink
+{
+ public:
+ QueueSink(const Address& name);
+ void declare(qpid::client::AsyncSession& session, const std::string& name);
+ void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message);
+ void cancel(qpid::client::AsyncSession& session, const std::string& name);
+ private:
+};
+bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address);
+bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address);
+
+bool in(const Variant& value, const std::vector<std::string>& choices)
+{
+ if (!value.isVoid()) {
+ for (std::vector<std::string>::const_iterator i = choices.begin(); i != choices.end(); ++i) {
+ if (value.asString() == *i) return true;
+ }
+ }
+ return false;
+}
+
+const Variant& getOption(const Variant::Map& options, const std::string& name)
+{
+ Variant::Map::const_iterator j = options.find(name);
+ if (j == options.end()) {
+ return EMPTY_VARIANT;
+ } else {
+ return j->second;
+ }
+}
+
+const Variant& getOption(const Address& address, const std::string& name)
+{
+ return getOption(address.getOptions(), name);
+}
+
+bool getReceiverPolicy(const Address& address, const std::string& key)
+{
+ return in(getOption(address, key), list_of<std::string>(ALWAYS)(RECEIVER));
+}
+
+bool getSenderPolicy(const Address& address, const std::string& key)
+{
+ return in(getOption(address, key), list_of<std::string>(ALWAYS)(SENDER));
+}
+
+struct Opt
+{
+ Opt(const Address& address);
+ Opt(const Variant::Map& base);
+ Opt& operator/(const std::string& name);
+ operator bool() const;
+ std::string str() const;
+ const Variant::List& asList() const;
+ void collect(qpid::framing::FieldTable& args) const;
+
+ const Variant::Map* options;
+ const Variant* value;
+};
+
+Opt::Opt(const Address& address) : options(&(address.getOptions())), value(0) {}
+Opt::Opt(const Variant::Map& base) : options(&base), value(0) {}
+Opt& Opt::operator/(const std::string& name)
+{
+ if (options) {
+ Variant::Map::const_iterator j = options->find(name);
+ if (j == options->end()) {
+ value = 0;
+ options = 0;
+ } else {
+ value = &(j->second);
+ if (value->getType() == VAR_MAP) options = &(value->asMap());
+ else options = 0;
+ }
+ }
+ return *this;
+}
+
+
+Opt::operator bool() const
+{
+ return value && !value->isVoid() && value->asBool();
+}
+
+std::string Opt::str() const
+{
+ if (value) return value->asString();
+ else return EMPTY_STRING;
+}
+
+const Variant::List& Opt::asList() const
+{
+ if (value) return value->asList();
+ else return EMPTY_LIST;
+}
+
+void Opt::collect(qpid::framing::FieldTable& args) const
+{
+ if (value) {
+ translate(value->asMap(), args);
+ }
+}
+
+bool AddressResolution::is_unreliable(const Address& address)
+{
+
+ return in((Opt(address)/LINK/RELIABILITY).str(),
+ list_of<std::string>(UNRELIABLE)(AT_MOST_ONCE));
+}
+
+bool AddressResolution::is_reliable(const Address& address)
+{
+ return in((Opt(address)/LINK/RELIABILITY).str(),
+ list_of<std::string>(AT_LEAST_ONCE)(EXACTLY_ONCE));
+}
+
+std::string checkAddressType(qpid::client::Session session, const Address& address)
+{
+ verifier.verify(address);
+ if (address.getName().empty()) {
+ throw MalformedAddress("Name cannot be null");
+ }
+ std::string type = (Opt(address)/NODE/TYPE).str();
+ if (type.empty()) {
+ ExchangeBoundResult result = session.exchangeBound(arg::exchange=address.getName(), arg::queue=address.getName());
+ if (result.getQueueNotFound() && result.getExchangeNotFound()) {
+ //neither a queue nor an exchange exists with that name; treat it as a queue
+ type = QUEUE_ADDRESS;
+ } else if (result.getExchangeNotFound()) {
+ //name refers to a queue
+ type = QUEUE_ADDRESS;
+ } else if (result.getQueueNotFound()) {
+ //name refers to an exchange
+ type = TOPIC_ADDRESS;
+ } else {
+ //both a queue and exchange exist for that name
+ throw ResolutionError("Ambiguous address, please specify queue or topic as node type");
+ }
+ }
+ return type;
+}
+
+std::auto_ptr<MessageSource> AddressResolution::resolveSource(qpid::client::Session session,
+ const Address& address)
+{
+ std::string type = checkAddressType(session, address);
+ if (type == TOPIC_ADDRESS) {
+ std::string exchangeType = sync(session).exchangeQuery(address.getName()).getType();
+ std::auto_ptr<MessageSource> source(new Subscription(address, exchangeType));
+ QPID_LOG(debug, "treating source address as topic: " << address);
+ return source;
+ } else if (type == QUEUE_ADDRESS) {
+ std::auto_ptr<MessageSource> source(new QueueSource(address));
+ QPID_LOG(debug, "treating source address as queue: " << address);
+ return source;
+ } else {
+ throw ResolutionError("Unrecognised type: " + type);
+ }
+}
+
+
+std::auto_ptr<MessageSink> AddressResolution::resolveSink(qpid::client::Session session,
+ const qpid::messaging::Address& address)
+{
+ std::string type = checkAddressType(session, address);
+ if (type == TOPIC_ADDRESS) {
+ std::auto_ptr<MessageSink> sink(new ExchangeSink(address));
+ QPID_LOG(debug, "treating target address as topic: " << address);
+ return sink;
+ } else if (type == QUEUE_ADDRESS) {
+ std::auto_ptr<MessageSink> sink(new QueueSink(address));
+ QPID_LOG(debug, "treating target address as queue: " << address);
+ return sink;
+ } else {
+ throw ResolutionError("Unrecognised type: " + type);
+ }
+}
+
+bool isBrowse(const Address& address)
+{
+ const Variant& mode = getOption(address, MODE);
+ if (!mode.isVoid()) {
+ std::string value = mode.asString();
+ if (value == BROWSE) return true;
+ else if (value != CONSUME) throw ResolutionError("Invalid mode");
+ }
+ return false;
+}
+
+QueueSource::QueueSource(const Address& address) :
+ Queue(address),
+ acceptMode(AddressResolution::is_unreliable(address) ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT),
+ acquireMode(isBrowse(address) ? ACQUIRE_MODE_NOT_ACQUIRED : ACQUIRE_MODE_PRE_ACQUIRED),
+ exclusive(false)
+{
+ //extract subscription arguments from address options (nb: setting
+ //of accept-mode/acquire-mode/destination controlled though other
+ //options)
+ exclusive = Opt(address)/LINK/X_SUBSCRIBE/EXCLUSIVE;
+ (Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(options);
+}
+
+void QueueSource::subscribe(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ checkCreate(session, FOR_RECEIVER);
+ checkAssert(session, FOR_RECEIVER);
+ linkBindings.bind(session);
+ session.messageSubscribe(arg::queue=name,
+ arg::destination=destination,
+ arg::acceptMode=acceptMode,
+ arg::acquireMode=acquireMode,
+ arg::exclusive=exclusive,
+ arg::arguments=options);
+}
+
+void QueueSource::cancel(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ linkBindings.unbind(session);
+ session.messageCancel(destination);
+ checkDelete(session, FOR_RECEIVER);
+}
+
+std::string Subscription::getSubscriptionName(const std::string& base, const std::string& name)
+{
+ if (name.empty()) {
+ return (boost::format("%1%_%2%") % base % Uuid(true).str()).str();
+ } else {
+ return (boost::format("%1%_%2%") % base % name).str();
+ }
+}
+
+Subscription::Subscription(const Address& address, const std::string& type)
+ : Exchange(address),
+ queue(getSubscriptionName(name, (Opt(address)/LINK/NAME).str())),
+ reliable(AddressResolution::is_reliable(address)),
+ durable(Opt(address)/LINK/DURABLE),
+ actualType(type.empty() ? (specifiedType.empty() ? TOPIC_EXCHANGE : specifiedType) : type)
+{
+ (Opt(address)/LINK/X_DECLARE/ARGUMENTS).collect(queueOptions);
+ (Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(subscriptionOptions);
+
+ if (!address.getSubject().empty()) bindSubject(address.getSubject());
+ else if (linkBindings.empty()) bindAll();
+}
+
+void Subscription::bindSubject(const std::string& subject)
+{
+ if (actualType == HEADERS_EXCHANGE) {
+ Binding b(name, queue, subject);
+ b.arguments.setString("qpid.subject", subject);
+ b.arguments.setString("x-match", "all");
+ bindings.push_back(b);
+ } else if (actualType == XML_EXCHANGE) {
+ Binding b(name, queue, subject);
+ std::string query = (boost::format("declare variable $qpid.subject external; $qpid.subject = '%1%'")
+ % subject).str();
+ b.arguments.setString("xquery", query);
+ bindings.push_back(b);
+ } else {
+ //Note: the fanout exchange doesn't support any filtering, so
+ //the subject is ignored in that case
+ add(name, subject);
+ }
+}
+
+void Subscription::bindAll()
+{
+ if (actualType == TOPIC_EXCHANGE) {
+ add(name, WILDCARD_ANY);
+ } else if (actualType == FANOUT_EXCHANGE) {
+ add(name, queue);
+ } else if (actualType == HEADERS_EXCHANGE) {
+ Binding b(name, queue, "match-all");
+ b.arguments.setString("x-match", "all");
+ bindings.push_back(b);
+ } else if (actualType == XML_EXCHANGE) {
+ Binding b(name, queue, EMPTY_STRING);
+ b.arguments.setString("xquery", "true()");
+ bindings.push_back(b);
+ } else {
+ add(name, EMPTY_STRING);
+ }
+}
+
+void Subscription::add(const std::string& exchange, const std::string& key)
+{
+ bindings.push_back(Binding(exchange, queue, key));
+}
+
+void Subscription::subscribe(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ //create exchange if required and specified by policy:
+ checkCreate(session, FOR_RECEIVER);
+ checkAssert(session, FOR_RECEIVER);
+
+ //create subscription queue:
+ session.queueDeclare(arg::queue=queue, arg::exclusive=true,
+ arg::autoDelete=!reliable, arg::durable=durable, arg::arguments=queueOptions);
+ //'default' binding:
+ bindings.bind(session);
+ //any explicit bindings:
+ linkBindings.setDefaultQueue(queue);
+ linkBindings.bind(session);
+ //subscribe to subscription queue:
+ AcceptMode accept = reliable ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE;
+ session.messageSubscribe(arg::queue=queue, arg::destination=destination,
+ arg::exclusive=true, arg::acceptMode=accept, arg::arguments=subscriptionOptions);
+}
+
+void Subscription::cancel(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ linkBindings.unbind(session);
+ session.messageCancel(destination);
+ session.queueDelete(arg::queue=queue);
+ checkDelete(session, FOR_RECEIVER);
+}
+
+ExchangeSink::ExchangeSink(const Address& address) : Exchange(address) {}
+
+void ExchangeSink::declare(qpid::client::AsyncSession& session, const std::string&)
+{
+ checkCreate(session, FOR_SENDER);
+ checkAssert(session, FOR_SENDER);
+ linkBindings.bind(session);
+}
+
+void ExchangeSink::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m)
+{
+ m.message.getDeliveryProperties().setRoutingKey(m.getSubject());
+ m.status = session.messageTransfer(arg::destination=name, arg::content=m.message);
+}
+
+void ExchangeSink::cancel(qpid::client::AsyncSession& session, const std::string&)
+{
+ linkBindings.unbind(session);
+ checkDelete(session, FOR_SENDER);
+}
+
+QueueSink::QueueSink(const Address& address) : Queue(address) {}
+
+void QueueSink::declare(qpid::client::AsyncSession& session, const std::string&)
+{
+ checkCreate(session, FOR_SENDER);
+ checkAssert(session, FOR_SENDER);
+ linkBindings.bind(session);
+}
+void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, OutgoingMessage& m)
+{
+ m.message.getDeliveryProperties().setRoutingKey(name);
+ m.status = session.messageTransfer(arg::content=m.message);
+}
+
+void QueueSink::cancel(qpid::client::AsyncSession& session, const std::string&)
+{
+ linkBindings.unbind(session);
+ checkDelete(session, FOR_SENDER);
+}
+
+Address AddressResolution::convert(const qpid::framing::ReplyTo& rt)
+{
+ Address address;
+ if (rt.getExchange().empty()) {//if default exchange, treat as queue
+ address.setName(rt.getRoutingKey());
+ address.setType(QUEUE_ADDRESS);
+ } else {
+ address.setName(rt.getExchange());
+ address.setSubject(rt.getRoutingKey());
+ address.setType(TOPIC_ADDRESS);
+ }
+ return address;
+}
+
+qpid::framing::ReplyTo AddressResolution::convert(const Address& address)
+{
+ if (address.getType() == QUEUE_ADDRESS || address.getType().empty()) {
+ return ReplyTo(EMPTY_STRING, address.getName());
+ } else if (address.getType() == TOPIC_ADDRESS) {
+ return ReplyTo(address.getName(), address.getSubject());
+ } else {
+ QPID_LOG(notice, "Unrecognised type for reply-to: " << address.getType());
+ return ReplyTo(EMPTY_STRING, address.getName());//treat as queue
+ }
+}
+
+bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address)
+{
+ return address.getType() == QUEUE_ADDRESS ||
+ (address.getType().empty() && session.queueQuery(address.getName()).getQueue() == address.getName());
+}
+
+bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address)
+{
+ if (address.getType().empty()) {
+ return !session.exchangeQuery(address.getName()).getNotFound();
+ } else if (address.getType() == TOPIC_ADDRESS) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Node::Node(const Address& address) : name(address.getName()),
+ createPolicy(getOption(address, CREATE)),
+ assertPolicy(getOption(address, ASSERT)),
+ deletePolicy(getOption(address, DELETE))
+{
+ nodeBindings.add((Opt(address)/NODE/X_BINDINGS).asList());
+ linkBindings.add((Opt(address)/LINK/X_BINDINGS).asList());
+}
+
+Queue::Queue(const Address& a) : Node(a),
+ durable(Opt(a)/NODE/DURABLE),
+ autoDelete(Opt(a)/NODE/X_DECLARE/AUTO_DELETE),
+ exclusive(Opt(a)/NODE/X_DECLARE/EXCLUSIVE),
+ alternateExchange((Opt(a)/NODE/X_DECLARE/ALTERNATE_EXCHANGE).str())
+{
+ (Opt(a)/NODE/X_DECLARE/ARGUMENTS).collect(arguments);
+ nodeBindings.setDefaultQueue(name);
+ linkBindings.setDefaultQueue(name);
+}
+
+void Queue::checkCreate(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(createPolicy, mode)) {
+ QPID_LOG(debug, "Auto-creating queue '" << name << "'");
+ try {
+ session.queueDeclare(arg::queue=name,
+ arg::durable=durable,
+ arg::autoDelete=autoDelete,
+ arg::exclusive=exclusive,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=arguments);
+ nodeBindings.bind(session);
+ session.sync();
+ } catch (const qpid::framing::ResourceLockedException& e) {
+ throw ResolutionError((boost::format("Creation failed for queue %1%; %2%") % name % e.what()).str());
+ } catch (const qpid::framing::NotAllowedException& e) {
+ throw ResolutionError((boost::format("Creation failed for queue %1%; %2%") % name % e.what()).str());
+ } catch (const qpid::framing::NotFoundException& e) {//may be thrown when creating bindings
+ throw ResolutionError((boost::format("Creation failed for queue %1%; %2%") % name % e.what()).str());
+ }
+ } else {
+ try {
+ sync(session).queueDeclare(arg::queue=name, arg::passive=true);
+ } catch (const qpid::framing::NotFoundException& /*e*/) {
+ throw NotFound((boost::format("Queue %1% does not exist") % name).str());
+ }
+ }
+}
+
+void Queue::checkDelete(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ //Note: queue-delete will cause a session exception if the queue
+ //does not exist, the query here prevents obvious cases of this
+ //but there is a race whenever two deletions are made concurrently
+ //so careful use of the delete policy is recommended at present
+ if (enabled(deletePolicy, mode) && sync(session).queueQuery(name).getQueue() == name) {
+ QPID_LOG(debug, "Auto-deleting queue '" << name << "'");
+ sync(session).queueDelete(arg::queue=name);
+ }
+}
+
+void Queue::checkAssert(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(assertPolicy, mode)) {
+ QueueQueryResult result = sync(session).queueQuery(name);
+ if (result.getQueue() != name) {
+ throw NotFound((boost::format("Queue not found: %1%") % name).str());
+ } else {
+ if (durable && !result.getDurable()) {
+ throw AssertionFailed((boost::format("Queue not durable: %1%") % name).str());
+ }
+ if (autoDelete && !result.getAutoDelete()) {
+ throw AssertionFailed((boost::format("Queue not set to auto-delete: %1%") % name).str());
+ }
+ if (exclusive && !result.getExclusive()) {
+ throw AssertionFailed((boost::format("Queue not exclusive: %1%") % name).str());
+ }
+ if (!alternateExchange.empty() && result.getAlternateExchange() != alternateExchange) {
+ throw AssertionFailed((boost::format("Alternate exchange does not match for %1%, expected %2%, got %3%")
+ % name % alternateExchange % result.getAlternateExchange()).str());
+ }
+ for (FieldTable::ValueMap::const_iterator i = arguments.begin(); i != arguments.end(); ++i) {
+ FieldTable::ValuePtr v = result.getArguments().get(i->first);
+ if (!v) {
+ throw AssertionFailed((boost::format("Option %1% not set for %2%") % i->first % name).str());
+ } else if (*i->second != *v) {
+ throw AssertionFailed((boost::format("Option %1% does not match for %2%, expected %3%, got %4%")
+ % i->first % name % *(i->second) % *v).str());
+ }
+ }
+ nodeBindings.check(session);
+ }
+ }
+}
+
+Exchange::Exchange(const Address& a) : Node(a),
+ specifiedType((Opt(a)/NODE/X_DECLARE/TYPE).str()),
+ durable(Opt(a)/NODE/DURABLE),
+ autoDelete(Opt(a)/NODE/X_DECLARE/AUTO_DELETE),
+ alternateExchange((Opt(a)/NODE/X_DECLARE/ALTERNATE_EXCHANGE).str())
+{
+ (Opt(a)/NODE/X_DECLARE/ARGUMENTS).collect(arguments);
+ nodeBindings.setDefaultExchange(name);
+ linkBindings.setDefaultExchange(name);
+}
+
+void Exchange::checkCreate(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(createPolicy, mode)) {
+ try {
+ std::string type = specifiedType;
+ if (type.empty()) type = TOPIC_EXCHANGE;
+ session.exchangeDeclare(arg::exchange=name,
+ arg::type=type,
+ arg::durable=durable,
+ arg::autoDelete=autoDelete,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=arguments);
+ nodeBindings.bind(session);
+ session.sync();
+ } catch (const qpid::framing::NotAllowedException& e) {
+ throw ResolutionError((boost::format("Create failed for exchange %1%; %2%") % name % e.what()).str());
+ } catch (const qpid::framing::NotFoundException& e) {//can be caused when creating bindings
+ throw ResolutionError((boost::format("Create failed for exchange %1%; %2%") % name % e.what()).str());
+ }
+ } else {
+ try {
+ sync(session).exchangeDeclare(arg::exchange=name, arg::passive=true);
+ } catch (const qpid::framing::NotFoundException& /*e*/) {
+ throw NotFound((boost::format("Exchange %1% does not exist") % name).str());
+ }
+ }
+}
+
+void Exchange::checkDelete(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ //Note: exchange-delete will cause a session exception if the
+ //exchange does not exist, the query here prevents obvious cases
+ //of this but there is a race whenever two deletions are made
+ //concurrently so careful use of the delete policy is recommended
+ //at present
+ if (enabled(deletePolicy, mode) && !sync(session).exchangeQuery(name).getNotFound()) {
+ sync(session).exchangeDelete(arg::exchange=name);
+ }
+}
+
+void Exchange::checkAssert(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(assertPolicy, mode)) {
+ ExchangeQueryResult result = sync(session).exchangeQuery(name);
+ if (result.getNotFound()) {
+ throw NotFound((boost::format("Exchange not found: %1%") % name).str());
+ } else {
+ if (specifiedType.size() && result.getType() != specifiedType) {
+ throw AssertionFailed((boost::format("Exchange %1% is of incorrect type, expected %2% but got %3%")
+ % name % specifiedType % result.getType()).str());
+ }
+ if (durable && !result.getDurable()) {
+ throw AssertionFailed((boost::format("Exchange not durable: %1%") % name).str());
+ }
+ //Note: Can't check auto-delete or alternate-exchange via
+ //exchange-query-result as these are not returned
+ //TODO: could use a passive declare to check alternate-exchange
+ for (FieldTable::ValueMap::const_iterator i = arguments.begin(); i != arguments.end(); ++i) {
+ FieldTable::ValuePtr v = result.getArguments().get(i->first);
+ if (!v) {
+ throw AssertionFailed((boost::format("Option %1% not set for %2%") % i->first % name).str());
+ } else if (i->second != v) {
+ throw AssertionFailed((boost::format("Option %1% does not match for %2%, expected %3%, got %4%")
+ % i->first % name % *(i->second) % *v).str());
+ }
+ }
+ nodeBindings.check(session);
+ }
+ }
+}
+
+Binding::Binding(const Variant::Map& b) :
+ exchange((Opt(b)/EXCHANGE).str()),
+ queue((Opt(b)/QUEUE).str()),
+ key((Opt(b)/KEY).str())
+{
+ (Opt(b)/ARGUMENTS).collect(arguments);
+}
+
+Binding::Binding(const std::string& e, const std::string& q, const std::string& k) : exchange(e), queue(q), key(k) {}
+
+
+void Bindings::add(const Variant::List& list)
+{
+ for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ push_back(Binding(i->asMap()));
+ }
+}
+
+void Bindings::setDefaultExchange(const std::string& exchange)
+{
+ for (Bindings::iterator i = begin(); i != end(); ++i) {
+ if (i->exchange.empty()) i->exchange = exchange;
+ }
+}
+
+void Bindings::setDefaultQueue(const std::string& queue)
+{
+ for (Bindings::iterator i = begin(); i != end(); ++i) {
+ if (i->queue.empty()) i->queue = queue;
+ }
+}
+
+void Bindings::bind(qpid::client::AsyncSession& session)
+{
+ for (Bindings::const_iterator i = begin(); i != end(); ++i) {
+ session.exchangeBind(arg::queue=i->queue,
+ arg::exchange=i->exchange,
+ arg::bindingKey=i->key,
+ arg::arguments=i->arguments);
+ }
+}
+
+void Bindings::unbind(qpid::client::AsyncSession& session)
+{
+ for (Bindings::const_iterator i = begin(); i != end(); ++i) {
+ session.exchangeUnbind(arg::queue=i->queue,
+ arg::exchange=i->exchange,
+ arg::bindingKey=i->key);
+ }
+}
+
+void Bindings::check(qpid::client::AsyncSession& session)
+{
+ for (Bindings::const_iterator i = begin(); i != end(); ++i) {
+ ExchangeBoundResult result = sync(session).exchangeBound(arg::queue=i->queue,
+ arg::exchange=i->exchange,
+ arg::bindingKey=i->key);
+ if (result.getQueueNotMatched() || result.getKeyNotMatched()) {
+ throw AssertionFailed((boost::format("No such binding [exchange=%1%, queue=%2%, key=%3%]")
+ % i->exchange % i->queue % i->key).str());
+ }
+ }
+}
+
+bool Node::enabled(const Variant& policy, CheckMode mode)
+{
+ bool result = false;
+ switch (mode) {
+ case FOR_RECEIVER:
+ result = in(policy, RECEIVER_MODES);
+ break;
+ case FOR_SENDER:
+ result = in(policy, SENDER_MODES);
+ break;
+ }
+ return result;
+}
+
+bool Node::createEnabled(const Address& address, CheckMode mode)
+{
+ const Variant& policy = getOption(address, CREATE);
+ return enabled(policy, mode);
+}
+
+void Node::convert(const Variant& options, FieldTable& arguments)
+{
+ if (!options.isVoid()) {
+ translate(options.asMap(), arguments);
+ }
+}
+std::vector<std::string> Node::RECEIVER_MODES = list_of<std::string>(ALWAYS) (RECEIVER);
+std::vector<std::string> Node::SENDER_MODES = list_of<std::string>(ALWAYS) (SENDER);
+
+Verifier::Verifier()
+{
+ defined[CREATE] = true;
+ defined[ASSERT] = true;
+ defined[DELETE] = true;
+ defined[MODE] = true;
+ Variant::Map node;
+ node[TYPE] = true;
+ node[DURABLE] = true;
+ node[X_DECLARE] = true;
+ node[X_BINDINGS] = true;
+ defined[NODE] = node;
+ Variant::Map link;
+ link[NAME] = true;
+ link[DURABLE] = true;
+ link[RELIABILITY] = true;
+ link[X_SUBSCRIBE] = true;
+ link[X_DECLARE] = true;
+ link[X_BINDINGS] = true;
+ defined[LINK] = link;
+}
+void Verifier::verify(const Address& address) const
+{
+ verify(defined, address.getOptions());
+}
+
+void Verifier::verify(const Variant::Map& allowed, const Variant::Map& actual) const
+{
+ for (Variant::Map::const_iterator i = actual.begin(); i != actual.end(); ++i) {
+ Variant::Map::const_iterator option = allowed.find(i->first);
+ if (option == allowed.end()) {
+ throw AddressError((boost::format("Unrecognised option: %1%") % i->first).str());
+ } else if (option->second.getType() == qpid::types::VAR_MAP) {
+ verify(option->second.asMap(), i->second.asMap());
+ }
+ }
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h
new file mode 100644
index 0000000000..fc8f1a1d18
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h
@@ -0,0 +1,64 @@
+#ifndef QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H
+#define QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Session.h"
+
+namespace qpid {
+
+namespace framing{
+class ReplyTo;
+}
+
+namespace messaging {
+class Address;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+class MessageSource;
+class MessageSink;
+
+/**
+ * Maps from a generic Address and optional Filter to an AMQP 0-10
+ * MessageSource which will then be used by a ReceiverImpl instance
+ * created for the address.
+ */
+class AddressResolution
+{
+ public:
+ std::auto_ptr<MessageSource> resolveSource(qpid::client::Session session,
+ const qpid::messaging::Address& address);
+
+ std::auto_ptr<MessageSink> resolveSink(qpid::client::Session session,
+ const qpid::messaging::Address& address);
+
+ static qpid::messaging::Address convert(const qpid::framing::ReplyTo&);
+ static qpid::framing::ReplyTo convert(const qpid::messaging::Address&);
+ static bool is_unreliable(const qpid::messaging::Address& address);
+ static bool is_reliable(const qpid::messaging::Address& address);
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
new file mode 100644
index 0000000000..a87a8dea67
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
@@ -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.
+ *
+ */
+#include "ConnectionImpl.h"
+#include "SessionImpl.h"
+#include "SimpleUrlParser.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/PrivateImplRef.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Url.h"
+#include <boost/intrusive_ptr.hpp>
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::types::Variant;
+using qpid::types::VAR_LIST;
+using qpid::framing::Uuid;
+
+namespace {
+void convert(const Variant::List& from, std::vector<std::string>& to)
+{
+ for (Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) {
+ to.push_back(i->asString());
+ }
+}
+
+std::string asString(const std::vector<std::string>& v) {
+ std::stringstream os;
+ os << "[";
+ for(std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i ) {
+ if (i != v.begin()) os << ", ";
+ os << *i;
+ }
+ os << "]";
+ return os.str();
+}
+}
+
+ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options) :
+ reconnect(false), timeout(-1), limit(-1),
+ minReconnectInterval(3), maxReconnectInterval(60),
+ retries(0), reconnectOnLimitExceeded(true)
+{
+ setOptions(options);
+ urls.insert(urls.begin(), url);
+ QPID_LOG(debug, "Created connection " << url << " with " << options);
+}
+
+void ConnectionImpl::setOptions(const Variant::Map& options)
+{
+ for (Variant::Map::const_iterator i = options.begin(); i != options.end(); ++i) {
+ setOption(i->first, i->second);
+ }
+}
+
+void ConnectionImpl::setOption(const std::string& name, const Variant& value)
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (name == "reconnect") {
+ reconnect = value;
+ } else if (name == "reconnect-timeout" || name == "reconnect_timeout") {
+ timeout = value;
+ } else if (name == "reconnect-limit" || name == "reconnect_limit") {
+ limit = value;
+ } else if (name == "reconnect-interval" || name == "reconnect_interval") {
+ maxReconnectInterval = minReconnectInterval = value;
+ } else if (name == "reconnect-interval-min" || name == "reconnect_interval_min") {
+ minReconnectInterval = value;
+ } else if (name == "reconnect-interval-max" || name == "reconnect_interval_max") {
+ maxReconnectInterval = value;
+ } else if (name == "reconnect-urls" || name == "reconnect_urls") {
+ if (value.getType() == VAR_LIST) {
+ convert(value.asList(), urls);
+ } else {
+ urls.push_back(value.asString());
+ }
+ } else if (name == "username") {
+ settings.username = value.asString();
+ } else if (name == "password") {
+ settings.password = value.asString();
+ } else if (name == "sasl-mechanism" || name == "sasl_mechanism" ||
+ name == "sasl-mechanisms" || name == "sasl_mechanisms") {
+ settings.mechanism = value.asString();
+ } else if (name == "sasl-service" || name == "sasl_service") {
+ settings.service = value.asString();
+ } else if (name == "sasl-min-ssf" || name == "sasl_min_ssf") {
+ settings.minSsf = value;
+ } else if (name == "sasl-max-ssf" || name == "sasl_max_ssf") {
+ settings.maxSsf = value;
+ } else if (name == "heartbeat") {
+ settings.heartbeat = value;
+ } else if (name == "tcp-nodelay" || name == "tcp_nodelay") {
+ settings.tcpNoDelay = value;
+ } else if (name == "locale") {
+ settings.locale = value.asString();
+ } else if (name == "max-channels" || name == "max_channels") {
+ settings.maxChannels = value;
+ } else if (name == "max-frame-size" || name == "max_frame_size") {
+ settings.maxFrameSize = value;
+ } else if (name == "bounds") {
+ settings.bounds = value;
+ } else if (name == "transport") {
+ settings.protocol = value.asString();
+ } else if (name == "ssl-cert-name" || name == "ssl_cert_name") {
+ settings.sslCertName = value.asString();
+ } else {
+ throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised"));
+ }
+}
+
+
+void ConnectionImpl::close()
+{
+ while(true) {
+ messaging::Session session;
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (sessions.empty()) break;
+ session = sessions.begin()->second;
+ }
+ session.close();
+ }
+ detach();
+}
+
+void ConnectionImpl::detach()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ connection.close();
+}
+
+bool ConnectionImpl::isOpen() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return connection.isOpen();
+}
+
+boost::intrusive_ptr<SessionImpl> getImplPtr(qpid::messaging::Session& session)
+{
+ return boost::dynamic_pointer_cast<SessionImpl>(
+ qpid::messaging::PrivateImplRef<qpid::messaging::Session>::get(session)
+ );
+}
+
+void ConnectionImpl::closed(SessionImpl& s)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) {
+ if (getImplPtr(i->second).get() == &s) {
+ sessions.erase(i);
+ break;
+ }
+ }
+}
+
+qpid::messaging::Session ConnectionImpl::getSession(const std::string& name) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ Sessions::const_iterator i = sessions.find(name);
+ if (i == sessions.end()) {
+ throw qpid::messaging::KeyError("No such session: " + name);
+ } else {
+ return i->second;
+ }
+}
+
+qpid::messaging::Session ConnectionImpl::newSession(bool transactional, const std::string& n)
+{
+ std::string name = n.empty() ? Uuid(true).str() : n;
+ qpid::messaging::Session impl(new SessionImpl(*this, transactional));
+ while (true) {
+ try {
+ getImplPtr(impl)->setSession(connection.newSession(name));
+ qpid::sys::Mutex::ScopedLock l(lock);
+ sessions[name] = impl;
+ break;
+ } catch (const qpid::TransportFailure&) {
+ open();
+ } catch (const qpid::SessionException& e) {
+ throw qpid::messaging::SessionError(e.what());
+ } catch (const std::exception& e) {
+ throw qpid::messaging::MessagingException(e.what());
+ }
+ }
+ return impl;
+}
+
+void ConnectionImpl::open()
+{
+ qpid::sys::AbsTime start = qpid::sys::now();
+ qpid::sys::ScopedLock<qpid::sys::Semaphore> l(semaphore);
+ try {
+ if (!connection.isOpen()) connect(start);
+ }
+ catch (const types::Exception&) { throw; }
+ catch (const qpid::Exception& e) { throw messaging::ConnectionError(e.what()); }
+}
+
+bool expired(const qpid::sys::AbsTime& start, int64_t timeout)
+{
+ if (timeout == 0) return true;
+ if (timeout < 0) return false;
+ qpid::sys::Duration used(start, qpid::sys::now());
+ qpid::sys::Duration allowed = timeout * qpid::sys::TIME_SEC;
+ return allowed < used;
+}
+
+void ConnectionImpl::connect(const qpid::sys::AbsTime& started)
+{
+ for (int64_t i = minReconnectInterval; !tryConnect(); i = std::min(i * 2, maxReconnectInterval)) {
+ if (!reconnect) {
+ throw qpid::messaging::TransportFailure("Failed to connect (reconnect disabled)");
+ }
+ if (limit >= 0 && retries++ >= limit) {
+ throw qpid::messaging::TransportFailure("Failed to connect within reconnect limit");
+ }
+ if (expired(started, timeout)) {
+ throw qpid::messaging::TransportFailure("Failed to connect within reconnect timeout");
+ }
+ else qpid::sys::sleep(i);
+ }
+ retries = 0;
+}
+
+void ConnectionImpl::mergeUrls(const std::vector<Url>& more, const sys::Mutex::ScopedLock&) {
+ if (more.size()) {
+ for (size_t i = 0; i < more.size(); ++i) {
+ if (std::find(urls.begin(), urls.end(), more[i].str()) == urls.end()) {
+ urls.push_back(more[i].str());
+ }
+ }
+ QPID_LOG(debug, "Added known-hosts, reconnect-urls=" << asString(urls));
+ }
+}
+
+bool ConnectionImpl::tryConnect()
+{
+ sys::Mutex::ScopedLock l(lock);
+ for (std::vector<std::string>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
+ try {
+ QPID_LOG(info, "Trying to connect to " << *i << "...");
+ //TODO: when url support is more complete can avoid this test here
+ if (i->find("amqp:") == 0) {
+ Url url(*i);
+ connection.open(url, settings);
+ } else {
+ SimpleUrlParser::parse(*i, settings);
+ connection.open(settings);
+ }
+ QPID_LOG(info, "Connected to " << *i);
+ mergeUrls(connection.getInitialBrokers(), l);
+ return resetSessions(l);
+ } catch (const qpid::ConnectionException& e) {
+ //TODO: need to fix timeout on
+ //qpid::client::Connection::open() so that it throws
+ //TransportFailure rather than a ConnectionException
+ QPID_LOG(info, "Failed to connect to " << *i << ": " << e.what());
+ }
+ }
+ return false;
+}
+
+bool ConnectionImpl::resetSessions(const sys::Mutex::ScopedLock& )
+{
+ try {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) {
+ getImplPtr(i->second)->setSession(connection.newSession(i->first));
+ }
+ return true;
+ } catch (const qpid::TransportFailure&) {
+ QPID_LOG(debug, "Connection failed while re-initialising sessions");
+ return false;
+ } catch (const qpid::framing::ResourceLimitExceededException& e) {
+ if (reconnectOnLimitExceeded) {
+ QPID_LOG(debug, "Detaching and reconnecting due to: " << e.what());
+ detach();
+ return false;
+ } else {
+ throw qpid::messaging::TargetCapacityExceeded(e.what());
+ }
+ }
+}
+
+bool ConnectionImpl::backoff()
+{
+ if (reconnectOnLimitExceeded) {
+ detach();
+ open();
+ return true;
+ } else {
+ return false;
+ }
+}
+std::string ConnectionImpl::getAuthenticatedUsername()
+{
+ return connection.getNegotiatedSettings().username;
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
new file mode 100644
index 0000000000..09f2038312
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
@@ -0,0 +1,80 @@
+#ifndef QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H
+#define QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/types/Variant.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Semaphore.h"
+#include <map>
+#include <vector>
+
+namespace qpid {
+struct Url;
+
+namespace client {
+namespace amqp0_10 {
+
+class SessionImpl;
+
+class ConnectionImpl : public qpid::messaging::ConnectionImpl
+{
+ public:
+ ConnectionImpl(const std::string& url, const qpid::types::Variant::Map& options);
+ void open();
+ bool isOpen() const;
+ void close();
+ qpid::messaging::Session newSession(bool transactional, const std::string& name);
+ qpid::messaging::Session getSession(const std::string& name) const;
+ void closed(SessionImpl&);
+ void detach();
+ void setOption(const std::string& name, const qpid::types::Variant& value);
+ bool backoff();
+ std::string getAuthenticatedUsername();
+ private:
+ typedef std::map<std::string, qpid::messaging::Session> Sessions;
+
+ mutable qpid::sys::Mutex lock;//used to protect data structures
+ qpid::sys::Semaphore semaphore;//used to coordinate reconnection
+ Sessions sessions;
+ qpid::client::Connection connection;
+ std::vector<std::string> urls;
+ qpid::client::ConnectionSettings settings;
+ bool reconnect;
+ int64_t timeout;
+ int32_t limit;
+ int64_t minReconnectInterval;
+ int64_t maxReconnectInterval;
+ int32_t retries;
+ bool reconnectOnLimitExceeded;
+
+ void setOptions(const qpid::types::Variant::Map& options);
+ void connect(const qpid::sys::AbsTime& started);
+ bool tryConnect();
+ bool resetSessions(const sys::Mutex::ScopedLock&); // dummy parameter indicates call with lock held.
+ void mergeUrls(const std::vector<Url>& more, const sys::Mutex::ScopedLock&);
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
new file mode 100644
index 0000000000..71e89bdba1
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
@@ -0,0 +1,361 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/IncomingMessages.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/types/Variant.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/enum.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using namespace qpid::framing;
+using namespace qpid::framing::message;
+using namespace qpid::amqp_0_10;
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::messaging::MessageImplAccess;
+using qpid::types::Variant;
+
+namespace {
+const std::string EMPTY_STRING;
+
+
+struct GetNone : IncomingMessages::Handler
+{
+ bool accept(IncomingMessages::MessageTransfer&) { return false; }
+};
+
+struct GetAny : IncomingMessages::Handler
+{
+ bool accept(IncomingMessages::MessageTransfer& transfer)
+ {
+ transfer.retrieve(0);
+ return true;
+ }
+};
+
+struct MatchAndTrack
+{
+ const std::string destination;
+ SequenceSet ids;
+
+ MatchAndTrack(const std::string& d) : destination(d) {}
+
+ bool operator()(boost::shared_ptr<qpid::framing::FrameSet> command)
+ {
+ if (command->as<MessageTransferBody>()->getDestination() == destination) {
+ ids.add(command->getId());
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+struct Match
+{
+ const std::string destination;
+ uint32_t matched;
+
+ Match(const std::string& d) : destination(d), matched(0) {}
+
+ bool operator()(boost::shared_ptr<qpid::framing::FrameSet> command)
+ {
+ if (command->as<MessageTransferBody>()->getDestination() == destination) {
+ ++matched;
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+}
+
+void IncomingMessages::setSession(qpid::client::AsyncSession s)
+{
+ sys::Mutex::ScopedLock l(lock);
+ session = s;
+ incoming = SessionBase_0_10Access(session).get()->getDemux().getDefault();
+ acceptTracker.reset();
+}
+
+bool IncomingMessages::get(Handler& handler, Duration timeout)
+{
+ {
+ sys::Mutex::ScopedLock l(lock);
+ //search through received list for any transfer of interest:
+ for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i++)
+ {
+ MessageTransfer transfer(*i, *this);
+ if (handler.accept(transfer)) {
+ received.erase(i);
+ return true;
+ }
+ }
+ }
+ //none found, check incoming:
+ return process(&handler, timeout);
+}
+
+bool IncomingMessages::getNextDestination(std::string& destination, Duration timeout)
+{
+ sys::Mutex::ScopedLock l(lock);
+ //if there is not already a received message, we must wait for one
+ if (received.empty() && !wait(timeout)) return false;
+ //else we have a message in received; return the corresponding destination
+ destination = received.front()->as<MessageTransferBody>()->getDestination();
+ return true;
+}
+
+void IncomingMessages::accept()
+{
+ sys::Mutex::ScopedLock l(lock);
+ acceptTracker.accept(session);
+}
+
+void IncomingMessages::accept(qpid::framing::SequenceNumber id)
+{
+ sys::Mutex::ScopedLock l(lock);
+ acceptTracker.accept(id, session);
+}
+
+
+void IncomingMessages::releaseAll()
+{
+ {
+ //first process any received messages...
+ sys::Mutex::ScopedLock l(lock);
+ while (!received.empty()) {
+ retrieve(received.front(), 0);
+ received.pop_front();
+ }
+ }
+ //then pump out any available messages from incoming queue...
+ GetAny handler;
+ while (process(&handler, 0)) ;
+ //now release all messages
+ sys::Mutex::ScopedLock l(lock);
+ acceptTracker.release(session);
+}
+
+void IncomingMessages::releasePending(const std::string& destination)
+{
+ //first pump all available messages from incoming to received...
+ while (process(0, 0)) ;
+
+ //now remove all messages for this destination from received list, recording their ids...
+ sys::Mutex::ScopedLock l(lock);
+ MatchAndTrack match(destination);
+ for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i = match(*i) ? received.erase(i) : ++i) ;
+ //now release those messages
+ session.messageRelease(match.ids);
+}
+
+/**
+ * Get a frameset that is accepted by the specified handler from
+ * session queue, waiting for up to the specified duration and
+ * returning true if this could be achieved, false otherwise. Messages
+ * that are not accepted by the handler are pushed onto received queue
+ * for later retrieval.
+ */
+bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration)
+{
+ AbsTime deadline(AbsTime::now(), duration);
+ FrameSet::shared_ptr content;
+ try {
+ for (Duration timeout = duration; incoming->pop(content, timeout); timeout = Duration(AbsTime::now(), deadline)) {
+ if (content->isA<MessageTransferBody>()) {
+ MessageTransfer transfer(content, *this);
+ if (handler && handler->accept(transfer)) {
+ QPID_LOG(debug, "Delivered " << *content->getMethod());
+ return true;
+ } else {
+ //received message for another destination, keep for later
+ QPID_LOG(debug, "Pushed " << *content->getMethod() << " to received queue");
+ sys::Mutex::ScopedLock l(lock);
+ received.push_back(content);
+ }
+ } else {
+ //TODO: handle other types of commands (e.g. message-accept, message-flow etc)
+ }
+ }
+ }
+ catch (const qpid::ClosedException&) {} // Just return false if queue closed.
+ return false;
+}
+
+bool IncomingMessages::wait(qpid::sys::Duration duration)
+{
+ AbsTime deadline(AbsTime::now(), duration);
+ FrameSet::shared_ptr content;
+ for (Duration timeout = duration; incoming->pop(content, timeout); timeout = Duration(AbsTime::now(), deadline)) {
+ if (content->isA<MessageTransferBody>()) {
+ QPID_LOG(debug, "Pushed " << *content->getMethod() << " to received queue");
+ sys::Mutex::ScopedLock l(lock);
+ received.push_back(content);
+ return true;
+ } else {
+ //TODO: handle other types of commands (e.g. message-accept, message-flow etc)
+ }
+ }
+ return false;
+}
+
+uint32_t IncomingMessages::pendingAccept()
+{
+ sys::Mutex::ScopedLock l(lock);
+ return acceptTracker.acceptsPending();
+}
+uint32_t IncomingMessages::pendingAccept(const std::string& destination)
+{
+ sys::Mutex::ScopedLock l(lock);
+ return acceptTracker.acceptsPending(destination);
+}
+
+uint32_t IncomingMessages::available()
+{
+ //first pump all available messages from incoming to received...
+ while (process(0, 0)) {}
+ //return the count of received messages
+ sys::Mutex::ScopedLock l(lock);
+ return received.size();
+}
+
+uint32_t IncomingMessages::available(const std::string& destination)
+{
+ //first pump all available messages from incoming to received...
+ while (process(0, 0)) {}
+
+ //count all messages for this destination from received list
+ sys::Mutex::ScopedLock l(lock);
+ return std::for_each(received.begin(), received.end(), Match(destination)).matched;
+}
+
+void populate(qpid::messaging::Message& message, FrameSet& command);
+
+/**
+ * Called when message is retrieved; records retrieval for subsequent
+ * acceptance, marks the command as completed and converts command to
+ * message if message is required
+ */
+void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* message)
+{
+ if (message) {
+ populate(*message, *command);
+ }
+ const MessageTransferBody* transfer = command->as<MessageTransferBody>();
+ if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) {
+ acceptTracker.delivered(transfer->getDestination(), command->getId());
+ }
+ session.markCompleted(command->getId(), false, false);
+}
+
+IncomingMessages::MessageTransfer::MessageTransfer(FrameSetPtr c, IncomingMessages& p) : content(c), parent(p) {}
+
+const std::string& IncomingMessages::MessageTransfer::getDestination()
+{
+ return content->as<MessageTransferBody>()->getDestination();
+}
+void IncomingMessages::MessageTransfer::retrieve(qpid::messaging::Message* message)
+{
+ parent.retrieve(content, message);
+}
+
+
+namespace {
+//TODO: unify conversion to and from 0-10 message that is currently
+//split between IncomingMessages and OutgoingMessage
+const std::string SUBJECT("qpid.subject");
+
+const std::string X_APP_ID("x-amqp-0-10.app-id");
+const std::string X_ROUTING_KEY("x-amqp-0-10.routing-key");
+const std::string X_CONTENT_ENCODING("x-amqp-0-10.content-encoding");
+}
+
+void populateHeaders(qpid::messaging::Message& message,
+ const DeliveryProperties* deliveryProperties,
+ const MessageProperties* messageProperties)
+{
+ if (deliveryProperties) {
+ message.setTtl(qpid::messaging::Duration(deliveryProperties->getTtl()));
+ message.setDurable(deliveryProperties->getDeliveryMode() == DELIVERY_MODE_PERSISTENT);
+ message.setPriority(deliveryProperties->getPriority());
+ message.setRedelivered(deliveryProperties->getRedelivered());
+ }
+ if (messageProperties) {
+ message.setContentType(messageProperties->getContentType());
+ if (messageProperties->hasReplyTo()) {
+ message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo()));
+ }
+ message.setSubject(messageProperties->getApplicationHeaders().getAsString(SUBJECT));
+ message.getProperties().clear();
+ translate(messageProperties->getApplicationHeaders(), message.getProperties());
+ message.setCorrelationId(messageProperties->getCorrelationId());
+ message.setUserId(messageProperties->getUserId());
+ if (messageProperties->hasMessageId()) {
+ message.setMessageId(messageProperties->getMessageId().str());
+ }
+ //expose 0-10 specific items through special properties:
+ // app-id, content-encoding
+ if (messageProperties->hasAppId()) {
+ message.getProperties()[X_APP_ID] = messageProperties->getAppId();
+ }
+ if (messageProperties->hasContentEncoding()) {
+ message.getProperties()[X_CONTENT_ENCODING] = messageProperties->getContentEncoding();
+ }
+ // routing-key, others?
+ if (deliveryProperties && deliveryProperties->hasRoutingKey()) {
+ message.getProperties()[X_ROUTING_KEY] = deliveryProperties->getRoutingKey();
+ }
+ }
+}
+
+void populateHeaders(qpid::messaging::Message& message, const AMQHeaderBody* headers)
+{
+ populateHeaders(message, headers->get<DeliveryProperties>(), headers->get<MessageProperties>());
+}
+
+void populate(qpid::messaging::Message& message, FrameSet& command)
+{
+ //need to be able to link the message back to the transfer it was delivered by
+ //e.g. for rejecting.
+ MessageImplAccess::get(message).setInternalId(command.getId());
+
+ message.setContent(command.getContent());
+
+ populateHeaders(message, command.getHeaders());
+}
+
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
new file mode 100644
index 0000000000..f6a291bc68
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
@@ -0,0 +1,100 @@
+#ifndef QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H
+#define QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "qpid/client/AsyncSession.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/sys/Time.h"
+#include "qpid/client/amqp0_10/AcceptTracker.h"
+
+namespace qpid {
+
+namespace framing{
+class FrameSet;
+}
+
+namespace messaging {
+class Message;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Queue of incoming messages.
+ */
+class IncomingMessages
+{
+ public:
+ typedef boost::shared_ptr<qpid::framing::FrameSet> FrameSetPtr;
+ class MessageTransfer
+ {
+ public:
+ const std::string& getDestination();
+ void retrieve(qpid::messaging::Message* message);
+ private:
+ FrameSetPtr content;
+ IncomingMessages& parent;
+
+ MessageTransfer(FrameSetPtr, IncomingMessages&);
+ friend class IncomingMessages;
+ };
+
+ struct Handler
+ {
+ virtual ~Handler() {}
+ virtual bool accept(MessageTransfer& transfer) = 0;
+ };
+
+ void setSession(qpid::client::AsyncSession session);
+ bool get(Handler& handler, qpid::sys::Duration timeout);
+ bool getNextDestination(std::string& destination, qpid::sys::Duration timeout);
+ void accept();
+ void accept(qpid::framing::SequenceNumber id);
+ void releaseAll();
+ void releasePending(const std::string& destination);
+
+ uint32_t pendingAccept();
+ uint32_t pendingAccept(const std::string& destination);
+
+ uint32_t available();
+ uint32_t available(const std::string& destination);
+ private:
+ typedef std::deque<FrameSetPtr> FrameSetQueue;
+
+ sys::Mutex lock;
+ qpid::client::AsyncSession session;
+ boost::shared_ptr< sys::BlockingQueue<FrameSetPtr> > incoming;
+ FrameSetQueue received;
+ AcceptTracker acceptTracker;
+
+ bool process(Handler*, qpid::sys::Duration);
+ bool wait(qpid::sys::Duration);
+ void retrieve(FrameSetPtr, qpid::messaging::Message*);
+
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h
new file mode 100644
index 0000000000..8d87a3c7bb
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h
@@ -0,0 +1,52 @@
+#ifndef QPID_CLIENT_AMQP0_10_MESSAGESINK_H
+#define QPID_CLIENT_AMQP0_10_MESSAGESINK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/client/AsyncSession.h"
+
+namespace qpid {
+
+namespace messaging {
+class Message;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+struct OutgoingMessage;
+
+/**
+ *
+ */
+class MessageSink
+{
+ public:
+ virtual ~MessageSink() {}
+ virtual void declare(qpid::client::AsyncSession& session, const std::string& name) = 0;
+ virtual void send(qpid::client::AsyncSession& session, const std::string& name, OutgoingMessage& message) = 0;
+ virtual void cancel(qpid::client::AsyncSession& session, const std::string& name) = 0;
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESINK_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h
new file mode 100644
index 0000000000..74f2732f59
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h
@@ -0,0 +1,47 @@
+#ifndef QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H
+#define QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/client/AsyncSession.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Abstraction behind which the AMQP 0-10 commands required to
+ * establish (and tear down) an incoming stream of messages from a
+ * given address are hidden.
+ */
+class MessageSource
+{
+ public:
+ virtual ~MessageSource() {}
+ virtual void subscribe(qpid::client::AsyncSession& session, const std::string& destination) = 0;
+ virtual void cancel(qpid::client::AsyncSession& session, const std::string& destination) = 0;
+
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
new file mode 100644
index 0000000000..d93416da75
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/amqp0_10/OutgoingMessage.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/types/Variant.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/framing/enum.h"
+#include <sstream>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Address;
+using qpid::messaging::MessageImplAccess;
+using qpid::types::Variant;
+using namespace qpid::framing::message;
+using namespace qpid::amqp_0_10;
+
+namespace {
+//TODO: unify conversion to and from 0-10 message that is currently
+//split between IncomingMessages and OutgoingMessage
+const std::string SUBJECT("qpid.subject");
+const std::string X_APP_ID("x-amqp-0-10.app-id");
+const std::string X_ROUTING_KEY("x-amqp-0-10.routing-key");
+const std::string X_CONTENT_ENCODING("x-amqp-0-10.content-encoding");
+}
+
+void OutgoingMessage::convert(const qpid::messaging::Message& from)
+{
+ //TODO: need to avoid copying as much as possible
+ message.setData(from.getContent());
+ message.getMessageProperties().setContentType(from.getContentType());
+ message.getMessageProperties().setCorrelationId(from.getCorrelationId());
+ message.getMessageProperties().setUserId(from.getUserId());
+ const Address& address = from.getReplyTo();
+ if (address) {
+ message.getMessageProperties().setReplyTo(AddressResolution::convert(address));
+ }
+ translate(from.getProperties(), message.getMessageProperties().getApplicationHeaders());
+ if (from.getTtl().getMilliseconds()) {
+ message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds());
+ }
+ if (from.getDurable()) {
+ message.getDeliveryProperties().setDeliveryMode(DELIVERY_MODE_PERSISTENT);
+ }
+ if (from.getRedelivered()) {
+ message.getDeliveryProperties().setRedelivered(true);
+ }
+ if (from.getPriority()) message.getDeliveryProperties().setPriority(from.getPriority());
+
+ //allow certain 0-10 specific items to be set through special properties:
+ // message-id, app-id, content-encoding
+ if (from.getMessageId().size()) {
+ qpid::framing::Uuid uuid;
+ std::istringstream data(from.getMessageId());
+ data >> uuid;
+ message.getMessageProperties().setMessageId(uuid);
+ }
+ Variant::Map::const_iterator i;
+ i = from.getProperties().find(X_APP_ID);
+ if (i != from.getProperties().end()) {
+ message.getMessageProperties().setAppId(i->second.asString());
+ }
+ i = from.getProperties().find(X_CONTENT_ENCODING);
+ if (i != from.getProperties().end()) {
+ message.getMessageProperties().setContentEncoding(i->second.asString());
+ }
+}
+
+void OutgoingMessage::setSubject(const std::string& subject)
+{
+ if (!subject.empty()) {
+ message.getMessageProperties().getApplicationHeaders().setString(SUBJECT, subject);
+ }
+}
+
+std::string OutgoingMessage::getSubject() const
+{
+ return message.getMessageProperties().getApplicationHeaders().getAsString(SUBJECT);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
new file mode 100644
index 0000000000..0cdd2a2336
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
@@ -0,0 +1,48 @@
+#ifndef QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H
+#define QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/Completion.h"
+#include "qpid/client/Message.h"
+
+namespace qpid {
+namespace messaging {
+class Message;
+}
+namespace client {
+namespace amqp0_10 {
+
+struct OutgoingMessage
+{
+ qpid::client::Message message;
+ qpid::client::Completion status;
+
+ void convert(const qpid::messaging::Message&);
+ void setSubject(const std::string& subject);
+ std::string getSubject() const;
+};
+
+
+
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_OUTGOINGMESSAGE_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp
new file mode 100644
index 0000000000..030b804143
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp
@@ -0,0 +1,225 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "ReceiverImpl.h"
+#include "AddressResolution.h"
+#include "MessageSource.h"
+#include "SessionImpl.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::NoMessageAvailable;
+using qpid::messaging::Receiver;
+using qpid::messaging::Duration;
+
+void ReceiverImpl::received(qpid::messaging::Message&)
+{
+ //TODO: should this be configurable
+ sys::Mutex::ScopedLock l(lock);
+ if (capacity && --window <= capacity/2) {
+ session.sendCompletion();
+ window = capacity;
+ }
+}
+
+qpid::messaging::Message ReceiverImpl::get(qpid::messaging::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!get(result, timeout)) throw NoMessageAvailable();
+ return result;
+}
+
+qpid::messaging::Message ReceiverImpl::fetch(qpid::messaging::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!fetch(result, timeout)) throw NoMessageAvailable();
+ return result;
+}
+
+bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ Get f(*this, message, timeout);
+ while (!parent->execute(f)) {}
+ return f.result;
+}
+
+bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ Fetch f(*this, message, timeout);
+ while (!parent->execute(f)) {}
+ return f.result;
+}
+
+void ReceiverImpl::close()
+{
+ execute<Close>();
+}
+
+void ReceiverImpl::start()
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (state == STOPPED) {
+ state = STARTED;
+ startFlow(l);
+ }
+}
+
+void ReceiverImpl::stop()
+{
+ sys::Mutex::ScopedLock l(lock);
+ state = STOPPED;
+ session.messageStop(destination);
+}
+
+void ReceiverImpl::setCapacity(uint32_t c)
+{
+ execute1<SetCapacity>(c);
+}
+
+void ReceiverImpl::startFlow(const sys::Mutex::ScopedLock&)
+{
+ if (capacity > 0) {
+ session.messageSetFlowMode(destination, FLOW_MODE_WINDOW);
+ session.messageFlow(destination, CREDIT_UNIT_MESSAGE, capacity);
+ session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit);
+ window = capacity;
+ }
+}
+
+void ReceiverImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver)
+{
+ sys::Mutex::ScopedLock l(lock);
+ session = s;
+ if (state == CANCELLED) return;
+ if (state == UNRESOLVED) {
+ source = resolver.resolveSource(session, address);
+ assert(source.get());
+ state = STARTED;
+ }
+ source->subscribe(session, destination);
+ startFlow(l);
+}
+
+const std::string& ReceiverImpl::getName() const {
+ sys::Mutex::ScopedLock l(lock);
+ return destination;
+}
+
+uint32_t ReceiverImpl::getCapacity()
+{
+ sys::Mutex::ScopedLock l(lock);
+ return capacity;
+}
+
+uint32_t ReceiverImpl::getAvailable()
+{
+ return parent->getReceivable(destination);
+}
+
+uint32_t ReceiverImpl::getUnsettled()
+{
+ return parent->getUnsettledAcks(destination);
+}
+
+ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name,
+ const qpid::messaging::Address& a) :
+
+ parent(&p), destination(name), address(a), byteCredit(0xFFFFFFFF),
+ state(UNRESOLVED), capacity(0), window(0) {}
+
+bool ReceiverImpl::getImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ {
+ sys::Mutex::ScopedLock l(lock);
+ if (state == CANCELLED) return false;
+ }
+ return parent->get(*this, message, timeout);
+}
+
+bool ReceiverImpl::fetchImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ {
+ sys::Mutex::ScopedLock l(lock);
+ if (state == CANCELLED) return false;
+
+ if (capacity == 0 || state != STARTED) {
+ session.messageSetFlowMode(destination, FLOW_MODE_CREDIT);
+ session.messageFlow(destination, CREDIT_UNIT_MESSAGE, 1);
+ session.messageFlow(destination, CREDIT_UNIT_BYTE, 0xFFFFFFFF);
+ }
+ }
+ if (getImpl(message, timeout)) {
+ return true;
+ } else {
+ qpid::client::Session s;
+ {
+ sys::Mutex::ScopedLock l(lock);
+ if (state == CANCELLED) return false; // Might have been closed during get.
+ s = sync(session);
+ }
+ s.messageFlush(destination);
+ {
+ sys::Mutex::ScopedLock l(lock);
+ startFlow(l); //reallocate credit
+ }
+ return getImpl(message, Duration::IMMEDIATE);
+ }
+}
+
+void ReceiverImpl::closeImpl()
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (state != CANCELLED) {
+ state = CANCELLED;
+ sync(session).messageStop(destination);
+ parent->releasePending(destination);
+ source->cancel(session, destination);
+ parent->receiverCancelled(destination);
+ }
+}
+
+bool ReceiverImpl::isClosed() const {
+ sys::Mutex::ScopedLock l(lock);
+ return state == CANCELLED;
+}
+
+void ReceiverImpl::setCapacityImpl(uint32_t c)
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (c != capacity) {
+ capacity = c;
+ if (state == STARTED) {
+ session.messageStop(destination);
+ startFlow(l);
+ }
+ }
+}
+
+qpid::messaging::Session ReceiverImpl::getSession() const
+{
+ return qpid::messaging::Session(parent.get());
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
new file mode 100644
index 0000000000..5693b7b71f
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
@@ -0,0 +1,151 @@
+#ifndef QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H
+#define QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/ReceiverImpl.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/intrusive_ptr.hpp>
+#include <memory>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class AddressResolution;
+class MessageSource;
+
+/**
+ * A receiver implementation based on an AMQP 0-10 subscription.
+ */
+class ReceiverImpl : public qpid::messaging::ReceiverImpl
+{
+ public:
+
+ enum State {UNRESOLVED, STOPPED, STARTED, CANCELLED};
+
+ ReceiverImpl(SessionImpl& parent, const std::string& name,
+ const qpid::messaging::Address& address);
+
+ void init(qpid::client::AsyncSession session, AddressResolution& resolver);
+ bool get(qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ qpid::messaging::Message get(qpid::messaging::Duration timeout);
+ bool fetch(qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ qpid::messaging::Message fetch(qpid::messaging::Duration timeout);
+ void close();
+ void start();
+ void stop();
+ const std::string& getName() const;
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getAvailable();
+ uint32_t getUnsettled();
+ void received(qpid::messaging::Message& message);
+ qpid::messaging::Session getSession() const;
+ bool isClosed() const;
+
+ private:
+ mutable sys::Mutex lock;
+ boost::intrusive_ptr<SessionImpl> parent;
+ const std::string destination;
+ const qpid::messaging::Address address;
+ const uint32_t byteCredit;
+ State state;
+
+ std::auto_ptr<MessageSource> source;
+ uint32_t capacity;
+ qpid::client::AsyncSession session;
+ qpid::messaging::MessageListener* listener;
+ uint32_t window;
+
+ void startFlow(const sys::Mutex::ScopedLock&); // Dummy param, call with lock held
+ //implementation of public facing methods
+ bool fetchImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ bool getImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+ void closeImpl();
+ void setCapacityImpl(uint32_t);
+
+ //functors for public facing methods.
+ struct Command
+ {
+ ReceiverImpl& impl;
+
+ Command(ReceiverImpl& i) : impl(i) {}
+ };
+
+ struct Get : Command
+ {
+ qpid::messaging::Message& message;
+ qpid::messaging::Duration timeout;
+ bool result;
+
+ Get(ReceiverImpl& i, qpid::messaging::Message& m, qpid::messaging::Duration t) :
+ Command(i), message(m), timeout(t), result(false) {}
+ void operator()() { result = impl.getImpl(message, timeout); }
+ };
+
+ struct Fetch : Command
+ {
+ qpid::messaging::Message& message;
+ qpid::messaging::Duration timeout;
+ bool result;
+
+ Fetch(ReceiverImpl& i, qpid::messaging::Message& m, qpid::messaging::Duration t) :
+ Command(i), message(m), timeout(t), result(false) {}
+ void operator()() { result = impl.fetchImpl(message, timeout); }
+ };
+
+ struct Close : Command
+ {
+ Close(ReceiverImpl& i) : Command(i) {}
+ void operator()() { impl.closeImpl(); }
+ };
+
+ struct SetCapacity : Command
+ {
+ uint32_t capacity;
+
+ SetCapacity(ReceiverImpl& i, uint32_t c) : Command(i), capacity(c) {}
+ void operator()() { impl.setCapacityImpl(capacity); }
+ };
+
+ //helper templates for some common patterns
+ template <class F> void execute()
+ {
+ F f(*this);
+ parent->execute(f);
+ }
+
+ template <class F, class P> void execute1(P p)
+ {
+ F f(*this, p);
+ parent->execute(f);
+ }
+};
+
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
new file mode 100644
index 0000000000..f2f0f1a9e5
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
@@ -0,0 +1,182 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "SenderImpl.h"
+#include "MessageSink.h"
+#include "SessionImpl.h"
+#include "AddressResolution.h"
+#include "OutgoingMessage.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name,
+ const qpid::messaging::Address& _address) :
+ parent(&_parent), name(_name), address(_address), state(UNRESOLVED),
+ capacity(50), window(0), flushed(false), unreliable(AddressResolution::is_unreliable(address)) {}
+
+void SenderImpl::send(const qpid::messaging::Message& message, bool sync)
+{
+ if (unreliable) { // immutable, don't need lock
+ UnreliableSend f(*this, &message);
+ parent->execute(f);
+ } else {
+ Send f(*this, &message);
+ while (f.repeat) parent->execute(f);
+ }
+ if (sync) parent->sync(true);
+}
+
+void SenderImpl::close()
+{
+ execute<Close>();
+}
+
+void SenderImpl::setCapacity(uint32_t c)
+{
+ bool flush;
+ {
+ sys::Mutex::ScopedLock l(lock);
+ flush = c < capacity;
+ capacity = c;
+ }
+ execute1<CheckPendingSends>(flush);
+}
+
+uint32_t SenderImpl::getCapacity() {
+ sys::Mutex::ScopedLock l(lock);
+ return capacity;
+}
+
+uint32_t SenderImpl::getUnsettled()
+{
+ CheckPendingSends f(*this, false);
+ parent->execute(f);
+ return f.pending;
+}
+
+void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver)
+{
+ sys::Mutex::ScopedLock l(lock);
+ session = s;
+ if (state == UNRESOLVED) {
+ sink = resolver.resolveSink(session, address);
+ state = ACTIVE;
+ }
+ if (state == CANCELLED) {
+ sink->cancel(session, name);
+ sys::Mutex::ScopedUnlock u(lock);
+ parent->senderCancelled(name);
+ } else {
+ sink->declare(session, name);
+ replay(l);
+ }
+}
+
+void SenderImpl::waitForCapacity()
+{
+ sys::Mutex::ScopedLock l(lock);
+ //TODO: add option to throw exception rather than blocking?
+ if (!unreliable && capacity <=
+ (flushed ? checkPendingSends(false, l) : outgoing.size()))
+ {
+ //Initial implementation is very basic. As outgoing is
+ //currently only reduced on receiving completions and we are
+ //blocking anyway we may as well sync(). If successful that
+ //should clear all outstanding sends.
+ session.sync();
+ checkPendingSends(false, l);
+ }
+ //flush periodically and check for conmpleted sends
+ if (++window > (capacity / 4)) {//TODO: make this configurable?
+ checkPendingSends(true, l);
+ window = 0;
+ }
+}
+
+void SenderImpl::sendImpl(const qpid::messaging::Message& m)
+{
+ sys::Mutex::ScopedLock l(lock);
+ std::auto_ptr<OutgoingMessage> msg(new OutgoingMessage());
+ msg->convert(m);
+ msg->setSubject(m.getSubject().empty() ? address.getSubject() : m.getSubject());
+ outgoing.push_back(msg.release());
+ sink->send(session, name, outgoing.back());
+}
+
+void SenderImpl::sendUnreliable(const qpid::messaging::Message& m)
+{
+ sys::Mutex::ScopedLock l(lock);
+ OutgoingMessage msg;
+ msg.convert(m);
+ msg.setSubject(m.getSubject().empty() ? address.getSubject() : m.getSubject());
+ sink->send(session, name, msg);
+}
+
+void SenderImpl::replay(const sys::Mutex::ScopedLock&)
+{
+ for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) {
+ i->message.setRedelivered(true);
+ sink->send(session, name, *i);
+ }
+}
+
+uint32_t SenderImpl::checkPendingSends(bool flush) {
+ sys::Mutex::ScopedLock l(lock);
+ return checkPendingSends(flush, l);
+}
+
+uint32_t SenderImpl::checkPendingSends(bool flush, const sys::Mutex::ScopedLock&)
+{
+ if (flush) {
+ session.flush();
+ flushed = true;
+ } else {
+ flushed = false;
+ }
+ while (!outgoing.empty() && outgoing.front().status.isComplete()) {
+ outgoing.pop_front();
+ }
+ return outgoing.size();
+}
+
+void SenderImpl::closeImpl()
+{
+ sys::Mutex::ScopedLock l(lock);
+ state = CANCELLED;
+ sink->cancel(session, name);
+ parent->senderCancelled(name);
+}
+
+const std::string& SenderImpl::getName() const
+{
+ sys::Mutex::ScopedLock l(lock);
+ return name;
+}
+
+qpid::messaging::Session SenderImpl::getSession() const
+{
+ sys::Mutex::ScopedLock l(lock);
+ return qpid::messaging::Session(parent.get());
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h
new file mode 100644
index 0000000000..c10c77ae18
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h
@@ -0,0 +1,160 @@
+#ifndef QPID_CLIENT_AMQP0_10_SENDERIMPL_H
+#define QPID_CLIENT_AMQP0_10_SENDERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/SenderImpl.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include <memory>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/ptr_container/ptr_deque.hpp>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class AddressResolution;
+class MessageSink;
+struct OutgoingMessage;
+
+/**
+ *
+ */
+class SenderImpl : public qpid::messaging::SenderImpl
+{
+ public:
+ enum State {UNRESOLVED, ACTIVE, CANCELLED};
+
+ SenderImpl(SessionImpl& parent, const std::string& name,
+ const qpid::messaging::Address& address);
+ void send(const qpid::messaging::Message&, bool sync);
+ void close();
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t getUnsettled();
+ void init(qpid::client::AsyncSession, AddressResolution&);
+ const std::string& getName() const;
+ qpid::messaging::Session getSession() const;
+
+ private:
+ mutable sys::Mutex lock;
+ boost::intrusive_ptr<SessionImpl> parent;
+ const std::string name;
+ const qpid::messaging::Address address;
+ State state;
+ std::auto_ptr<MessageSink> sink;
+
+ qpid::client::AsyncSession session;
+ std::string destination;
+ std::string routingKey;
+
+ typedef boost::ptr_deque<OutgoingMessage> OutgoingMessages;
+ OutgoingMessages outgoing;
+ uint32_t capacity;
+ uint32_t window;
+ bool flushed;
+ const bool unreliable;
+
+ uint32_t checkPendingSends(bool flush);
+ // Dummy ScopedLock parameter means call with lock held
+ uint32_t checkPendingSends(bool flush, const sys::Mutex::ScopedLock&);
+ void replay(const sys::Mutex::ScopedLock&);
+ void waitForCapacity();
+
+ //logic for application visible methods:
+ void sendImpl(const qpid::messaging::Message&);
+ void sendUnreliable(const qpid::messaging::Message&);
+ void closeImpl();
+
+
+ //functors for application visible methods (allowing locking and
+ //retry to be centralised):
+ struct Command
+ {
+ SenderImpl& impl;
+
+ Command(SenderImpl& i) : impl(i) {}
+ };
+
+ struct Send : Command
+ {
+ const qpid::messaging::Message* message;
+ bool repeat;
+
+ Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m), repeat(true) {}
+ void operator()()
+ {
+ impl.waitForCapacity();
+ //from this point message will be recorded if there is any
+ //failure (and replayed) so need not repeat the call
+ repeat = false;
+ impl.sendImpl(*message);
+ }
+ };
+
+ struct UnreliableSend : Command
+ {
+ const qpid::messaging::Message* message;
+
+ UnreliableSend(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m) {}
+ void operator()()
+ {
+ //TODO: ideally want to put messages on the outbound
+ //queue and pull them off in io thread, but the old
+ //0-10 client doesn't support that option so for now
+ //we simply don't queue unreliable messages
+ impl.sendUnreliable(*message);
+ }
+ };
+
+ struct Close : Command
+ {
+ Close(SenderImpl& i) : Command(i) {}
+ void operator()() { impl.closeImpl(); }
+ };
+
+ struct CheckPendingSends : Command
+ {
+ bool flush;
+ uint32_t pending;
+ CheckPendingSends(SenderImpl& i, bool f) : Command(i), flush(f), pending(0) {}
+ void operator()() { pending = impl.checkPendingSends(flush); }
+ };
+
+ //helper templates for some common patterns
+ template <class F> void execute()
+ {
+ F f(*this);
+ parent->execute(f);
+ }
+
+ template <class F, class P> bool execute1(P p)
+ {
+ F f(*this, p);
+ return parent->execute(f);
+ }
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_SENDERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
new file mode 100644
index 0000000000..75a71997fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
@@ -0,0 +1,525 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include "qpid/client/amqp0_10/ConnectionImpl.h"
+#include "qpid/client/amqp0_10/ReceiverImpl.h"
+#include "qpid/client/amqp0_10/SenderImpl.h"
+#include "qpid/client/amqp0_10/MessageSource.h"
+#include "qpid/client/amqp0_10/MessageSink.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/messaging/PrivateImplRef.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Session.h"
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+using qpid::messaging::KeyError;
+using qpid::messaging::NoMessageAvailable;
+using qpid::messaging::MessagingException;
+using qpid::messaging::TransactionAborted;
+using qpid::messaging::SessionError;
+using qpid::messaging::MessageImplAccess;
+using qpid::messaging::Sender;
+using qpid::messaging::Receiver;
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+typedef qpid::sys::Mutex::ScopedLock ScopedLock;
+typedef qpid::sys::Mutex::ScopedUnlock ScopedUnlock;
+
+SessionImpl::SessionImpl(ConnectionImpl& c, bool t) : connection(&c), transactional(t) {}
+
+void SessionImpl::checkError()
+{
+ qpid::client::SessionBase_0_10Access s(session);
+ s.get()->assertOpen();
+}
+
+bool SessionImpl::hasError()
+{
+ qpid::client::SessionBase_0_10Access s(session);
+ return s.get()->hasError();
+}
+
+void SessionImpl::sync(bool block)
+{
+ if (block) retry<Sync>();
+ else execute<NonBlockingSync>();
+}
+
+void SessionImpl::commit()
+{
+ if (!execute<Commit>()) {
+ throw TransactionAborted("Transaction aborted due to transport failure");
+ }
+}
+
+void SessionImpl::rollback()
+{
+ //If the session fails during this operation, the transaction will
+ //be rolled back anyway.
+ execute<Rollback>();
+}
+
+void SessionImpl::acknowledge(bool sync_)
+{
+ //Should probably throw an exception on failure here, or indicate
+ //it through a return type at least. Failure means that the
+ //message may be redelivered; i.e. the application cannot delete
+ //any state necessary for preventing reprocessing of the message
+ execute<Acknowledge>();
+ sync(sync_);
+}
+
+void SessionImpl::reject(qpid::messaging::Message& m)
+{
+ //Possibly want to somehow indicate failure here as well. Less
+ //clear need as compared to acknowledge however.
+ execute1<Reject>(m);
+}
+
+void SessionImpl::release(qpid::messaging::Message& m)
+{
+ execute1<Release>(m);
+}
+
+void SessionImpl::acknowledge(qpid::messaging::Message& m)
+{
+ //Should probably throw an exception on failure here, or indicate
+ //it through a return type at least. Failure means that the
+ //message may be redelivered; i.e. the application cannot delete
+ //any state necessary for preventing reprocessing of the message
+ execute1<Acknowledge1>(m);
+}
+
+void SessionImpl::close()
+{
+ if (hasError()) {
+ ScopedLock l(lock);
+ senders.clear();
+ receivers.clear();
+ } else {
+ while (true) {
+ Sender s;
+ {
+ ScopedLock l(lock);
+ if (senders.empty()) break;
+ s = senders.begin()->second;
+ }
+ s.close(); // outside the lock, will call senderCancelled
+ }
+ while (true) {
+ Receiver r;
+ {
+ ScopedLock l(lock);
+ if (receivers.empty()) break;
+ r = receivers.begin()->second;
+ }
+ r.close(); // outside the lock, will call receiverCancelled
+ }
+ }
+ connection->closed(*this);
+ if (!hasError()) session.close();
+}
+
+template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t)
+{
+ return boost::dynamic_pointer_cast<S>(qpid::messaging::PrivateImplRef<T>::get(t));
+}
+
+template <class T> void getFreeKey(std::string& key, T& map)
+{
+ std::string name = key;
+ int count = 1;
+ for (typename T::const_iterator i = map.find(name); i != map.end(); i = map.find(name)) {
+ name = (boost::format("%1%_%2%") % key % ++count).str();
+ }
+ key = name;
+}
+
+
+void SessionImpl::setSession(qpid::client::Session s)
+{
+ ScopedLock l(lock);
+ session = s;
+ incoming.setSession(session);
+ if (transactional) session.txSelect();
+ for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) {
+ getImplPtr<Receiver, ReceiverImpl>(i->second)->init(session, resolver);
+ }
+ for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) {
+ getImplPtr<Sender, SenderImpl>(i->second)->init(session, resolver);
+ }
+ session.sync();
+}
+
+struct SessionImpl::CreateReceiver : Command
+{
+ qpid::messaging::Receiver result;
+ const qpid::messaging::Address& address;
+
+ CreateReceiver(SessionImpl& i, const qpid::messaging::Address& a) :
+ Command(i), address(a) {}
+ void operator()() { result = impl.createReceiverImpl(address); }
+};
+
+Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address)
+{
+ return get1<CreateReceiver, Receiver>(address);
+}
+
+Receiver SessionImpl::createReceiverImpl(const qpid::messaging::Address& address)
+{
+ ScopedLock l(lock);
+ std::string name = address.getName();
+ getFreeKey(name, receivers);
+ Receiver receiver(new ReceiverImpl(*this, name, address));
+ getImplPtr<Receiver, ReceiverImpl>(receiver)->init(session, resolver);
+ receivers[name] = receiver;
+ return receiver;
+}
+
+struct SessionImpl::CreateSender : Command
+{
+ qpid::messaging::Sender result;
+ const qpid::messaging::Address& address;
+
+ CreateSender(SessionImpl& i, const qpid::messaging::Address& a) :
+ Command(i), address(a) {}
+ void operator()() { result = impl.createSenderImpl(address); }
+};
+
+Sender SessionImpl::createSender(const qpid::messaging::Address& address)
+{
+ return get1<CreateSender, Sender>(address);
+}
+
+Sender SessionImpl::createSenderImpl(const qpid::messaging::Address& address)
+{
+ ScopedLock l(lock);
+ std::string name = address.getName();
+ getFreeKey(name, senders);
+ Sender sender(new SenderImpl(*this, name, address));
+ getImplPtr<Sender, SenderImpl>(sender)->init(session, resolver);
+ senders[name] = sender;
+ return sender;
+}
+
+Sender SessionImpl::getSender(const std::string& name) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ Senders::const_iterator i = senders.find(name);
+ if (i == senders.end()) {
+ throw KeyError(name);
+ } else {
+ return i->second;
+ }
+}
+
+Receiver SessionImpl::getReceiver(const std::string& name) const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ Receivers::const_iterator i = receivers.find(name);
+ if (i == receivers.end()) {
+ throw KeyError(name);
+ } else {
+ return i->second;
+ }
+}
+
+SessionImpl& SessionImpl::convert(qpid::messaging::Session& s)
+{
+ boost::intrusive_ptr<SessionImpl> impl = getImplPtr<qpid::messaging::Session, SessionImpl>(s);
+ if (!impl) {
+ throw SessionError(QPID_MSG("Configuration error; require qpid::client::amqp0_10::SessionImpl"));
+ }
+ return *impl;
+}
+
+namespace {
+
+struct IncomingMessageHandler : IncomingMessages::Handler
+{
+ typedef boost::function1<bool, IncomingMessages::MessageTransfer&> Callback;
+ Callback callback;
+
+ IncomingMessageHandler(Callback c) : callback(c) {}
+
+ bool accept(IncomingMessages::MessageTransfer& transfer)
+ {
+ return callback(transfer);
+ }
+};
+
+}
+
+
+bool SessionImpl::getNextReceiver(Receiver* receiver, IncomingMessages::MessageTransfer& transfer)
+{
+ ScopedLock l(lock);
+ Receivers::const_iterator i = receivers.find(transfer.getDestination());
+ if (i == receivers.end()) {
+ QPID_LOG(error, "Received message for unknown destination " << transfer.getDestination());
+ return false;
+ } else {
+ *receiver = i->second;
+ return true;
+ }
+}
+
+bool SessionImpl::accept(ReceiverImpl* receiver,
+ qpid::messaging::Message* message,
+ IncomingMessages::MessageTransfer& transfer)
+{
+ if (receiver->getName() == transfer.getDestination()) {
+ transfer.retrieve(message);
+ receiver->received(*message);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+qpid::sys::Duration adjust(qpid::messaging::Duration timeout)
+{
+ uint64_t ms = timeout.getMilliseconds();
+ if (ms < (uint64_t) (qpid::sys::TIME_INFINITE/qpid::sys::TIME_MSEC)) {
+ return ms * qpid::sys::TIME_MSEC;
+ } else {
+ return qpid::sys::TIME_INFINITE;
+ }
+}
+
+bool SessionImpl::getIncoming(IncomingMessages::Handler& handler, qpid::messaging::Duration timeout)
+{
+ return incoming.get(handler, adjust(timeout));
+}
+
+bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::messaging::Duration timeout)
+{
+ IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, _1));
+ return getIncoming(handler, timeout);
+}
+
+bool SessionImpl::nextReceiver(qpid::messaging::Receiver& receiver, qpid::messaging::Duration timeout)
+{
+ while (true) {
+ try {
+ std::string destination;
+ if (incoming.getNextDestination(destination, adjust(timeout))) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ Receivers::const_iterator i = receivers.find(destination);
+ if (i == receivers.end()) {
+ throw qpid::messaging::ReceiverError(QPID_MSG("Received message for unknown destination " << destination));
+ } else {
+ receiver = i->second;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } catch (TransportFailure&) {
+ reconnect();
+ } catch (const qpid::framing::ResourceLimitExceededException& e) {
+ if (backoff()) return false;
+ else throw qpid::messaging::TargetCapacityExceeded(e.what());
+ } catch (const qpid::framing::UnauthorizedAccessException& e) {
+ throw qpid::messaging::UnauthorizedAccess(e.what());
+ } catch (const qpid::SessionException& e) {
+ throw qpid::messaging::SessionError(e.what());
+ } catch (const qpid::ConnectionException& e) {
+ throw qpid::messaging::ConnectionError(e.what());
+ } catch (const qpid::ChannelException& e) {
+ throw qpid::messaging::MessagingException(e.what());
+ }
+ }
+}
+
+qpid::messaging::Receiver SessionImpl::nextReceiver(qpid::messaging::Duration timeout)
+{
+ qpid::messaging::Receiver receiver;
+ if (!nextReceiver(receiver, timeout)) throw NoMessageAvailable();
+ if (!receiver) throw SessionError("Bad receiver returned!");
+ return receiver;
+}
+
+uint32_t SessionImpl::getReceivable()
+{
+ return get1<Receivable, uint32_t>((const std::string*) 0);
+}
+uint32_t SessionImpl::getReceivable(const std::string& destination)
+{
+ return get1<Receivable, uint32_t>(&destination);
+}
+
+struct SessionImpl::Receivable : Command
+{
+ const std::string* destination;
+ uint32_t result;
+
+ Receivable(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {}
+ void operator()() { result = impl.getReceivableImpl(destination); }
+};
+
+uint32_t SessionImpl::getReceivableImpl(const std::string* destination)
+{
+ ScopedLock l(lock);
+ if (destination) {
+ return incoming.available(*destination);
+ } else {
+ return incoming.available();
+ }
+}
+
+uint32_t SessionImpl::getUnsettledAcks()
+{
+ return get1<UnsettledAcks, uint32_t>((const std::string*) 0);
+}
+
+uint32_t SessionImpl::getUnsettledAcks(const std::string& destination)
+{
+ return get1<UnsettledAcks, uint32_t>(&destination);
+}
+
+struct SessionImpl::UnsettledAcks : Command
+{
+ const std::string* destination;
+ uint32_t result;
+
+ UnsettledAcks(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {}
+ void operator()() { result = impl.getUnsettledAcksImpl(destination); }
+};
+
+uint32_t SessionImpl::getUnsettledAcksImpl(const std::string* destination)
+{
+ ScopedLock l(lock);
+ if (destination) {
+ return incoming.pendingAccept(*destination);
+ } else {
+ return incoming.pendingAccept();
+ }
+}
+
+void SessionImpl::syncImpl(bool block)
+{
+ if (block) session.sync();
+ else session.flush();
+ //cleanup unconfirmed accept records:
+ incoming.pendingAccept();
+}
+
+void SessionImpl::commitImpl()
+{
+ ScopedLock l(lock);
+ incoming.accept();
+ session.txCommit();
+}
+
+void SessionImpl::rollbackImpl()
+{
+ ScopedLock l(lock);
+ for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) {
+ getImplPtr<Receiver, ReceiverImpl>(i->second)->stop();
+ }
+ //ensure that stop has been processed and all previously sent
+ //messages are available for release:
+ session.sync();
+ incoming.releaseAll();
+ session.txRollback();
+
+ for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) {
+ getImplPtr<Receiver, ReceiverImpl>(i->second)->start();
+ }
+}
+
+void SessionImpl::acknowledgeImpl()
+{
+ ScopedLock l(lock);
+ if (!transactional) incoming.accept();
+}
+
+void SessionImpl::acknowledgeImpl(qpid::messaging::Message& m)
+{
+ ScopedLock l(lock);
+ if (!transactional) incoming.accept(MessageImplAccess::get(m).getInternalId());
+}
+
+void SessionImpl::rejectImpl(qpid::messaging::Message& m)
+{
+ SequenceSet set;
+ set.add(MessageImplAccess::get(m).getInternalId());
+ session.messageReject(set);
+}
+
+void SessionImpl::releaseImpl(qpid::messaging::Message& m)
+{
+ SequenceSet set;
+ set.add(MessageImplAccess::get(m).getInternalId());
+ session.messageRelease(set);
+}
+
+void SessionImpl::receiverCancelled(const std::string& name)
+{
+ ScopedLock l(lock);
+ receivers.erase(name);
+ session.sync();
+ incoming.releasePending(name);
+}
+
+void SessionImpl::releasePending(const std::string& name)
+{
+ ScopedLock l(lock);
+ incoming.releasePending(name);
+}
+
+void SessionImpl::senderCancelled(const std::string& name)
+{
+ ScopedLock l(lock);
+ senders.erase(name);
+}
+
+void SessionImpl::reconnect()
+{
+ connection->open();
+}
+
+bool SessionImpl::backoff()
+{
+ return connection->backoff();
+}
+
+qpid::messaging::Connection SessionImpl::getConnection() const
+{
+ return qpid::messaging::Connection(connection.get());
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h
new file mode 100644
index 0000000000..2a2aa47df6
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h
@@ -0,0 +1,247 @@
+#ifndef QPID_CLIENT_AMQP0_10_SESSIONIMPL_H
+#define QPID_CLIENT_AMQP0_10_SESSIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/IncomingMessages.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/framing/reply_exceptions.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+
+namespace messaging {
+class Address;
+class Connection;
+class Message;
+class Receiver;
+class Sender;
+class Session;
+}
+
+namespace client {
+namespace amqp0_10 {
+
+class ConnectionImpl;
+class ReceiverImpl;
+class SenderImpl;
+
+/**
+ * Implementation of the protocol independent Session interface using
+ * AMQP 0-10.
+ */
+class SessionImpl : public qpid::messaging::SessionImpl
+{
+ public:
+ SessionImpl(ConnectionImpl&, bool transactional);
+ void commit();
+ void rollback();
+ void acknowledge(bool sync);
+ void reject(qpid::messaging::Message&);
+ void release(qpid::messaging::Message&);
+ void acknowledge(qpid::messaging::Message& msg);
+ void close();
+ void sync(bool block);
+ qpid::messaging::Sender createSender(const qpid::messaging::Address& address);
+ qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address);
+
+ qpid::messaging::Sender getSender(const std::string& name) const;
+ qpid::messaging::Receiver getReceiver(const std::string& name) const;
+
+ bool nextReceiver(qpid::messaging::Receiver& receiver, qpid::messaging::Duration timeout);
+ qpid::messaging::Receiver nextReceiver(qpid::messaging::Duration timeout);
+
+ qpid::messaging::Connection getConnection() const;
+ void checkError();
+ bool hasError();
+
+ bool get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::messaging::Duration timeout);
+
+ void releasePending(const std::string& destination);
+ void receiverCancelled(const std::string& name);
+ void senderCancelled(const std::string& name);
+
+ uint32_t getReceivable();
+ uint32_t getReceivable(const std::string& destination);
+
+ uint32_t getUnsettledAcks();
+ uint32_t getUnsettledAcks(const std::string& destination);
+
+ void setSession(qpid::client::Session);
+
+ template <class T> bool execute(T& f)
+ {
+ try {
+ f();
+ return true;
+ } catch (const qpid::TransportFailure&) {
+ reconnect();
+ return false;
+ } catch (const qpid::framing::ResourceLimitExceededException& e) {
+ if (backoff()) return false;
+ else throw qpid::messaging::TargetCapacityExceeded(e.what());
+ } catch (const qpid::framing::UnauthorizedAccessException& e) {
+ throw qpid::messaging::UnauthorizedAccess(e.what());
+ } catch (const qpid::SessionException& e) {
+ throw qpid::messaging::SessionError(e.what());
+ } catch (const qpid::ConnectionException& e) {
+ throw qpid::messaging::ConnectionError(e.what());
+ } catch (const qpid::ChannelException& e) {
+ throw qpid::messaging::MessagingException(e.what());
+ }
+ }
+
+ static SessionImpl& convert(qpid::messaging::Session&);
+
+ private:
+ typedef std::map<std::string, qpid::messaging::Receiver> Receivers;
+ typedef std::map<std::string, qpid::messaging::Sender> Senders;
+
+ mutable qpid::sys::Mutex lock;
+ boost::intrusive_ptr<ConnectionImpl> connection;
+ qpid::client::Session session;
+ AddressResolution resolver;
+ IncomingMessages incoming;
+ Receivers receivers;
+ Senders senders;
+ const bool transactional;
+
+ bool accept(ReceiverImpl*, qpid::messaging::Message*, IncomingMessages::MessageTransfer&);
+ bool getIncoming(IncomingMessages::Handler& handler, qpid::messaging::Duration timeout);
+ bool getNextReceiver(qpid::messaging::Receiver* receiver, IncomingMessages::MessageTransfer& transfer);
+ void reconnect();
+ bool backoff();
+
+ void commitImpl();
+ void rollbackImpl();
+ void acknowledgeImpl();
+ void acknowledgeImpl(qpid::messaging::Message&);
+ void rejectImpl(qpid::messaging::Message&);
+ void releaseImpl(qpid::messaging::Message&);
+ void closeImpl();
+ void syncImpl(bool block);
+ qpid::messaging::Sender createSenderImpl(const qpid::messaging::Address& address);
+ qpid::messaging::Receiver createReceiverImpl(const qpid::messaging::Address& address);
+ uint32_t getReceivableImpl(const std::string* destination);
+ uint32_t getUnsettledAcksImpl(const std::string* destination);
+
+ //functors for public facing methods (allows locking and retry
+ //logic to be centralised)
+ struct Command
+ {
+ SessionImpl& impl;
+
+ Command(SessionImpl& i) : impl(i) {}
+ };
+
+ struct Commit : Command
+ {
+ Commit(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.commitImpl(); }
+ };
+
+ struct Rollback : Command
+ {
+ Rollback(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.rollbackImpl(); }
+ };
+
+ struct Acknowledge : Command
+ {
+ Acknowledge(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.acknowledgeImpl(); }
+ };
+
+ struct Sync : Command
+ {
+ Sync(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.syncImpl(true); }
+ };
+
+ struct NonBlockingSync : Command
+ {
+ NonBlockingSync(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.syncImpl(false); }
+ };
+
+ struct Reject : Command
+ {
+ qpid::messaging::Message& message;
+
+ Reject(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {}
+ void operator()() { impl.rejectImpl(message); }
+ };
+
+ struct Release : Command
+ {
+ qpid::messaging::Message& message;
+
+ Release(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {}
+ void operator()() { impl.releaseImpl(message); }
+ };
+
+ struct Acknowledge1 : Command
+ {
+ qpid::messaging::Message& message;
+
+ Acknowledge1(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {}
+ void operator()() { impl.acknowledgeImpl(message); }
+ };
+
+ struct CreateSender;
+ struct CreateReceiver;
+ struct UnsettledAcks;
+ struct Receivable;
+
+ //helper templates for some common patterns
+ template <class F> bool execute()
+ {
+ F f(*this);
+ return execute(f);
+ }
+
+ template <class F> void retry()
+ {
+ while (!execute<F>()) {}
+ }
+
+ template <class F, class P> bool execute1(P p)
+ {
+ F f(*this, p);
+ return execute(f);
+ }
+
+ template <class F, class R, class P> R get1(P p)
+ {
+ F f(*this, p);
+ while (!execute(f)) {}
+ return f.result;
+ }
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_SESSIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp
new file mode 100644
index 0000000000..327c2274a6
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp
@@ -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.
+ *
+ */
+#include "SimpleUrlParser.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/Exception.h"
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+bool split(const std::string& in, char delim, std::pair<std::string, std::string>& result)
+{
+ std::string::size_type i = in.find(delim);
+ if (i != std::string::npos) {
+ result.first = in.substr(0, i);
+ if (i+1 < in.size()) {
+ result.second = in.substr(i+1);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void parseUsernameAndPassword(const std::string& in, qpid::client::ConnectionSettings& result)
+{
+ std::pair<std::string, std::string> parts;
+ if (!split(in, '/', parts)) {
+ result.username = in;
+ } else {
+ result.username = parts.first;
+ result.password = parts.second;
+ }
+}
+
+void parseHostAndPort(const std::string& in, qpid::client::ConnectionSettings& result)
+{
+ std::pair<std::string, std::string> parts;
+ if (!split(in, ':', parts)) {
+ result.host = in;
+ } else {
+ result.host = parts.first;
+ if (parts.second.size()) {
+ result.port = boost::lexical_cast<uint16_t>(parts.second);
+ }
+ }
+}
+
+void SimpleUrlParser::parse(const std::string& url, qpid::client::ConnectionSettings& result)
+{
+ std::pair<std::string, std::string> parts;
+ if (!split(url, '@', parts)) {
+ parseHostAndPort(url, result);
+ } else {
+ parseUsernameAndPassword(parts.first, result);
+ parseHostAndPort(parts.second, result);
+ }
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h
new file mode 100644
index 0000000000..24f90ca9d6
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h
@@ -0,0 +1,42 @@
+#ifndef QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H
+#define QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+
+namespace qpid {
+namespace client {
+
+struct ConnectionSettings;
+
+namespace amqp0_10 {
+
+/**
+ * Parses a simple url of the form user/password@hostname:port
+ */
+struct SimpleUrlParser
+{
+ static void parse(const std::string& url, qpid::client::ConnectionSettings& result);
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H*/
diff --git a/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp
new file mode 100644
index 0000000000..d1ae762f1b
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/SaslFactory.h"
+
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/log/Statement.h"
+
+#include "boost/tokenizer.hpp"
+
+namespace qpid {
+
+using qpid::sys::SecurityLayer;
+using qpid::sys::SecuritySettings;
+using qpid::framing::InternalErrorException;
+
+struct WindowsSaslSettings
+{
+ WindowsSaslSettings ( ) :
+ username ( std::string(0) ),
+ password ( std::string(0) ),
+ service ( std::string(0) ),
+ host ( std::string(0) ),
+ minSsf ( 0 ),
+ maxSsf ( 0 )
+ {
+ }
+
+ WindowsSaslSettings ( const std::string & user, const std::string & password, const std::string & service, const std::string & host, int minSsf, int maxSsf ) :
+ username(user),
+ password(password),
+ service(service),
+ host(host),
+ minSsf(minSsf),
+ maxSsf(maxSsf)
+ {
+ }
+
+ std::string username,
+ password,
+ service,
+ host;
+
+ int minSsf,
+ maxSsf;
+};
+
+class WindowsSasl : public Sasl
+{
+ public:
+ WindowsSasl( const std::string &, const std::string &, const std::string &, const std::string &, int, int );
+ ~WindowsSasl();
+ std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings);
+ std::string step(const std::string& challenge);
+ std::string getMechanism();
+ std::string getUserId();
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+ private:
+ WindowsSaslSettings settings;
+ std::string mechanism;
+};
+
+qpid::sys::Mutex SaslFactory::lock;
+std::auto_ptr<SaslFactory> SaslFactory::instance;
+
+SaslFactory::SaslFactory()
+{
+}
+
+SaslFactory::~SaslFactory()
+{
+}
+
+SaslFactory& SaslFactory::getInstance()
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (!instance.get()) {
+ instance = std::auto_ptr<SaslFactory>(new SaslFactory());
+ }
+ return *instance;
+}
+
+std::auto_ptr<Sasl> SaslFactory::create( const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool )
+{
+ std::auto_ptr<Sasl> sasl(new WindowsSasl( username, password, serviceName, hostName, minSsf, maxSsf ));
+ return sasl;
+}
+
+namespace {
+ const std::string ANONYMOUS = "ANONYMOUS";
+ const std::string PLAIN = "PLAIN";
+}
+
+WindowsSasl::WindowsSasl( const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf )
+ : settings(username, password, serviceName, hostName, minSsf, maxSsf)
+{
+}
+
+WindowsSasl::~WindowsSasl()
+{
+}
+
+std::string WindowsSasl::start(const std::string& mechanisms,
+ const SecuritySettings* /*externalSettings*/)
+{
+ QPID_LOG(debug, "WindowsSasl::start(" << mechanisms << ")");
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ bool havePlain = false;
+ bool haveAnon = false;
+ tokenizer mechs(mechanisms, sep);
+ for (tokenizer::iterator mech = mechs.begin();
+ mech != mechs.end();
+ ++mech) {
+ if (*mech == ANONYMOUS)
+ haveAnon = true;
+ else if (*mech == PLAIN)
+ havePlain = true;
+ }
+ if (!haveAnon && !havePlain)
+ throw InternalErrorException(QPID_MSG("Sasl error: no common mechanism"));
+
+ std::string resp = "";
+ if (havePlain) {
+ mechanism = PLAIN;
+ resp = ((char)0) + settings.username + ((char)0) + settings.password;
+ }
+ else {
+ mechanism = ANONYMOUS;
+ }
+ return resp;
+}
+
+std::string WindowsSasl::step(const std::string& /*challenge*/)
+{
+ // Shouldn't get this for PLAIN...
+ throw InternalErrorException(QPID_MSG("Sasl step error"));
+}
+
+std::string WindowsSasl::getMechanism()
+{
+ return mechanism;
+}
+
+std::string WindowsSasl::getUserId()
+{
+ return std::string(); // TODO - when GSSAPI is supported, return userId for connection.
+}
+
+std::auto_ptr<SecurityLayer> WindowsSasl::getSecurityLayer(uint16_t /*maxFrameSize*/)
+{
+ return std::auto_ptr<SecurityLayer>(0);
+}
+
+} // namespace qpid
diff --git a/qpid/cpp/src/qpid/client/windows/SslConnector.cpp b/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
new file mode 100644
index 0000000000..785c817928
--- /dev/null
+++ b/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/client/TCPConnector.h"
+
+#include "config.h"
+#include "qpid/Msg.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/windows/check.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include <memory.h>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+#include <winsock2.h>
+
+namespace qpid {
+namespace client {
+namespace windows {
+
+using namespace qpid::sys;
+using boost::format;
+using boost::str;
+
+
+class SslConnector : public qpid::client::TCPConnector
+{
+ qpid::sys::windows::ClientSslAsynchIO *shim;
+ boost::shared_ptr<qpid::sys::Poller> poller;
+ std::string brokerHost;
+ SCHANNEL_CRED cred;
+ CredHandle credHandle;
+ TimeStamp credExpiry;
+
+ virtual ~SslConnector();
+ void negotiationDone(SECURITY_STATUS status);
+
+ // A number of AsynchIO callbacks go right through to TCPConnector, but
+ // we can't boost::bind to a protected ancestor, so these methods redirect
+ // to those TCPConnector methods.
+ bool redirectReadbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void redirectWritebuff(qpid::sys::AsynchIO&);
+ void redirectEof(qpid::sys::AsynchIO&);
+
+public:
+ SslConnector(boost::shared_ptr<qpid::sys::Poller>,
+ framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+ virtual void connect(const std::string& host, const std::string& port);
+ virtual void connected(const Socket&);
+ unsigned int getSSF();
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(boost::shared_ptr<qpid::sys::Poller> p,
+ framing::ProtocolVersion v,
+ const ConnectionSettings& s,
+ ConnectionImpl* c) {
+ return new SslConnector(p, v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ try {
+ Connector::registerFactory("ssl", &create);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL connector: " << e.what());
+ }
+ };
+ ~StaticInit() { }
+ } init;
+}
+
+void SslConnector::negotiationDone(SECURITY_STATUS status)
+{
+ if (status == SEC_E_OK)
+ initAmqp();
+ else
+ connectFailed(QPID_MSG(qpid::sys::strError(status)));
+}
+
+bool SslConnector::redirectReadbuff(qpid::sys::AsynchIO& a,
+ qpid::sys::AsynchIOBufferBase* b) {
+ return readbuff(a, b);
+}
+
+void SslConnector::redirectWritebuff(qpid::sys::AsynchIO& a) {
+ writebuff(a);
+}
+
+void SslConnector::redirectEof(qpid::sys::AsynchIO& a) {
+ eof(a);
+}
+
+SslConnector::SslConnector(boost::shared_ptr<qpid::sys::Poller> p,
+ framing::ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : TCPConnector(p, ver, settings, cimpl), shim(0), poller(p)
+{
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ &credExpiry);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+ QPID_LOG(debug, "SslConnector created for " << ver.toString());
+}
+
+SslConnector::~SslConnector()
+{
+ ::FreeCredentialsHandle(&credHandle);
+}
+
+ // Will this get reach via virtual method via boost::bind????
+
+void SslConnector::connect(const std::string& host, const std::string& port) {
+ brokerHost = host;
+ TCPConnector::connect(host, port);
+}
+
+void SslConnector::connected(const Socket& s) {
+ shim = new qpid::sys::windows::ClientSslAsynchIO(brokerHost,
+ s,
+ credHandle,
+ boost::bind(&SslConnector::redirectReadbuff, this, _1, _2),
+ boost::bind(&SslConnector::redirectEof, this, _1),
+ boost::bind(&SslConnector::redirectEof, this, _1),
+ 0, // closed
+ 0, // nobuffs
+ boost::bind(&SslConnector::redirectWritebuff, this, _1),
+ boost::bind(&SslConnector::negotiationDone, this, _1));
+ start(shim);
+ shim->start(poller);
+}
+
+unsigned int SslConnector::getSSF()
+{
+ return shim->getSslKeySize();
+}
+
+}}} // namespace qpid::client::windows
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp
new file mode 100644
index 0000000000..0daf0c7f5a
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp
@@ -0,0 +1,1176 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/**
+ * <h1>CLUSTER IMPLEMENTATION OVERVIEW</h1>
+ *
+ * The cluster works on the principle that if all members of the
+ * cluster receive identical input, they will all produce identical
+ * results. cluster::Connections intercept data received from clients
+ * and multicast it via CPG. The data is processed (passed to the
+ * broker::Connection) only when it is received from CPG in cluster
+ * order. Each cluster member has Connection objects for directly
+ * connected clients and "shadow" Connection objects for connections
+ * to other members.
+ *
+ * This assumes that all broker actions occur deterministically in
+ * response to data arriving on client connections. There are two
+ * situations where this assumption fails:
+ * - sending data in response to polling local connections for writabiliy.
+ * - taking actions based on a timer or timestamp comparison.
+ *
+ * IMPORTANT NOTE: any time code is added to the broker that uses timers,
+ * the cluster may need to be updated to take account of this.
+ *
+ *
+ * USE OF TIMESTAMPS IN THE BROKER
+ *
+ * The following are the current areas where broker uses timers or timestamps:
+ *
+ * - Producer flow control: broker::SemanticState uses
+ * connection::getClusterOrderOutput. a FrameHandler that sends
+ * frames to the client via the cluster. Used by broker::SessionState
+ *
+ * - QueueCleaner, Message TTL: uses ExpiryPolicy, which is
+ * implemented by cluster::ExpiryPolicy.
+ *
+ * - Connection heartbeat: sends connection controls, not part of
+ * session command counting so OK to ignore.
+ *
+ * - LinkRegistry: only cluster elder is ever active for links.
+ *
+ * - management::ManagementBroker: uses MessageHandler supplied by cluster
+ * to send messages to the broker via the cluster.
+ *
+ * - Dtx: not yet supported with cluster.
+ *
+ * cluster::ExpiryPolicy implements the strategy for message expiry.
+ *
+ * ClusterTimer implements periodic timed events in the cluster context.
+ * Used for periodic management events.
+ *
+ * <h1>CLUSTER PROTOCOL OVERVIEW</h1>
+ *
+ * Messages sent to/from CPG are called Events.
+ *
+ * An Event carries a ConnectionId, which includes a MemberId and a
+ * connection number.
+ *
+ * Events are either
+ * - Connection events: non-0 connection number and are associated with a connection.
+ * - Cluster Events: 0 connection number, are not associated with a connection.
+ *
+ * Events are further categorized as:
+ * - Control: carries method frame(s) that affect cluster behavior.
+ * - Data: carries raw data received from a client connection.
+ *
+ * The cluster defines extensions to the AMQP command set in ../../../xml/cluster.xml
+ * which defines two classes:
+ * - cluster: cluster control information.
+ * - cluster.connection: control information for a specific connection.
+ *
+ * The following combinations are legal:
+ * - Data frames carrying connection data.
+ * - Cluster control events carrying cluster commands.
+ * - Connection control events carrying cluster.connection commands.
+ * - Connection control events carrying non-cluster frames: frames sent to the client.
+ * e.g. flow-control frames generated on a timer.
+ *
+ * <h1>CLUSTER INITIALIZATION OVERVIEW</h1>
+ *
+ * @see InitialStatusMap
+ *
+ * When a new member joins the CPG group, all members (including the
+ * new one) multicast their "initial status." The new member is in
+ * PRE_INIT mode until it gets a complete set of initial status
+ * messages from all cluster members. In a newly-forming cluster is
+ * then in INIT mode until the configured cluster-size members have
+ * joined.
+ *
+ * The newcomer uses initial status to determine
+ * - The cluster UUID
+ * - Am I speaking the correct version of the cluster protocol?
+ * - Do I need to get an update from an existing active member?
+ * - Can I recover from my own store?
+ *
+ * Pre-initialization happens in the Cluster constructor (plugin
+ * early-init phase) because it needs to set the recovery flag before
+ * the store initializes. This phase lasts until inital-status is
+ * received for all active members. The PollableQueues and Multicaster
+ * are in "bypass" mode during this phase since the poller has not
+ * started so there are no threads to serve pollable queues.
+ *
+ * The remaining initialization happens in Cluster::initialize() or,
+ * if cluster-size=N is specified, in the deliver thread when an
+ * initial-status control is delivered that brings the total to N.
+ */
+#include "qpid/Exception.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/sys/ClusterSafe.h"
+#include "qpid/cluster/ClusterSettings.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/cluster/RetractClient.h"
+#include "qpid/cluster/FailoverExchange.h"
+#include "qpid/cluster/UpdateDataExchange.h"
+#include "qpid/cluster/UpdateExchange.h"
+#include "qpid/cluster/ClusterTimer.h"
+
+#include "qpid/assert.h"
+#include "qmf/org/apache/qpid/cluster/ArgsClusterStopClusterNode.h"
+#include "qmf/org/apache/qpid/cluster/Package.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/SignalHandler.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQP_AllOperations.h"
+#include "qpid/framing/AllInvoker.h"
+#include "qpid/framing/ClusterConfigChangeBody.h"
+#include "qpid/framing/ClusterConnectionDeliverCloseBody.h"
+#include "qpid/framing/ClusterConnectionAbortBody.h"
+#include "qpid/framing/ClusterRetractOfferBody.h"
+#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h"
+#include "qpid/framing/ClusterReadyBody.h"
+#include "qpid/framing/ClusterShutdownBody.h"
+#include "qpid/framing/ClusterUpdateOfferBody.h"
+#include "qpid/framing/ClusterUpdateRequestBody.h"
+#include "qpid/framing/ClusterConnectionAnnounceBody.h"
+#include "qpid/framing/ClusterErrorCheckBody.h"
+#include "qpid/framing/ClusterTimerWakeupBody.h"
+#include "qpid/framing/ClusterDeliverToQueueBody.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Helpers.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/memory.h"
+#include "qpid/sys/Thread.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/cast.hpp>
+#include <boost/current_function.hpp>
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <ostream>
+
+
+namespace qpid {
+namespace cluster {
+using namespace qpid;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace qpid::cluster;
+using namespace framing::cluster;
+using namespace std;
+using management::ManagementAgent;
+using management::ManagementObject;
+using management::Manageable;
+using management::Args;
+namespace _qmf = ::qmf::org::apache::qpid::cluster;
+
+/**
+ * NOTE: must increment this number whenever any incompatible changes in
+ * cluster protocol/behavior are made. It allows early detection and
+ * sensible reporting of an attempt to mix different versions in a
+ * cluster.
+ *
+ * Currently use SVN revision to avoid clashes with versions from
+ * different branches.
+ */
+const uint32_t Cluster::CLUSTER_VERSION = 1097431;
+
+struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler {
+ qpid::cluster::Cluster& cluster;
+ MemberId member;
+ Cluster::Lock& l;
+ ClusterDispatcher(Cluster& c, const MemberId& id, Cluster::Lock& l_) : cluster(c), member(id), l(l_) {}
+
+ void updateRequest(const std::string& url) { cluster.updateRequest(member, url, l); }
+
+ void initialStatus(uint32_t version, bool active, const Uuid& clusterId,
+ uint8_t storeState, const Uuid& shutdownId,
+ const std::string& firstConfig)
+ {
+ cluster.initialStatus(
+ member, version, active, clusterId,
+ framing::cluster::StoreState(storeState), shutdownId,
+ firstConfig, l);
+ }
+ void ready(const std::string& url) {
+ cluster.ready(member, url, l);
+ }
+ void configChange(const std::string& members,
+ const std::string& left,
+ const std::string& joined)
+ {
+ cluster.configChange(member, members, left, joined, l);
+ }
+ void updateOffer(uint64_t updatee) {
+ cluster.updateOffer(member, updatee, l);
+ }
+ void retractOffer(uint64_t updatee) { cluster.retractOffer(member, updatee, l); }
+ void messageExpired(uint64_t id) { cluster.messageExpired(member, id, l); }
+ void errorCheck(uint8_t type, const framing::SequenceNumber& frameSeq) {
+ cluster.errorCheck(member, type, frameSeq, l);
+ }
+ void timerWakeup(const std::string& name) { cluster.timerWakeup(member, name, l); }
+ void timerDrop(const std::string& name) { cluster.timerDrop(member, name, l); }
+ void shutdown(const Uuid& id) { cluster.shutdown(member, id, l); }
+ void deliverToQueue(const std::string& queue, const std::string& message) {
+ cluster.deliverToQueue(queue, message, l);
+ }
+ bool invoke(AMQBody& body) { return framing::invoke(*this, body).wasHandled(); }
+};
+
+Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
+ settings(set),
+ broker(b),
+ mgmtObject(0),
+ poller(b.getPoller()),
+ cpg(*this),
+ name(settings.name),
+ self(cpg.self()),
+ clusterId(true),
+ mAgent(0),
+ expiryPolicy(new ExpiryPolicy(mcast, self, broker.getTimer())),
+ mcast(cpg, poller, boost::bind(&Cluster::leave, this)),
+ dispatcher(cpg, poller, boost::bind(&Cluster::leave, this)),
+ deliverEventQueue(boost::bind(&Cluster::deliveredEvent, this, _1),
+ boost::bind(&Cluster::leave, this),
+ "Error decoding events, may indicate a broker version mismatch",
+ poller),
+ deliverFrameQueue(boost::bind(&Cluster::deliveredFrame, this, _1),
+ boost::bind(&Cluster::leave, this),
+ "Error delivering frames",
+ poller),
+ failoverExchange(new FailoverExchange(broker.GetVhostObject(), &broker)),
+ updateDataExchange(new UpdateDataExchange(*this)),
+ quorum(boost::bind(&Cluster::leave, this)),
+ decoder(boost::bind(&Cluster::deliverFrame, this, _1)),
+ discarding(true),
+ state(PRE_INIT),
+ initMap(self, settings.size),
+ store(broker.getDataDir().getPath()),
+ elder(false),
+ lastAliveCount(0),
+ lastBroker(false),
+ updateRetracted(false),
+ updateClosed(false),
+ error(*this)
+{
+ broker.setInCluster(true);
+
+ // We give ownership of the timer to the broker and keep a plain pointer.
+ // This is OK as it means the timer has the same lifetime as the broker.
+ timer = new ClusterTimer(*this);
+ broker.setClusterTimer(std::auto_ptr<sys::Timer>(timer));
+
+ // Failover exchange provides membership updates to clients.
+ broker.getExchanges().registerExchange(failoverExchange);
+
+ // Update exchange is used during updates to replicate messages
+ // without modifying delivery-properties.exchange.
+ broker.getExchanges().registerExchange(
+ boost::shared_ptr<broker::Exchange>(new UpdateExchange(this)));
+
+ // Update-data exchange is used for passing data that may be too large
+ // for single control frame.
+ broker.getExchanges().registerExchange(updateDataExchange);
+
+ // Load my store status before we go into initialization
+ if (! broker::NullMessageStore::isNullStore(&broker.getStore())) {
+ store.load();
+ clusterId = store.getClusterId();
+ QPID_LOG(notice, "Cluster store state: " << store)
+ }
+ cpg.join(name);
+ // pump the CPG dispatch manually till we get past PRE_INIT.
+ while (state == PRE_INIT)
+ cpg.dispatchOne();
+}
+
+Cluster::~Cluster() {
+ broker.setClusterTimer(std::auto_ptr<sys::Timer>(0)); // Delete cluster timer
+ if (updateThread) updateThread.join(); // Join the previous updatethread.
+}
+
+void Cluster::initialize() {
+ if (settings.quorum) quorum.start(poller);
+ if (settings.url.empty())
+ myUrl = Url::getIpAddressesUrl(broker.getPort(broker::Broker::TCP_TRANSPORT));
+ else
+ myUrl = settings.url;
+ broker.getKnownBrokers = boost::bind(&Cluster::getUrls, this);
+ broker.deferDelivery = boost::bind(&Cluster::deferDeliveryImpl, this, _1, _2);
+ broker.setExpiryPolicy(expiryPolicy);
+ deliverEventQueue.bypassOff();
+ deliverEventQueue.start();
+ deliverFrameQueue.bypassOff();
+ deliverFrameQueue.start();
+ mcast.start();
+
+ /// Create management object
+ mAgent = broker.getManagementAgent();
+ if (mAgent != 0){
+ _qmf::Package packageInit(mAgent);
+ mgmtObject = new _qmf::Cluster (mAgent, this, &broker,name,myUrl.str());
+ mAgent->addObject (mgmtObject);
+ }
+
+ // Run initMapCompleted immediately to process the initial configuration
+ // that allowed us to transition out of PRE_INIT
+ assert(state == INIT);
+ initMapCompleted(*(Mutex::ScopedLock*)0); // Fake lock, single-threaded context.
+
+ // Add finalizer last for exception safety.
+ broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this));
+
+ // Start dispatching CPG events.
+ dispatcher.start();
+}
+
+// Called in connection thread to insert a client connection.
+void Cluster::addLocalConnection(const boost::intrusive_ptr<Connection>& c) {
+ assert(c->getId().getMember() == self);
+ localConnections.insert(c);
+}
+
+// Called in connection thread to insert an updated shadow connection.
+void Cluster::addShadowConnection(const boost::intrusive_ptr<Connection>& c) {
+ QPID_LOG(debug, *this << " new shadow connection " << c->getId());
+ // Safe to use connections here because we're pre-catchup, stalled
+ // and discarding, so deliveredFrame is not processing any
+ // connection events.
+ assert(discarding);
+ pair<ConnectionMap::iterator, bool> ib
+ = connections.insert(ConnectionMap::value_type(c->getId(), c));
+ assert(ib.second);
+}
+
+void Cluster::erase(const ConnectionId& id) {
+ Lock l(lock);
+ erase(id,l);
+}
+
+// Called by Connection::deliverClose() in deliverFrameQueue thread.
+void Cluster::erase(const ConnectionId& id, Lock&) {
+ connections.erase(id);
+ decoder.erase(id);
+}
+
+std::vector<string> Cluster::getIds() const {
+ Lock l(lock);
+ return getIds(l);
+}
+
+std::vector<string> Cluster::getIds(Lock&) const {
+ return map.memberIds();
+}
+
+std::vector<Url> Cluster::getUrls() const {
+ Lock l(lock);
+ return getUrls(l);
+}
+
+std::vector<Url> Cluster::getUrls(Lock&) const {
+ return map.memberUrls();
+}
+
+void Cluster::leave() {
+ Lock l(lock);
+ leave(l);
+}
+
+#define LEAVE_TRY(STMT) try { STMT; } \
+ catch (const std::exception& e) { \
+ QPID_LOG(warning, *this << " error leaving cluster: " << e.what()); \
+ } do {} while(0)
+
+void Cluster::leave(Lock&) {
+ if (state != LEFT) {
+ state = LEFT;
+ QPID_LOG(notice, *this << " leaving cluster " << name);
+ // Finalize connections now now to avoid problems later in destructor.
+ ClusterSafeScope css; // Don't trigger cluster-safe assertions.
+ LEAVE_TRY(localConnections.clear());
+ LEAVE_TRY(connections.clear());
+ LEAVE_TRY(broker::SignalHandler::shutdown());
+ }
+}
+
+// Deliver CPG message.
+void Cluster::deliver(
+ cpg_handle_t /*handle*/,
+ const cpg_name* /*group*/,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ int msg_len)
+{
+ MemberId from(nodeid, pid);
+ framing::Buffer buf(static_cast<char*>(msg), msg_len);
+ Event e(Event::decodeCopy(from, buf));
+ deliverEvent(e);
+}
+
+void Cluster::deliverEvent(const Event& e) { deliverEventQueue.push(e); }
+
+void Cluster::deliverFrame(const EventFrame& e) { deliverFrameQueue.push(e); }
+
+const ClusterUpdateOfferBody* castUpdateOffer(const framing::AMQBody* body) {
+ return (body && body->getMethod() &&
+ body->getMethod()->isA<ClusterUpdateOfferBody>()) ?
+ static_cast<const ClusterUpdateOfferBody*>(body) : 0;
+}
+
+const ClusterConnectionAnnounceBody* castAnnounce( const framing::AMQBody *body) {
+ return (body && body->getMethod() &&
+ body->getMethod()->isA<ClusterConnectionAnnounceBody>()) ?
+ static_cast<const ClusterConnectionAnnounceBody*>(body) : 0;
+}
+
+// Handler for deliverEventQueue.
+// This thread decodes frames from events.
+void Cluster::deliveredEvent(const Event& e) {
+ if (e.isCluster()) {
+ EventFrame ef(e, e.getFrame());
+ // Stop the deliverEventQueue on update offers.
+ // This preserves the connection decoder fragments for an update.
+ // Only do this for the two brokers that are directly involved in this
+ // offer: the one making the offer, or the one receiving it.
+ const ClusterUpdateOfferBody* offer = castUpdateOffer(ef.frame.getBody());
+ if (offer && ( e.getMemberId() == self || MemberId(offer->getUpdatee()) == self) ) {
+ QPID_LOG(info, *this << " stall for update offer from " << e.getMemberId()
+ << " to " << MemberId(offer->getUpdatee()));
+ deliverEventQueue.stop();
+ }
+ deliverFrame(ef);
+ }
+ else if(!discarding) {
+ if (e.isControl())
+ deliverFrame(EventFrame(e, e.getFrame()));
+ else {
+ try { decoder.decode(e, e.getData()); }
+ catch (const Exception& ex) {
+ // Close a connection that is sending us invalid data.
+ QPID_LOG(error, *this << " aborting connection "
+ << e.getConnectionId() << ": " << ex.what());
+ framing::AMQFrame abort((ClusterConnectionAbortBody()));
+ deliverFrame(EventFrame(EventHeader(CONTROL, e.getConnectionId()), abort));
+ }
+ }
+ }
+}
+
+void Cluster::flagError(
+ Connection& connection, ErrorCheck::ErrorType type, const std::string& msg)
+{
+ Mutex::ScopedLock l(lock);
+ if (connection.isCatchUp()) {
+ QPID_LOG(critical, *this << " error on update connection " << connection
+ << ": " << msg);
+ leave(l);
+ }
+ error.error(connection, type, map.getFrameSeq(), map.getMembers(), msg);
+}
+
+// Handler for deliverFrameQueue.
+// This thread executes the main logic.
+void Cluster::deliveredFrame(const EventFrame& efConst) {
+ Mutex::ScopedLock l(lock);
+ sys::ClusterSafeScope css; // Don't trigger cluster-safe asserts.
+ if (state == LEFT) return;
+ EventFrame e(efConst);
+ const ClusterUpdateOfferBody* offer = castUpdateOffer(e.frame.getBody());
+ if (offer && error.isUnresolved()) {
+ // We can't honour an update offer that is delivered while an
+ // error is in progress so replace it with a retractOffer and re-start
+ // the event queue.
+ e.frame = AMQFrame(
+ ClusterRetractOfferBody(ProtocolVersion(), offer->getUpdatee()));
+ deliverEventQueue.start();
+ }
+ // Process each frame through the error checker.
+ if (error.isUnresolved()) {
+ error.delivered(e);
+ while (error.canProcess()) // There is a frame ready to process.
+ processFrame(error.getNext(), l);
+ }
+ else
+ processFrame(e, l);
+}
+
+
+void Cluster::processFrame(const EventFrame& e, Lock& l) {
+ if (e.isCluster()) {
+ QPID_LOG(trace, *this << " DLVR: " << e);
+ ClusterDispatcher dispatch(*this, e.connectionId.getMember(), l);
+ if (!framing::invoke(dispatch, *e.frame.getBody()).wasHandled())
+ throw Exception(QPID_MSG("Invalid cluster control"));
+ }
+ else if (state >= CATCHUP) {
+ map.incrementFrameSeq();
+ ConnectionPtr connection = getConnection(e, l);
+ if (connection) {
+ QPID_LOG(trace, *this << " DLVR " << map.getFrameSeq() << ": " << e);
+ connection->deliveredFrame(e);
+ }
+ else
+ throw Exception(QPID_MSG("Unknown connection: " << e));
+ }
+ else // Drop connection frames while state < CATCHUP
+ QPID_LOG(trace, *this << " DROP (joining): " << e);
+}
+
+// Called in deliverFrameQueue thread
+ConnectionPtr Cluster::getConnection(const EventFrame& e, Lock&) {
+ ConnectionId id = e.connectionId;
+ ConnectionMap::iterator i = connections.find(id);
+ if (i != connections.end()) return i->second;
+ ConnectionPtr cp;
+ // If the frame is an announcement for a new connection, add it.
+ const ClusterConnectionAnnounceBody *announce = castAnnounce(e.frame.getBody());
+ if (e.frame.getBody() && e.frame.getMethod() && announce)
+ {
+ if (id.getMember() == self) { // Announces one of my own
+ cp = localConnections.getErase(id);
+ assert(cp);
+ }
+ else { // New remote connection, create a shadow.
+ qpid::sys::SecuritySettings secSettings;
+ if (announce) {
+ secSettings.ssf = announce->getSsf();
+ secSettings.authid = announce->getAuthid();
+ secSettings.nodict = announce->getNodict();
+ }
+ cp = new Connection(*this, shadowOut, announce->getManagementId(), id, secSettings);
+ }
+ connections.insert(ConnectionMap::value_type(id, cp));
+ }
+ return cp;
+}
+
+Cluster::ConnectionVector Cluster::getConnections(Lock&) {
+ ConnectionVector result(connections.size());
+ std::transform(connections.begin(), connections.end(), result.begin(),
+ boost::bind(&ConnectionMap::value_type::second, _1));
+ return result;
+}
+
+// CPG config-change callback.
+void Cluster::configChange (
+ cpg_handle_t /*handle*/,
+ const cpg_name */*group*/,
+ const cpg_address *members, int nMembers,
+ const cpg_address *left, int nLeft,
+ const cpg_address *joined, int nJoined)
+{
+ Mutex::ScopedLock l(lock);
+ string membersStr, leftStr, joinedStr;
+ // Encode members and enqueue as an event so the config change can
+ // be executed in the correct thread.
+ for (const cpg_address* p = members; p < members+nMembers; ++p)
+ membersStr.append(MemberId(*p).str());
+ for (const cpg_address* p = left; p < left+nLeft; ++p)
+ leftStr.append(MemberId(*p).str());
+ for (const cpg_address* p = joined; p < joined+nJoined; ++p)
+ joinedStr.append(MemberId(*p).str());
+ deliverEvent(Event::control(ClusterConfigChangeBody(
+ ProtocolVersion(), membersStr, leftStr, joinedStr),
+ self));
+}
+
+void Cluster::setReady(Lock&) {
+ state = READY;
+ mcast.setReady();
+ broker.getQueueEvents().enable();
+ enableClusterSafe(); // Enable cluster-safe assertions.
+}
+
+// Set the management status from the Cluster::state.
+//
+// NOTE: Management updates are sent based on property changes. In
+// order to keep consistency across the cluster, we touch the local
+// management status property even if it is locally unchanged for any
+// event that could have cause a cluster property change on any cluster member.
+void Cluster::setMgmtStatus(Lock&) {
+ if (mgmtObject)
+ mgmtObject->set_status(state >= CATCHUP ? "ACTIVE" : "JOINING");
+}
+
+void Cluster::initMapCompleted(Lock& l) {
+ // Called on completion of the initial status map.
+ QPID_LOG(debug, *this << " initial status map complete. ");
+ setMgmtStatus(l);
+ if (state == PRE_INIT) {
+ // PRE_INIT means we're still in the earlyInitialize phase, in the constructor.
+ // We decide here whether we want to recover from our store.
+ // We won't recover if we are joining an active cluster or our store is dirty.
+ if (store.hasStore() &&
+ store.getState() != STORE_STATE_EMPTY_STORE &&
+ (initMap.isActive() || store.getState() == STORE_STATE_DIRTY_STORE))
+ broker.setRecovery(false); // Ditch my current store.
+ state = INIT;
+ }
+ else if (state == INIT) {
+ // INIT means we are past Cluster::initialize().
+
+ // If we're forming an initial cluster (no active members)
+ // then we wait to reach the configured cluster-size
+ if (!initMap.isActive() && initMap.getActualSize() < initMap.getRequiredSize()) {
+ QPID_LOG(info, *this << initMap.getActualSize()
+ << " members, waiting for at least " << initMap.getRequiredSize());
+ return;
+ }
+
+ initMap.checkConsistent();
+ elders = initMap.getElders();
+ QPID_LOG(debug, *this << " elders: " << elders);
+ if (elders.empty())
+ becomeElder(l);
+ else {
+ broker.getLinks().setPassive(true);
+ broker.getQueueEvents().disable();
+ QPID_LOG(info, *this << " not active for links.");
+ }
+ setClusterId(initMap.getClusterId(), l);
+
+ if (initMap.isUpdateNeeded()) { // Joining established cluster.
+ broker.setRecovery(false); // Ditch my current store.
+ broker.setClusterUpdatee(true);
+ if (mAgent) mAgent->suppress(true); // Suppress mgmt output during update.
+ state = JOINER;
+ mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self);
+ QPID_LOG(notice, *this << " joining cluster " << name);
+ }
+ else { // I can go ready.
+ discarding = false;
+ setReady(l);
+ memberUpdate(l);
+ updateMgmtMembership(l);
+ mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
+ QPID_LOG(notice, *this << " joined cluster " << name);
+ }
+ }
+}
+
+void Cluster::configChange(const MemberId&,
+ const std::string& membersStr,
+ const std::string& leftStr,
+ const std::string& joinedStr,
+ Lock& l)
+{
+ if (state == LEFT) return;
+ MemberSet members = decodeMemberSet(membersStr);
+ MemberSet left = decodeMemberSet(leftStr);
+ MemberSet joined = decodeMemberSet(joinedStr);
+ QPID_LOG(notice, *this << " configuration change: " << members);
+ QPID_LOG_IF(notice, !left.empty(), *this << " Members left: " << left);
+ QPID_LOG_IF(notice, !joined.empty(), *this << " Members joined: " << joined);
+
+ // If we are still joining, make sure there is someone to give us an update.
+ elders = intersection(elders, members);
+ if (elders.empty() && INIT < state && state < CATCHUP) {
+ QPID_LOG(critical, "Cannot update, all potential updaters left the cluster.");
+ leave(l);
+ return;
+ }
+ bool memberChange = map.configChange(members);
+
+ // Update initital status for members joining or leaving.
+ initMap.configChange(members);
+ if (initMap.isResendNeeded()) {
+ mcast.mcastControl(
+ ClusterInitialStatusBody(
+ ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId,
+ store.getState(), store.getShutdownId(),
+ initMap.getFirstConfigStr()
+ ),
+ self);
+ }
+ if (initMap.transitionToComplete()) initMapCompleted(l);
+
+ if (state >= CATCHUP && memberChange) {
+ memberUpdate(l);
+ if (elders.empty()) becomeElder(l);
+ }
+
+ updateMgmtMembership(l); // Update on every config change for consistency
+}
+
+void Cluster::becomeElder(Lock&) {
+ if (elder) return; // We were already the elder.
+ // We are the oldest, reactive links if necessary
+ QPID_LOG(info, *this << " became the elder, active for links.");
+ elder = true;
+ broker.getLinks().setPassive(false);
+ timer->becomeElder();
+}
+
+void Cluster::makeOffer(const MemberId& id, Lock& ) {
+ if (state == READY && map.isJoiner(id)) {
+ state = OFFER;
+ QPID_LOG(info, *this << " send update-offer to " << id);
+ mcast.mcastControl(ClusterUpdateOfferBody(ProtocolVersion(), id), self);
+ }
+}
+
+namespace {
+struct AppendQueue {
+ ostream* os;
+ AppendQueue(ostream& o) : os(&o) {}
+ void operator()(const boost::shared_ptr<broker::Queue>& q) {
+ (*os) << " " << q->getName() << "=" << q->getMessageCount();
+ }
+};
+} // namespace
+
+// Log a snapshot of broker state, used for debugging inconsistency problems.
+// May only be called in deliver thread.
+std::string Cluster::debugSnapshot() {
+ assertClusterSafe();
+ std::ostringstream msg;
+ msg << "Member joined, frameSeq=" << map.getFrameSeq() << ", queue snapshot:";
+ AppendQueue append(msg);
+ broker.getQueues().eachQueue(append);
+ return msg.str();
+}
+
+// Called from Broker::~Broker when broker is shut down. At this
+// point we know the poller has stopped so no poller callbacks will be
+// invoked. We must ensure that CPG has also shut down so no CPG
+// callbacks will be invoked.
+//
+void Cluster::brokerShutdown() {
+ sys::ClusterSafeScope css; // Don't trigger cluster-safe asserts.
+ try { cpg.shutdown(); }
+ catch (const std::exception& e) {
+ QPID_LOG(error, *this << " shutting down CPG: " << e.what());
+ }
+ delete this;
+}
+
+void Cluster::updateRequest(const MemberId& id, const std::string& url, Lock& l) {
+ map.updateRequest(id, url);
+ makeOffer(id, l);
+}
+
+void Cluster::initialStatus(const MemberId& member, uint32_t version, bool active,
+ const framing::Uuid& id,
+ framing::cluster::StoreState store,
+ const framing::Uuid& shutdownId,
+ const std::string& firstConfig,
+ Lock& l)
+{
+ if (version != CLUSTER_VERSION) {
+ QPID_LOG(critical, *this << " incompatible cluster versions " <<
+ version << " != " << CLUSTER_VERSION);
+ leave(l);
+ return;
+ }
+ QPID_LOG_IF(debug, state == PRE_INIT, *this
+ << " received initial status from " << member);
+ initMap.received(
+ member,
+ ClusterInitialStatusBody(ProtocolVersion(), version, active, id,
+ store, shutdownId, firstConfig)
+ );
+ if (initMap.transitionToComplete()) initMapCompleted(l);
+}
+
+void Cluster::ready(const MemberId& id, const std::string& url, Lock& l) {
+ try {
+ if (map.ready(id, Url(url)))
+ memberUpdate(l);
+ if (state == CATCHUP && id == self) {
+ setReady(l);
+ QPID_LOG(notice, *this << " caught up.");
+ }
+ } catch (const Url::Invalid& e) {
+ QPID_LOG(error, "Invalid URL in cluster ready command: " << url);
+ }
+ // Update management on every ready event to be consistent across cluster.
+ setMgmtStatus(l);
+ updateMgmtMembership(l);
+}
+
+void Cluster::updateOffer(const MemberId& updater, uint64_t updateeInt, Lock& l) {
+ // NOTE: deliverEventQueue has been stopped at the update offer by
+ // deliveredEvent in case an update is required.
+ if (state == LEFT) return;
+ MemberId updatee(updateeInt);
+ boost::optional<Url> url = map.updateOffer(updater, updatee);
+ if (updater == self) {
+ assert(state == OFFER);
+ if (url) // My offer was first.
+ updateStart(updatee, *url, l);
+ else { // Another offer was first.
+ QPID_LOG(info, *this << " cancelled offer to " << updatee << " unstall");
+ setReady(l);
+ makeOffer(map.firstJoiner(), l); // Maybe make another offer.
+ deliverEventQueue.start(); // Go back to normal processing
+ }
+ }
+ else if (updatee == self && url) {
+ assert(state == JOINER);
+ state = UPDATEE;
+ QPID_LOG(notice, *this << " receiving update from " << updater);
+ checkUpdateIn(l);
+ }
+ else {
+ QPID_LOG(info, *this << " unstall, ignore update " << updater
+ << " to " << updatee);
+ deliverEventQueue.start(); // Not involved in update.
+ }
+ if (updatee != self && url) {
+ QPID_LOG(debug, debugSnapshot());
+ if (mAgent) mAgent->clusterUpdate();
+ // Updatee will call clusterUpdate when update completes
+ }
+}
+
+static client::ConnectionSettings connectionSettings(const ClusterSettings& settings) {
+ client::ConnectionSettings cs;
+ cs.username = settings.username;
+ cs.password = settings.password;
+ cs.mechanism = settings.mechanism;
+ return cs;
+}
+
+void Cluster::retractOffer(const MemberId& updater, uint64_t updateeInt, Lock& l) {
+ // An offer was received while handling an error, and converted to a retract.
+ // Behavior is very similar to updateOffer.
+ if (state == LEFT) return;
+ MemberId updatee(updateeInt);
+ boost::optional<Url> url = map.updateOffer(updater, updatee);
+ if (updater == self) {
+ assert(state == OFFER);
+ if (url) { // My offer was first.
+ if (updateThread)
+ updateThread.join(); // Join the previous updateThread to avoid leaks.
+ updateThread = Thread(new RetractClient(*url, connectionSettings(settings)));
+ }
+ setReady(l);
+ makeOffer(map.firstJoiner(), l); // Maybe make another offer.
+ // Don't unstall the event queue, that was already done in deliveredFrame
+ }
+ QPID_LOG(debug,*this << " retracted offer " << updater << " to " << updatee);
+}
+
+void Cluster::updateStart(const MemberId& updatee, const Url& url, Lock& l) {
+ // NOTE: deliverEventQueue is already stopped at the stall point by deliveredEvent.
+ if (state == LEFT) return;
+ assert(state == OFFER);
+ state = UPDATER;
+ QPID_LOG(notice, *this << " sending update to " << updatee << " at " << url);
+ if (updateThread)
+ updateThread.join(); // Join the previous updateThread to avoid leaks.
+ updateThread = Thread(
+ new UpdateClient(self, updatee, url, broker, map, *expiryPolicy,
+ getConnections(l), decoder,
+ boost::bind(&Cluster::updateOutDone, this),
+ boost::bind(&Cluster::updateOutError, this, _1),
+ connectionSettings(settings)));
+}
+
+// Called in network thread
+void Cluster::updateInClosed() {
+ Lock l(lock);
+ assert(!updateClosed);
+ updateClosed = true;
+ checkUpdateIn(l);
+}
+
+// Called in update thread.
+void Cluster::updateInDone(const ClusterMap& m) {
+ Lock l(lock);
+ updatedMap = m;
+ checkUpdateIn(l);
+}
+
+void Cluster::updateInRetracted() {
+ Lock l(lock);
+ updateRetracted = true;
+ map.clearStatus();
+ checkUpdateIn(l);
+}
+
+bool Cluster::isExpectingUpdate() {
+ Lock l(lock);
+ return state <= UPDATEE;
+}
+
+// Called in update thread or deliver thread.
+void Cluster::checkUpdateIn(Lock& l) {
+ if (state != UPDATEE) return; // Wait till we reach the stall point.
+ if (!updateClosed) return; // Wait till update connection closes.
+ if (updatedMap) { // We're up to date
+ map = *updatedMap;
+ failoverExchange->setUrls(getUrls(l));
+ mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
+ state = CATCHUP;
+ memberUpdate(l);
+ // NB: don't updateMgmtMembership() here as we are not in the deliver
+ // thread. It will be updated on delivery of the "ready" we just mcast.
+ broker.setClusterUpdatee(false);
+ discarding = false; // OK to set, we're stalled for update.
+ QPID_LOG(notice, *this << " update complete, starting catch-up.");
+ QPID_LOG(debug, debugSnapshot()); // OK to call because we're stalled.
+ if (mAgent) {
+ // Update management agent now, after all update activity is complete.
+ updateDataExchange->updateManagementAgent(mAgent);
+ mAgent->suppress(false); // Enable management output.
+ mAgent->clusterUpdate();
+ }
+ // Restore alternate exchange settings on exchanges.
+ broker.getExchanges().eachExchange(
+ boost::bind(&broker::Exchange::recoveryComplete, _1,
+ boost::ref(broker.getExchanges())));
+ enableClusterSafe(); // Enable cluster-safe assertions
+ deliverEventQueue.start();
+ }
+ else if (updateRetracted) { // Update was retracted, request another update
+ updateRetracted = false;
+ updateClosed = false;
+ state = JOINER;
+ QPID_LOG(notice, *this << " update retracted, sending new update request.");
+ mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self);
+ deliverEventQueue.start();
+ }
+}
+
+void Cluster::updateOutDone() {
+ Monitor::ScopedLock l(lock);
+ updateOutDone(l);
+}
+
+void Cluster::updateOutDone(Lock& l) {
+ QPID_LOG(notice, *this << " update sent");
+ assert(state == UPDATER);
+ state = READY;
+ deliverEventQueue.start(); // Start processing events again.
+ makeOffer(map.firstJoiner(), l); // Try another offer
+}
+
+void Cluster::updateOutError(const std::exception& e) {
+ Monitor::ScopedLock l(lock);
+ QPID_LOG(error, *this << " error sending update: " << e.what());
+ updateOutDone(l);
+}
+
+void Cluster ::shutdown(const MemberId& , const Uuid& id, Lock& l) {
+ QPID_LOG(notice, *this << " cluster shut down by administrator.");
+ if (store.hasStore()) store.clean(id);
+ leave(l);
+}
+
+ManagementObject* Cluster::GetManagementObject() const { return mgmtObject; }
+
+Manageable::status_t Cluster::ManagementMethod (uint32_t methodId, Args& args, string&) {
+ Lock l(lock);
+ QPID_LOG(debug, *this << " managementMethod [id=" << methodId << "]");
+ switch (methodId) {
+ case _qmf::Cluster::METHOD_STOPCLUSTERNODE :
+ {
+ _qmf::ArgsClusterStopClusterNode& iargs = (_qmf::ArgsClusterStopClusterNode&) args;
+ stringstream stream;
+ stream << self;
+ if (iargs.i_brokerId == stream.str())
+ stopClusterNode(l);
+ }
+ break;
+ case _qmf::Cluster::METHOD_STOPFULLCLUSTER :
+ stopFullCluster(l);
+ break;
+ default:
+ return Manageable::STATUS_UNKNOWN_METHOD;
+ }
+ return Manageable::STATUS_OK;
+}
+
+void Cluster::stopClusterNode(Lock& l) {
+ QPID_LOG(notice, *this << " cluster member stopped by administrator.");
+ leave(l);
+}
+
+void Cluster::stopFullCluster(Lock& ) {
+ QPID_LOG(notice, *this << " shutting down cluster " << name);
+ mcast.mcastControl(ClusterShutdownBody(ProtocolVersion(), Uuid(true)), self);
+}
+
+void Cluster::memberUpdate(Lock& l) {
+ // Ignore config changes while we are joining.
+ if (state < CATCHUP) return;
+ QPID_LOG(info, *this << " member update: " << map);
+ size_t aliveCount = map.aliveCount();
+ assert(map.isAlive(self));
+ failoverExchange->updateUrls(getUrls(l));
+
+ // Mark store clean if I am the only broker, dirty otherwise.
+ if (store.hasStore()) {
+ if (aliveCount == 1) {
+ if (store.getState() != STORE_STATE_CLEAN_STORE) {
+ QPID_LOG(notice, *this << "Sole member of cluster, marking store clean.");
+ store.clean(Uuid(true));
+ }
+ }
+ else {
+ if (store.getState() != STORE_STATE_DIRTY_STORE) {
+ QPID_LOG(notice, "Running in a cluster, marking store dirty.");
+ store.dirty();
+ }
+ }
+ }
+
+ // If I am the last member standing, set queue policies.
+ if (aliveCount == 1 && lastAliveCount > 1 && state >= CATCHUP) {
+ QPID_LOG(notice, *this << " last broker standing, update queue policies");
+ lastBroker = true;
+ broker.getQueues().updateQueueClusterState(true);
+ }
+ else if (aliveCount > 1 && lastBroker) {
+ QPID_LOG(notice, *this << " last broker standing joined by " << aliveCount-1
+ << " replicas, updating queue policies.");
+ lastBroker = false;
+ broker.getQueues().updateQueueClusterState(false);
+ }
+ lastAliveCount = aliveCount;
+
+ // Close connections belonging to members that have left the cluster.
+ ConnectionMap::iterator i = connections.begin();
+ while (i != connections.end()) {
+ ConnectionMap::iterator j = i++;
+ MemberId m = j->second->getId().getMember();
+ if (m != self && !map.isMember(m)) {
+ j->second->close();
+ erase(j->second->getId(), l);
+ }
+ }
+}
+
+// See comment on Cluster::setMgmtStatus
+void Cluster::updateMgmtMembership(Lock& l) {
+ if (!mgmtObject) return;
+ std::vector<Url> urls = getUrls(l);
+ mgmtObject->set_clusterSize(urls.size());
+ string urlstr;
+ for(std::vector<Url>::iterator i = urls.begin(); i != urls.end(); i++ ) {
+ if (i != urls.begin()) urlstr += ";";
+ urlstr += i->str();
+ }
+ std::vector<string> ids = getIds(l);
+ string idstr;
+ for(std::vector<string>::iterator i = ids.begin(); i != ids.end(); i++ ) {
+ if (i != ids.begin()) idstr += ";";
+ idstr += *i;
+ }
+ mgmtObject->set_members(urlstr);
+ mgmtObject->set_memberIDs(idstr);
+}
+
+std::ostream& operator<<(std::ostream& o, const Cluster& cluster) {
+ static const char* STATE[] = {
+ "PRE_INIT", "INIT", "JOINER", "UPDATEE", "CATCHUP",
+ "READY", "OFFER", "UPDATER", "LEFT"
+ };
+ assert(sizeof(STATE)/sizeof(*STATE) == Cluster::LEFT+1);
+ o << "cluster(" << cluster.self << " " << STATE[cluster.state];
+ if (cluster.error.isUnresolved()) o << "/error";
+ return o << ")";
+}
+
+MemberId Cluster::getId() const {
+ return self; // Immutable, no need to lock.
+}
+
+broker::Broker& Cluster::getBroker() const {
+ return broker; // Immutable, no need to lock.
+}
+
+void Cluster::setClusterId(const Uuid& uuid, Lock&) {
+ clusterId = uuid;
+ if (store.hasStore()) store.setClusterId(uuid);
+ if (mgmtObject) {
+ stringstream stream;
+ stream << self;
+ mgmtObject->set_clusterID(clusterId.str());
+ mgmtObject->set_memberID(stream.str());
+ }
+ QPID_LOG(notice, *this << " cluster-uuid = " << clusterId);
+}
+
+void Cluster::messageExpired(const MemberId&, uint64_t id, Lock&) {
+ expiryPolicy->deliverExpire(id);
+}
+
+void Cluster::errorCheck(const MemberId& from, uint8_t type, framing::SequenceNumber frameSeq, Lock&) {
+ // If we see an errorCheck here (rather than in the ErrorCheck
+ // class) then we have processed succesfully past the point of the
+ // error.
+ if (state >= CATCHUP) // Don't respond pre catchup, we don't know what happened
+ error.respondNone(from, type, frameSeq);
+}
+
+void Cluster::timerWakeup(const MemberId& , const std::string& name, Lock&) {
+ if (state >= CATCHUP) // Pre catchup our timer isn't set up.
+ timer->deliverWakeup(name);
+}
+
+void Cluster::timerDrop(const MemberId& , const std::string& name, Lock&) {
+ QPID_LOG(debug, "Cluster timer drop " << map.getFrameSeq() << ": " << name)
+ if (state >= CATCHUP) // Pre catchup our timer isn't set up.
+ timer->deliverDrop(name);
+}
+
+bool Cluster::isElder() const {
+ return elder;
+}
+
+void Cluster::deliverToQueue(const std::string& queue, const std::string& message, Lock& l)
+{
+ broker::Queue::shared_ptr q = broker.getQueues().find(queue);
+ if (!q) {
+ QPID_LOG(critical, *this << " cluster delivery to non-existent queue: " << queue);
+ leave(l);
+ }
+ framing::Buffer buf(const_cast<char*>(message.data()), message.size());
+ boost::intrusive_ptr<broker::Message> msg(new broker::Message);
+ msg->decodeHeader(buf);
+ msg->decodeContent(buf);
+ q->deliver(msg);
+}
+
+bool Cluster::deferDeliveryImpl(const std::string& queue,
+ const boost::intrusive_ptr<broker::Message>& msg)
+{
+ if (isClusterSafe()) return false;
+ std::string message;
+ message.resize(msg->encodedSize());
+ framing::Buffer buf(const_cast<char*>(message.data()), message.size());
+ msg->encode(buf);
+ mcast.mcastControl(ClusterDeliverToQueueBody(ProtocolVersion(), queue, message), self);
+ return true;
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.h b/qpid/cpp/src/qpid/cluster/Cluster.h
new file mode 100644
index 0000000000..78d325cdf9
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Cluster.h
@@ -0,0 +1,308 @@
+#ifndef QPID_CLUSTER_CLUSTER_H
+#define QPID_CLUSTER_CLUSTER_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "ClusterMap.h"
+#include "ClusterSettings.h"
+#include "Cpg.h"
+#include "Decoder.h"
+#include "ErrorCheck.h"
+#include "Event.h"
+#include "EventFrame.h"
+#include "ExpiryPolicy.h"
+#include "FailoverExchange.h"
+#include "InitialStatusMap.h"
+#include "LockedConnectionMap.h"
+#include "Multicaster.h"
+#include "NoOpConnectionOutputHandler.h"
+#include "PollableQueue.h"
+#include "PollerDispatch.h"
+#include "Quorum.h"
+#include "StoreStatus.h"
+#include "UpdateReceiver.h"
+
+#include "qmf/org/apache/qpid/cluster/Cluster.h"
+#include "qpid/Url.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/sys/Monitor.h"
+
+#include <boost/bind.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+namespace qpid {
+
+namespace broker {
+class Message;
+}
+
+namespace framing {
+class AMQBody;
+struct Uuid;
+}
+
+namespace cluster {
+
+class Connection;
+struct EventFrame;
+class ClusterTimer;
+class UpdateDataExchange;
+
+/**
+ * Connection to the cluster
+ */
+class Cluster : private Cpg::Handler, public management::Manageable {
+ public:
+ typedef boost::intrusive_ptr<Connection> ConnectionPtr;
+ typedef std::vector<ConnectionPtr> ConnectionVector;
+
+ // Public functions are thread safe unless otherwise mentioned in a comment.
+
+ // Construct the cluster in plugin earlyInitialize.
+ Cluster(const ClusterSettings&, broker::Broker&);
+ virtual ~Cluster();
+
+ // Called by plugin initialize: cluster start-up requires transport plugins .
+ // Thread safety: only called by plugin initialize.
+ void initialize();
+
+ // Connection map.
+ void addLocalConnection(const ConnectionPtr&);
+ void addShadowConnection(const ConnectionPtr&);
+ void erase(const ConnectionId&);
+
+ // URLs of current cluster members.
+ std::vector<std::string> getIds() const;
+ std::vector<Url> getUrls() const;
+ boost::shared_ptr<FailoverExchange> getFailoverExchange() const { return failoverExchange; }
+
+ // Leave the cluster - called when fatal errors occur.
+ void leave();
+
+ // Update completed - called in update thread
+ void updateInClosed();
+ void updateInDone(const ClusterMap&);
+ void updateInRetracted();
+ // True if we are expecting to receive catch-up connections.
+ bool isExpectingUpdate();
+
+ MemberId getId() const;
+ broker::Broker& getBroker() const;
+ Multicaster& getMulticast() { return mcast; }
+
+ const ClusterSettings& getSettings() const { return settings; }
+
+ void deliverFrame(const EventFrame&);
+
+ // Called in deliverFrame thread to indicate an error from the broker.
+ void flagError(Connection&, ErrorCheck::ErrorType, const std::string& msg);
+
+ // Called only during update by Connection::shadowReady
+ Decoder& getDecoder() { return decoder; }
+
+ ExpiryPolicy& getExpiryPolicy() { return *expiryPolicy; }
+
+ UpdateReceiver& getUpdateReceiver() { return updateReceiver; }
+
+ bool isElder() const;
+
+ // Generates a log message for debugging purposes.
+ std::string debugSnapshot();
+
+ // Defer messages delivered in an unsafe context by multicasting.
+ bool deferDeliveryImpl(const std::string& queue,
+ const boost::intrusive_ptr<broker::Message>& msg);
+
+ private:
+ typedef sys::Monitor::ScopedLock Lock;
+
+ typedef PollableQueue<Event> PollableEventQueue;
+ typedef PollableQueue<EventFrame> PollableFrameQueue;
+ typedef std::map<ConnectionId, ConnectionPtr> ConnectionMap;
+
+ /** Version number of the cluster protocol, to avoid mixed versions. */
+ static const uint32_t CLUSTER_VERSION;
+
+ // NB: A dummy Lock& parameter marks functions that must only be
+ // called with Cluster::lock locked.
+
+ void leave(Lock&);
+ std::vector<std::string> getIds(Lock&) const;
+ std::vector<Url> getUrls(Lock&) const;
+
+ // == Called in main thread from Broker destructor.
+ void brokerShutdown();
+
+ // == Called in deliverEventQueue thread
+ void deliveredEvent(const Event&);
+
+ // == Called in deliverFrameQueue thread
+ void deliveredFrame(const EventFrame&);
+ void processFrame(const EventFrame&, Lock&);
+
+ // Cluster controls implement XML methods from cluster.xml.
+ void updateRequest(const MemberId&, const std::string&, Lock&);
+ void updateOffer(const MemberId& updater, uint64_t updatee, Lock&);
+ void retractOffer(const MemberId& updater, uint64_t updatee, Lock&);
+ void initialStatus(const MemberId&,
+ uint32_t version,
+ bool active,
+ const framing::Uuid& clusterId,
+ framing::cluster::StoreState,
+ const framing::Uuid& shutdownId,
+ const std::string& firstConfig,
+ Lock&);
+ void ready(const MemberId&, const std::string&, Lock&);
+ void configChange(const MemberId&,
+ const std::string& members,
+ const std::string& left,
+ const std::string& joined,
+ Lock& l);
+ void messageExpired(const MemberId&, uint64_t, Lock& l);
+ void errorCheck(const MemberId&, uint8_t type, SequenceNumber frameSeq, Lock&);
+ void timerWakeup(const MemberId&, const std::string& name, Lock&);
+ void timerDrop(const MemberId&, const std::string& name, Lock&);
+ void shutdown(const MemberId&, const framing::Uuid& shutdownId, Lock&);
+ void deliverToQueue(const std::string& queue, const std::string& message, Lock&);
+
+ // Helper functions
+ ConnectionPtr getConnection(const EventFrame&, Lock&);
+ ConnectionVector getConnections(Lock&);
+ void updateStart(const MemberId& updatee, const Url& url, Lock&);
+ void makeOffer(const MemberId&, Lock&);
+ void setReady(Lock&);
+ void memberUpdate(Lock&);
+ void setClusterId(const framing::Uuid&, Lock&);
+ void erase(const ConnectionId&, Lock&);
+ void requestUpdate(Lock& );
+ void initMapCompleted(Lock&);
+ void becomeElder(Lock&);
+ void setMgmtStatus(Lock&);
+ void updateMgmtMembership(Lock&);
+
+ // == Called in CPG dispatch thread
+ void deliver( // CPG deliver callback.
+ cpg_handle_t /*handle*/,
+ const struct cpg_name *group,
+ uint32_t /*nodeid*/,
+ uint32_t /*pid*/,
+ void* /*msg*/,
+ int /*msg_len*/);
+
+ void deliverEvent(const Event&);
+
+ void configChange( // CPG config change callback.
+ cpg_handle_t /*handle*/,
+ const struct cpg_name */*group*/,
+ const struct cpg_address */*members*/, int /*nMembers*/,
+ const struct cpg_address */*left*/, int /*nLeft*/,
+ const struct cpg_address */*joined*/, int /*nJoined*/
+ );
+
+ // == Called in management threads.
+ virtual qpid::management::ManagementObject* GetManagementObject() const;
+ virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+
+ void stopClusterNode(Lock&);
+ void stopFullCluster(Lock&);
+
+ // == Called in connection IO threads .
+ void checkUpdateIn(Lock&);
+
+ // == Called in UpdateClient thread.
+ void updateOutDone();
+ void updateOutError(const std::exception&);
+ void updateOutDone(Lock&);
+
+ // Immutable members set on construction, never changed.
+ const ClusterSettings settings;
+ broker::Broker& broker;
+ qmf::org::apache::qpid::cluster::Cluster* mgmtObject; // mgnt owns lifecycle
+ boost::shared_ptr<sys::Poller> poller;
+ Cpg cpg;
+ const std::string name;
+ Url myUrl;
+ const MemberId self;
+ framing::Uuid clusterId;
+ NoOpConnectionOutputHandler shadowOut;
+ qpid::management::ManagementAgent* mAgent;
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+
+ // Thread safe members
+ Multicaster mcast;
+ PollerDispatch dispatcher;
+ PollableEventQueue deliverEventQueue;
+ PollableFrameQueue deliverFrameQueue;
+ boost::shared_ptr<FailoverExchange> failoverExchange;
+ boost::shared_ptr<UpdateDataExchange> updateDataExchange;
+ Quorum quorum;
+ LockedConnectionMap localConnections;
+
+ // Used only in deliverEventQueue thread or when stalled for update.
+ Decoder decoder;
+ bool discarding;
+
+
+ // Remaining members are protected by lock.
+ mutable sys::Monitor lock;
+
+
+ // Local cluster state, cluster map
+ enum {
+ PRE_INIT,///< Have not yet received complete initial status map.
+ INIT, ///< Waiting to reach cluster-size.
+ JOINER, ///< Sent update request, waiting for update offer.
+ UPDATEE, ///< Stalled receive queue at update offer, waiting for update to complete.
+ CATCHUP, ///< Update complete, unstalled but has not yet seen own "ready" event.
+ READY, ///< Fully operational
+ OFFER, ///< Sent an offer, waiting for accept/reject.
+ UPDATER, ///< Offer accepted, sending a state update.
+ LEFT ///< Final state, left the cluster.
+ } state;
+
+ ConnectionMap connections;
+ InitialStatusMap initMap;
+ StoreStatus store;
+ ClusterMap map;
+ MemberSet elders;
+ bool elder;
+ size_t lastAliveCount;
+ bool lastBroker;
+ sys::Thread updateThread;
+ boost::optional<ClusterMap> updatedMap;
+ bool updateRetracted, updateClosed;
+ ErrorCheck error;
+ UpdateReceiver updateReceiver;
+ ClusterTimer* timer;
+
+ friend std::ostream& operator<<(std::ostream&, const Cluster&);
+ friend struct ClusterDispatcher;
+};
+
+}} // namespace qpid::cluster
+
+
+
+#endif /*!QPID_CLUSTER_CLUSTER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ClusterMap.cpp b/qpid/cpp/src/qpid/cluster/ClusterMap.cpp
new file mode 100644
index 0000000000..a8389095c9
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterMap.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/ClusterMap.h"
+#include "qpid/Url.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <ostream>
+
+using namespace std;
+using namespace boost;
+
+namespace qpid {
+using namespace framing;
+
+namespace cluster {
+
+namespace {
+
+void addFieldTableValue(FieldTable::ValueMap::value_type vt, ClusterMap::Map& map, ClusterMap::Set& set) {
+ MemberId id(vt.first);
+ set.insert(id);
+ string url = vt.second->get<string>();
+ if (!url.empty())
+ map.insert(ClusterMap::Map::value_type(id, Url(url)));
+}
+
+void insertFieldTableFromMapValue(FieldTable& ft, const ClusterMap::Map::value_type& vt) {
+ ft.setString(vt.first.str(), vt.second.str());
+}
+
+}
+
+ClusterMap::ClusterMap() : frameSeq(0) {}
+
+ClusterMap::ClusterMap(const Map& map) : frameSeq(0) {
+ transform(map.begin(), map.end(), inserter(alive, alive.begin()), bind(&Map::value_type::first, _1));
+ members = map;
+}
+
+ClusterMap::ClusterMap(const FieldTable& joinersFt, const FieldTable& membersFt,
+ framing::SequenceNumber frameSeq_)
+ : frameSeq(frameSeq_)
+{
+ for_each(joinersFt.begin(), joinersFt.end(), bind(&addFieldTableValue, _1, ref(joiners), ref(alive)));
+ for_each(membersFt.begin(), membersFt.end(), bind(&addFieldTableValue, _1, ref(members), ref(alive)));
+}
+
+void ClusterMap::toMethodBody(framing::ClusterConnectionMembershipBody& b) const {
+ b.getJoiners().clear();
+ for_each(joiners.begin(), joiners.end(), bind(&insertFieldTableFromMapValue, ref(b.getJoiners()), _1));
+ for(Set::const_iterator i = alive.begin(); i != alive.end(); ++i) {
+ if (!isMember(*i) && !isJoiner(*i))
+ b.getJoiners().setString(i->str(), string());
+ }
+ b.getMembers().clear();
+ for_each(members.begin(), members.end(), bind(&insertFieldTableFromMapValue, ref(b.getMembers()), _1));
+ b.setFrameSeq(frameSeq);
+}
+
+Url ClusterMap::getUrl(const Map& map, const MemberId& id) {
+ Map::const_iterator i = map.find(id);
+ return i == map.end() ? Url() : i->second;
+}
+
+MemberId ClusterMap::firstJoiner() const {
+ return joiners.empty() ? MemberId() : joiners.begin()->first;
+}
+
+vector<string> ClusterMap::memberIds() const {
+ vector<string> ids;
+ for (Map::const_iterator iter = members.begin();
+ iter != members.end(); iter++) {
+ stringstream stream;
+ stream << iter->first;
+ ids.push_back(stream.str());
+ }
+ return ids;
+}
+
+vector<Url> ClusterMap::memberUrls() const {
+ vector<Url> urls(members.size());
+ transform(members.begin(), members.end(), urls.begin(),
+ bind(&Map::value_type::second, _1));
+ return urls;
+}
+
+ClusterMap::Set ClusterMap::getAlive() const { return alive; }
+
+ClusterMap::Set ClusterMap::getMembers() const {
+ Set s;
+ transform(members.begin(), members.end(), inserter(s, s.begin()),
+ bind(&Map::value_type::first, _1));
+ return s;
+}
+
+ostream& operator<<(ostream& o, const ClusterMap::Map& m) {
+ ostream_iterator<MemberId> oi(o);
+ transform(m.begin(), m.end(), oi, bind(&ClusterMap::Map::value_type::first, _1));
+ return o;
+}
+
+ostream& operator<<(ostream& o, const ClusterMap& m) {
+ for (ClusterMap::Set::const_iterator i = m.alive.begin(); i != m.alive.end(); ++i) {
+ o << *i;
+ if (m.isMember(*i)) o << "(member)";
+ else if (m.isJoiner(*i)) o << "(joiner)";
+ else o << "(unknown)";
+ o << " ";
+ }
+ o << "frameSeq=" << m.getFrameSeq();
+ return o;
+}
+
+bool ClusterMap::updateRequest(const MemberId& id, const string& url) {
+ try {
+ if (isAlive(id)) {
+ joiners[id] = Url(url);
+ return true;
+ }
+ } catch (const Url::Invalid&) {
+ QPID_LOG(error, "Invalid URL in cluster update request: " << url);
+ }
+ return false;
+}
+
+bool ClusterMap::ready(const MemberId& id, const Url& url) {
+ return isAlive(id) && members.insert(Map::value_type(id,url)).second;
+}
+
+bool ClusterMap::configChange(const Set& update) {
+ bool memberChange = false;
+ Set removed;
+ set_difference(alive.begin(), alive.end(),
+ update.begin(), update.end(),
+ inserter(removed, removed.begin()));
+ alive = update;
+ for (Set::const_iterator i = removed.begin(); i != removed.end(); ++i) {
+ memberChange = memberChange || members.erase(*i);
+ joiners.erase(*i);
+ }
+ return memberChange;
+}
+
+optional<Url> ClusterMap::updateOffer(const MemberId& from, const MemberId& to) {
+ Map::iterator i = joiners.find(to);
+ if (isAlive(from) && i != joiners.end()) {
+ Url url= i->second;
+ joiners.erase(i); // No longer a potential updatee.
+ return url;
+ }
+ return optional<Url>();
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/ClusterMap.h b/qpid/cpp/src/qpid/cluster/ClusterMap.h
new file mode 100644
index 0000000000..cfa4ad924a
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterMap.h
@@ -0,0 +1,106 @@
+#ifndef QPID_CLUSTER_CLUSTERMAP_H
+#define QPID_CLUSTER_CLUSTERMAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "MemberSet.h"
+#include "qpid/Url.h"
+#include "qpid/framing/ClusterConnectionMembershipBody.h"
+#include "qpid/framing/SequenceNumber.h"
+
+#include <boost/function.hpp>
+#include <boost/optional.hpp>
+
+#include <vector>
+#include <deque>
+#include <map>
+#include <iosfwd>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Map of established cluster members and joiners waiting for an update,
+ * along with other cluster state that must be updated.
+ */
+class ClusterMap {
+ public:
+ typedef std::map<MemberId, Url> Map;
+ typedef std::set<MemberId> Set;
+
+ ClusterMap();
+ ClusterMap(const Map& map);
+ ClusterMap(const framing::FieldTable& joiners, const framing::FieldTable& members,
+ framing::SequenceNumber frameSeq);
+
+ /** Update from config change.
+ *@return true if member set changed.
+ */
+ bool configChange(const Set& members);
+
+ bool isJoiner(const MemberId& id) const { return joiners.find(id) != joiners.end(); }
+ bool isMember(const MemberId& id) const { return members.find(id) != members.end(); }
+ bool isAlive(const MemberId& id) const { return alive.find(id) != alive.end(); }
+
+ Url getJoinerUrl(const MemberId& id) { return getUrl(joiners, id); }
+ Url getMemberUrl(const MemberId& id) { return getUrl(members, id); }
+
+ /** First joiner in the cluster in ID order, target for offers */
+ MemberId firstJoiner() const;
+
+ /** Convert map contents to a cluster control body. */
+ void toMethodBody(framing::ClusterConnectionMembershipBody&) const;
+
+ size_t aliveCount() const { return alive.size(); }
+ size_t memberCount() const { return members.size(); }
+ std::vector<std::string> memberIds() const;
+ std::vector<Url> memberUrls() const;
+ Set getAlive() const;
+ Set getMembers() const;
+
+ bool updateRequest(const MemberId& id, const std::string& url);
+ /** Return non-empty Url if accepted */
+ boost::optional<Url> updateOffer(const MemberId& from, const MemberId& to);
+
+ /**@return true If this is a new member */
+ bool ready(const MemberId& id, const Url&);
+
+ framing::SequenceNumber getFrameSeq() const { return frameSeq; }
+ framing::SequenceNumber incrementFrameSeq() { return ++frameSeq; }
+
+ /** Clear out all knowledge of joiners & members, just keep alive set */
+ void clearStatus() { joiners.clear(); members.clear(); }
+
+ private:
+ Url getUrl(const Map& map, const MemberId& id);
+
+ Map joiners, members;
+ Set alive;
+ framing::SequenceNumber frameSeq;
+
+ friend std::ostream& operator<<(std::ostream&, const Map&);
+ friend std::ostream& operator<<(std::ostream&, const ClusterMap&);
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_CLUSTERMAP_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp
new file mode 100644
index 0000000000..2962daaa07
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp
@@ -0,0 +1,123 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/ConnectionCodec.h"
+#include "qpid/cluster/ClusterSettings.h"
+
+#include "qpid/cluster/SecureConnectionFactory.h"
+
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/ConnectionCodec.h"
+#include "qpid/cluster/UpdateClient.h"
+
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/client/ConnectionSettings.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility/in_place_factory.hpp>
+#include <boost/scoped_ptr.hpp>
+
+namespace qpid {
+namespace cluster {
+
+using namespace std;
+using broker::Broker;
+using management::ManagementAgent;
+
+
+/** Note separating options from settings to work around boost version differences.
+ * Old boost takes a reference to options objects, but new boost makes a copy.
+ * New boost allows a shared_ptr but that's not compatible with old boost.
+ */
+struct ClusterOptions : public Options {
+ ClusterSettings& settings;
+
+ ClusterOptions(ClusterSettings& v) : Options("Cluster Options"), settings(v) {
+ addOptions()
+ ("cluster-name", optValue(settings.name, "NAME"), "Name of cluster to join")
+ ("cluster-url", optValue(settings.url,"URL"),
+ "Set URL of this individual broker, to be advertized to clients.\n"
+ "Defaults to a URL listing all the local IP addresses\n")
+ ("cluster-username", optValue(settings.username, ""), "Username for connections between brokers")
+ ("cluster-password", optValue(settings.password, ""), "Password for connections between brokers")
+ ("cluster-mechanism", optValue(settings.mechanism, ""), "Authentication mechanism for connections between brokers")
+#if HAVE_LIBCMAN_H
+ ("cluster-cman", optValue(settings.quorum), "Integrate with Cluster Manager (CMAN) cluster.")
+#endif
+ ("cluster-size", optValue(settings.size, "N"), "Wait for N cluster members before allowing clients to connect.")
+ ("cluster-read-max", optValue(settings.readMax,"N"), "Experimental: flow-control limit reads per connection. 0=no limit.")
+ ;
+ }
+};
+
+typedef boost::shared_ptr<sys::ConnectionCodec::Factory> CodecFactoryPtr;
+
+struct ClusterPlugin : public Plugin {
+
+ ClusterSettings settings;
+ ClusterOptions options;
+ Cluster* cluster;
+ boost::scoped_ptr<ConnectionCodec::Factory> factory;
+
+ ClusterPlugin() : options(settings), cluster(0) {}
+
+ // Cluster needs to be initialized after the store
+ int initOrder() const { return Plugin::DEFAULT_INIT_ORDER+500; }
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(Plugin::Target& target) {
+ if (settings.name.empty()) return; // Only if --cluster-name option was specified.
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ cluster = new Cluster(settings, *broker);
+ CodecFactoryPtr simpleFactory(new broker::ConnectionFactory(*broker));
+ CodecFactoryPtr clusterFactory(new ConnectionCodec::Factory(simpleFactory, *cluster));
+ CodecFactoryPtr secureFactory(new SecureConnectionFactory(clusterFactory));
+ broker->setConnectionFactory(secureFactory);
+ }
+
+ void disallowManagementMethods(ManagementAgent* agent) {
+ if (!agent) return;
+ agent->disallowV1Methods();
+ }
+
+ void initialize(Plugin::Target& target) {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (broker && cluster) {
+ disallowManagementMethods(broker->getManagementAgent());
+ cluster->initialize();
+ }
+ }
+};
+
+static ClusterPlugin instance; // Static initialization.
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/ClusterSettings.h b/qpid/cpp/src/qpid/cluster/ClusterSettings.h
new file mode 100644
index 0000000000..8e708aa139
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterSettings.h
@@ -0,0 +1,50 @@
+#ifndef QPID_CLUSTER_CLUSTERSETTINGS_H
+#define QPID_CLUSTER_CLUSTERSETTINGS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/Url.h>
+#include <string>
+
+namespace qpid {
+namespace cluster {
+
+struct ClusterSettings {
+ std::string name;
+ std::string url;
+ bool quorum;
+ size_t readMax;
+ std::string username, password, mechanism;
+ size_t size;
+
+ ClusterSettings() : quorum(false), readMax(10), size(1)
+ {}
+
+ Url getUrl(uint16_t port) const {
+ if (url.empty()) return Url::getIpAddressesUrl(port);
+ return Url(url);
+ }
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_CLUSTERSETTINGS_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp b/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp
new file mode 100644
index 0000000000..f6e1c7a849
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp
@@ -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.
+ *
+ */
+
+#include "Cluster.h"
+#include "ClusterTimer.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/ClusterTimerWakeupBody.h"
+#include "qpid/framing/ClusterTimerDropBody.h"
+
+namespace qpid {
+namespace cluster {
+
+using boost::intrusive_ptr;
+using std::max;
+using sys::Timer;
+using sys::TimerTask;
+
+//
+// Note on use of Broker::getTimer() rather than getClusterTime in broker code.
+// The following uses of getTimer() are cluster safe:
+//
+// LinkRegistry: maintenance visits in timer can call Bridge::create/cancel
+// but these don't modify any management state.
+//
+// broker::Connection:
+// - Heartbeats use ClusterOrderOutput to ensure consistency
+// - timeout: aborts connection in timer, cluster does an orderly connection close.
+//
+// SessionState: scheduledCredit - uses ClusterOrderProxy
+// Broker::queueCleaner: cluster implements ExpiryPolicy for consistent expiry.
+//
+// Broker::dtxManager: dtx disabled with cluster.
+//
+// requestIOProcessing: called in doOutput.
+//
+
+
+ClusterTimer::ClusterTimer(Cluster& c) : cluster(c) {
+ // Allow more generous overrun threshold with cluster as we
+ // have to do a CPG round trip before executing the task.
+ overran = 10*sys::TIME_MSEC;
+ late = 100*sys::TIME_MSEC;
+}
+
+ClusterTimer::~ClusterTimer() {}
+
+// Initialization or deliver thread.
+void ClusterTimer::add(intrusive_ptr<TimerTask> task)
+{
+ QPID_LOG(trace, "Adding cluster timer task " << task->getName());
+ Map::iterator i = map.find(task->getName());
+ if (i != map.end())
+ throw Exception(QPID_MSG("Task already exists with name " << task->getName()));
+ map[task->getName()] = task;
+ // Only the elder actually activates the task with the Timer base class.
+ if (cluster.isElder()) {
+ QPID_LOG(trace, "Elder activating cluster timer task " << task->getName());
+ Timer::add(task);
+ }
+}
+
+// Timer thread
+void ClusterTimer::fire(intrusive_ptr<TimerTask> t) {
+ // Elder mcasts wakeup on fire, task is not fired until deliverWakeup
+ if (cluster.isElder()) {
+ QPID_LOG(trace, "Sending cluster timer wakeup " << t->getName());
+ cluster.getMulticast().mcastControl(
+ framing::ClusterTimerWakeupBody(framing::ProtocolVersion(), t->getName()),
+ cluster.getId());
+ }
+ else
+ QPID_LOG(trace, "Cluster timer task fired, but not elder " << t->getName());
+}
+
+// Timer thread
+void ClusterTimer::drop(intrusive_ptr<TimerTask> t) {
+ // Elder mcasts drop, task is droped in deliverDrop
+ if (cluster.isElder()) {
+ QPID_LOG(trace, "Sending cluster timer drop " << t->getName());
+ cluster.getMulticast().mcastControl(
+ framing::ClusterTimerDropBody(framing::ProtocolVersion(), t->getName()),
+ cluster.getId());
+ }
+ else
+ QPID_LOG(trace, "Cluster timer task dropped, but not on elder " << t->getName());
+}
+
+// Deliver thread
+void ClusterTimer::deliverWakeup(const std::string& name) {
+ QPID_LOG(trace, "Cluster timer wakeup delivered for " << name);
+ Map::iterator i = map.find(name);
+ if (i == map.end())
+ throw Exception(QPID_MSG("Cluster timer wakeup non-existent task " << name));
+ else {
+ intrusive_ptr<TimerTask> t = i->second;
+ map.erase(i);
+ Timer::fire(t);
+ }
+}
+
+// Deliver thread
+void ClusterTimer::deliverDrop(const std::string& name) {
+ QPID_LOG(trace, "Cluster timer drop delivered for " << name);
+ Map::iterator i = map.find(name);
+ if (i == map.end())
+ throw Exception(QPID_MSG("Cluster timer drop non-existent task " << name));
+ else {
+ intrusive_ptr<TimerTask> t = i->second;
+ map.erase(i);
+ }
+}
+
+// Deliver thread
+void ClusterTimer::becomeElder() {
+ for (Map::iterator i = map.begin(); i != map.end(); ++i) {
+ Timer::add(i->second);
+ }
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/cluster/ClusterTimer.h b/qpid/cpp/src/qpid/cluster/ClusterTimer.h
new file mode 100644
index 0000000000..69f6c622e4
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ClusterTimer.h
@@ -0,0 +1,64 @@
+#ifndef QPID_CLUSTER_CLUSTERTIMER_H
+#define QPID_CLUSTER_CLUSTERTIMER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Timer.h"
+#include <map>
+
+namespace qpid {
+namespace cluster {
+
+class Cluster;
+
+/**
+ * Timer implementation that executes tasks consistently in the
+ * deliver thread across a cluster. Task is not executed when timer
+ * fires, instead the elder multicasts a wakeup. The task is executed
+ * when the wakeup is delivered.
+ */
+class ClusterTimer : public sys::Timer {
+ public:
+ ClusterTimer(Cluster&);
+ ~ClusterTimer();
+
+ void add(boost::intrusive_ptr<sys::TimerTask> task);
+
+ void deliverWakeup(const std::string& name);
+ void deliverDrop(const std::string& name);
+ void becomeElder();
+
+ protected:
+ void fire(boost::intrusive_ptr<sys::TimerTask> task);
+ void drop(boost::intrusive_ptr<sys::TimerTask> task);
+
+ private:
+ typedef std::map<std::string, boost::intrusive_ptr<sys::TimerTask> > Map;
+ Cluster& cluster;
+ Map map;
+};
+
+
+}}
+
+
+#endif /*!QPID_CLUSTER_CLUSTERTIMER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp
new file mode 100644
index 0000000000..b9895290e9
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Connection.cpp
@@ -0,0 +1,728 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/amqp_0_10/Codecs.h"
+#include "Connection.h"
+#include "UpdateClient.h"
+#include "Cluster.h"
+#include "UpdateReceiver.h"
+#include "qpid/assert.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/broker/TxBuffer.h"
+#include "qpid/broker/TxPublish.h"
+#include "qpid/broker/TxAccept.h"
+#include "qpid/broker/RecoveredEnqueue.h"
+#include "qpid/broker/RecoveredDequeue.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Fairshare.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AllInvoker.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/ClusterConnectionDeliverCloseBody.h"
+#include "qpid/framing/ClusterConnectionAnnounceBody.h"
+#include "qpid/framing/ConnectionCloseBody.h"
+#include "qpid/framing/ConnectionCloseOkBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/ClusterSafe.h"
+#include "qpid/types/Variant.h"
+#include "qpid/management/ManagementAgent.h"
+#include <boost/current_function.hpp>
+
+
+namespace qpid {
+namespace cluster {
+
+using namespace framing;
+using namespace framing::cluster;
+using amqp_0_10::ListCodec;
+using types::Variant;
+
+qpid::sys::AtomicValue<uint64_t> Connection::catchUpId(0x5000000000000000LL);
+
+Connection::NullFrameHandler Connection::nullFrameHandler;
+
+struct NullFrameHandler : public framing::FrameHandler {
+ void handle(framing::AMQFrame&) {}
+};
+
+
+namespace {
+sys::AtomicValue<uint64_t> idCounter;
+const std::string shadowPrefix("[shadow]");
+}
+
+
+// Shadow connection
+Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
+ const std::string& mgmtId,
+ const ConnectionId& id, const qpid::sys::SecuritySettings& external)
+ : cluster(c), self(id), catchUp(false), announced(false), output(*this, out),
+ connectionCtor(&output, cluster.getBroker(), mgmtId, external, false, 0, true),
+ expectProtocolHeader(false),
+ mcastFrameHandler(cluster.getMulticast(), self),
+ updateIn(c.getUpdateReceiver()),
+ secureConnection(0)
+{}
+
+// Local connection
+Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
+ const std::string& mgmtId, MemberId member,
+ bool isCatchUp, bool isLink, const qpid::sys::SecuritySettings& external
+) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), announced(false), output(*this, out),
+ connectionCtor(&output, cluster.getBroker(),
+ mgmtId,
+ external,
+ isLink,
+ isCatchUp ? ++catchUpId : 0,
+ isCatchUp), // isCatchUp => shadow
+ expectProtocolHeader(isLink),
+ mcastFrameHandler(cluster.getMulticast(), self),
+ updateIn(c.getUpdateReceiver()),
+ secureConnection(0)
+{
+ if (isLocalClient()) {
+ giveReadCredit(cluster.getSettings().readMax); // Flow control
+ // Delay adding the connection to the management map until announce()
+ connectionCtor.delayManagement = true;
+ }
+ else {
+ // Catch-up shadow connections initialized using nextShadow id.
+ assert(catchUp);
+ if (!updateIn.nextShadowMgmtId.empty())
+ connectionCtor.mgmtId = updateIn.nextShadowMgmtId;
+ updateIn.nextShadowMgmtId.clear();
+ }
+ init();
+ QPID_LOG(debug, cluster << " local connection " << *this);
+}
+
+void Connection::setSecureConnection(broker::SecureConnection* sc) {
+ secureConnection = sc;
+ if (connection.get()) connection->setSecureConnection(sc);
+}
+
+void Connection::init() {
+ connection = connectionCtor.construct();
+ if (isLocalClient()) {
+ if (secureConnection) connection->setSecureConnection(secureConnection);
+ // Actively send cluster-order frames from local node
+ connection->setClusterOrderOutput(mcastFrameHandler);
+ }
+ else { // Shadow or catch-up connection
+ // Passive, discard cluster-order frames
+ connection->setClusterOrderOutput(nullFrameHandler);
+ // Disable client throttling, done by active node.
+ connection->setClientThrottling(false);
+ }
+ if (!isCatchUp())
+ connection->setErrorListener(this);
+}
+
+// Called when we have consumed a read buffer to give credit to the
+// connection layer to continue reading.
+void Connection::giveReadCredit(int credit) {
+ if (cluster.getSettings().readMax && credit)
+ output.giveReadCredit(credit);
+}
+
+void Connection::announce(
+ const std::string& mgmtId, uint32_t ssf, const std::string& authid, bool nodict,
+ const std::string& username, const std::string& initialFrames)
+{
+ QPID_ASSERT(mgmtId == connectionCtor.mgmtId);
+ QPID_ASSERT(ssf == connectionCtor.external.ssf);
+ QPID_ASSERT(authid == connectionCtor.external.authid);
+ QPID_ASSERT(nodict == connectionCtor.external.nodict);
+ // Local connections are already initialized but with management delayed.
+ if (isLocalClient()) {
+ connection->addManagementObject();
+ }
+ else if (isShadow()) {
+ init();
+ // Play initial frames into the connection.
+ Buffer buf(const_cast<char*>(initialFrames.data()), initialFrames.size());
+ AMQFrame frame;
+ while (frame.decode(buf))
+ connection->received(frame);
+ connection->setUserId(username);
+ }
+ // Do managment actions now that the connection is replicated.
+ connection->raiseConnectEvent();
+ QPID_LOG(debug, cluster << " replicated connection " << *this);
+}
+
+Connection::~Connection() {
+ if (connection.get()) connection->setErrorListener(0);
+ // Don't trigger cluster-safe asserts in broker:: ~Connection as
+ // it may be called in an IO thread context during broker
+ // shutdown.
+ sys::ClusterSafeScope css;
+ connection.reset();
+}
+
+bool Connection::doOutput() {
+ return output.doOutput();
+}
+
+// Received from a directly connected client.
+void Connection::received(framing::AMQFrame& f) {
+ if (!connection.get()) {
+ QPID_LOG(warning, cluster << " ignoring frame on closed connection "
+ << *this << ": " << f);
+ return;
+ }
+ QPID_LOG(trace, cluster << " RECV " << *this << ": " << f);
+ if (isLocal()) { // Local catch-up connection.
+ currentChannel = f.getChannel();
+ if (!framing::invoke(*this, *f.getBody()).wasHandled())
+ connection->received(f);
+ }
+ else { // Shadow or updated catch-up connection.
+ if (f.getMethod() && f.getMethod()->isA<ConnectionCloseBody>()) {
+ if (isShadow())
+ cluster.addShadowConnection(this);
+ AMQFrame ok((ConnectionCloseOkBody()));
+ connection->getOutput().send(ok);
+ output.closeOutput();
+ catchUp = false;
+ }
+ else
+ QPID_LOG(warning, cluster << " ignoring unexpected frame " << *this << ": " << f);
+ }
+}
+
+bool Connection::checkUnsupported(const AMQBody& body) {
+ std::string message;
+ if (body.getMethod()) {
+ switch (body.getMethod()->amqpClassId()) {
+ case DTX_CLASS_ID: message = "DTX transactions are not currently supported by cluster."; break;
+ }
+ }
+ if (!message.empty())
+ connection->close(connection::CLOSE_CODE_FRAMING_ERROR, message);
+ return !message.empty();
+}
+
+struct GiveReadCreditOnExit {
+ Connection& connection;
+ int credit;
+ GiveReadCreditOnExit(Connection& connection_, int credit_) :
+ connection(connection_), credit(credit_) {}
+ ~GiveReadCreditOnExit() { if (credit) connection.giveReadCredit(credit); }
+};
+
+void Connection::deliverDoOutput(uint32_t limit) {
+ output.deliverDoOutput(limit);
+}
+
+// Called in delivery thread, in cluster order.
+void Connection::deliveredFrame(const EventFrame& f) {
+ GiveReadCreditOnExit gc(*this, f.readCredit);
+ assert(!catchUp);
+ currentChannel = f.frame.getChannel();
+ if (f.frame.getBody() // frame can be emtpy with just readCredit
+ && !framing::invoke(*this, *f.frame.getBody()).wasHandled() // Connection contol.
+ && !checkUnsupported(*f.frame.getBody())) // Unsupported operation.
+ {
+ if (f.type == DATA) // incoming data frames to broker::Connection
+ connection->received(const_cast<AMQFrame&>(f.frame));
+ else { // frame control, send frame via SessionState
+ broker::SessionState* ss = connection->getChannel(currentChannel).getSession();
+ if (ss) ss->out(const_cast<AMQFrame&>(f.frame));
+ }
+ }
+}
+
+// A local connection is closed by the network layer. Called in the connection thread.
+void Connection::closed() {
+ try {
+ if (isUpdated()) {
+ QPID_LOG(debug, cluster << " update connection closed " << *this);
+ close();
+ cluster.updateInClosed();
+ }
+ else if (catchUp && cluster.isExpectingUpdate()) {
+ QPID_LOG(critical, cluster << " catch-up connection closed prematurely " << *this);
+ cluster.leave();
+ }
+ else if (isLocal()) {
+ // This was a local replicated connection. Multicast a deliver
+ // closed and process any outstanding frames from the cluster
+ // until self-delivery of deliver-close.
+ output.closeOutput();
+ if (announced)
+ cluster.getMulticast().mcastControl(
+ ClusterConnectionDeliverCloseBody(), self);
+ }
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(error, cluster << " error closing connection " << *this << ": " << e.what());
+ }
+}
+
+// Self-delivery of close message, close the connection.
+void Connection::deliverClose () {
+ close();
+ cluster.erase(self);
+}
+
+// Close the connection
+void Connection::close() {
+ if (connection.get()) {
+ QPID_LOG(debug, cluster << " closed connection " << *this);
+ connection->closed();
+ connection.reset();
+ }
+}
+
+// The connection has sent invalid data and should be aborted.
+// All members will get the same abort since they all process the same data.
+void Connection::abort() {
+ connection->abort();
+ // Aborting the connection will result in a call to ::closed()
+ // and allow the connection to close in an orderly manner.
+}
+
+// ConnectionCodec::decode receives read buffers from directly-connected clients.
+size_t Connection::decode(const char* data, size_t size) {
+ GiveReadCreditOnExit grc(*this, 1); // Give a read credit by default.
+ const char* ptr = data;
+ const char* end = data + size;
+ if (catchUp) { // Handle catch-up locally.
+ if (!cluster.isExpectingUpdate()) {
+ QPID_LOG(error, "Rejecting unexpected catch-up connection.");
+ abort(); // Cluster is not expecting catch-up connections.
+ }
+ bool wasOpen = connection->isOpen();
+ Buffer buf(const_cast<char*>(ptr), size);
+ ptr += size;
+ while (localDecoder.decode(buf))
+ received(localDecoder.getFrame());
+ if (!wasOpen && connection->isOpen()) {
+ // Connections marked as federation links are allowed to proxy
+ // messages with user-ID that doesn't match the connection's
+ // authenticated ID. This is important for updates.
+ connection->setFederationLink(isCatchUp());
+ }
+ }
+ else { // Multicast local connections.
+ assert(isLocalClient());
+ assert(connection.get());
+ if (!checkProtocolHeader(ptr, size)) // Updates ptr
+ return 0; // Incomplete header
+
+ if (!connection->isOpen())
+ processInitialFrames(ptr, end-ptr); // Updates ptr
+
+ if (connection->isOpen() && end - ptr > 0) {
+ // We're multi-casting, we will give read credit on delivery.
+ grc.credit = 0;
+ cluster.getMulticast().mcastBuffer(ptr, end - ptr, self);
+ ptr = end;
+ }
+ }
+ return ptr - data;
+}
+
+// Decode the protocol header if needed. Updates data and size
+// returns true if the header is complete or already read.
+bool Connection::checkProtocolHeader(const char*& data, size_t size) {
+ if (expectProtocolHeader) {
+ // This is an outgoing link connection, we will receive a protocol
+ // header which needs to be decoded first
+ framing::ProtocolInitiation pi;
+ Buffer buf(const_cast<char*&>(data), size);
+ if (pi.decode(buf)) {
+ //TODO: check the version is correct
+ expectProtocolHeader = false;
+ data += pi.encodedSize();
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void Connection::processInitialFrames(const char*& ptr, size_t size) {
+ // Process the initial negotiation locally and store it so
+ // it can be replayed on other brokers in announce()
+ Buffer buf(const_cast<char*>(ptr), size);
+ framing::AMQFrame frame;
+ while (!connection->isOpen() && frame.decode(buf))
+ received(frame);
+ initialFrames.append(ptr, buf.getPosition());
+ ptr += buf.getPosition();
+ if (connection->isOpen()) { // initial negotiation complete
+ cluster.getMulticast().mcastControl(
+ ClusterConnectionAnnounceBody(
+ ProtocolVersion(),
+ connectionCtor.mgmtId,
+ connectionCtor.external.ssf,
+ connectionCtor.external.authid,
+ connectionCtor.external.nodict,
+ connection->getUserId(),
+ initialFrames),
+ getId());
+ announced = true;
+ initialFrames.clear();
+ }
+}
+
+broker::SessionState& Connection::sessionState() {
+ return *connection->getChannel(currentChannel).getSession();
+}
+
+broker::SemanticState& Connection::semanticState() {
+ return sessionState().getSemanticState();
+}
+
+void Connection::shadowPrepare(const std::string& mgmtId) {
+ updateIn.nextShadowMgmtId = mgmtId;
+}
+
+void Connection::shadowSetUser(const std::string& userId) {
+ connection->setUserId(userId);
+}
+
+void Connection::consumerState(const string& name, bool blocked, bool notifyEnabled, const SequenceNumber& position)
+{
+ broker::SemanticState::ConsumerImpl& c = semanticState().find(name);
+ c.position = position;
+ c.setBlocked(blocked);
+ if (notifyEnabled) c.enableNotify(); else c.disableNotify();
+ updateIn.consumerNumbering.add(c.shared_from_this());
+}
+
+
+void Connection::sessionState(
+ const SequenceNumber& replayStart,
+ const SequenceNumber& sendCommandPoint,
+ const SequenceSet& sentIncomplete,
+ const SequenceNumber& expected,
+ const SequenceNumber& received,
+ const SequenceSet& unknownCompleted,
+ const SequenceSet& receivedIncomplete)
+{
+ sessionState().setState(
+ replayStart,
+ sendCommandPoint,
+ sentIncomplete,
+ expected,
+ received,
+ unknownCompleted,
+ receivedIncomplete);
+ QPID_LOG(debug, cluster << " received session state update for " << sessionState().getId());
+ // The output tasks will be added later in the update process.
+ connection->getOutputTasks().removeAll();
+}
+
+void Connection::outputTask(uint16_t channel, const std::string& name) {
+ broker::SessionState* session = connection->getChannel(channel).getSession();
+ if (!session)
+ throw Exception(QPID_MSG(cluster << " channel not attached " << *this
+ << "[" << channel << "] "));
+ OutputTask* task = &session->getSemanticState().find(name);
+ connection->getOutputTasks().addOutputTask(task);
+}
+
+void Connection::shadowReady(
+ uint64_t memberId, uint64_t connectionId, const string& mgmtId,
+ const string& username, const string& fragment, uint32_t sendMax)
+{
+ QPID_ASSERT(mgmtId == getBrokerConnection()->getMgmtId());
+ ConnectionId shadowId = ConnectionId(memberId, connectionId);
+ QPID_LOG(debug, cluster << " catch-up connection " << *this
+ << " becomes shadow " << shadowId);
+ self = shadowId;
+ connection->setUserId(username);
+ // OK to use decoder here because cluster is stalled for update.
+ cluster.getDecoder().get(self).setFragment(fragment.data(), fragment.size());
+ connection->setErrorListener(this);
+ output.setSendMax(sendMax);
+}
+
+void Connection::membership(const FieldTable& joiners, const FieldTable& members,
+ const framing::SequenceNumber& frameSeq)
+{
+ QPID_LOG(debug, cluster << " incoming update complete on connection " << *this);
+ updateIn.consumerNumbering.clear();
+ closeUpdated();
+ cluster.updateInDone(ClusterMap(joiners, members, frameSeq));
+}
+
+void Connection::retractOffer() {
+ QPID_LOG(info, cluster << " incoming update retracted on connection " << *this);
+ closeUpdated();
+ cluster.updateInRetracted();
+}
+
+void Connection::closeUpdated() {
+ self.second = 0; // Mark this as completed update connection.
+ if (connection.get())
+ connection->close(connection::CLOSE_CODE_NORMAL, "OK");
+}
+
+bool Connection::isLocal() const {
+ return self.first == cluster.getId() && self.second;
+}
+
+bool Connection::isShadow() const {
+ return self.first != cluster.getId();
+}
+
+bool Connection::isUpdated() const {
+ return self.first == cluster.getId() && self.second == 0;
+}
+
+
+boost::shared_ptr<broker::Queue> Connection::findQueue(const std::string& qname) {
+ boost::shared_ptr<broker::Queue> queue = cluster.getBroker().getQueues().find(qname);
+ if (!queue) throw Exception(QPID_MSG(cluster << " can't find queue " << qname));
+ return queue;
+}
+
+broker::QueuedMessage Connection::getUpdateMessage() {
+ boost::shared_ptr<broker::Queue> updateq = findQueue(UpdateClient::UPDATE);
+ assert(!updateq->isDurable());
+ broker::QueuedMessage m = updateq->get();
+ if (!m.payload) throw Exception(QPID_MSG(cluster << " empty update queue"));
+ return m;
+}
+
+void Connection::deliveryRecord(const string& qname,
+ const SequenceNumber& position,
+ const string& tag,
+ const SequenceNumber& id,
+ bool acquired,
+ bool accepted,
+ bool cancelled,
+ bool completed,
+ bool ended,
+ bool windowing,
+ bool enqueued,
+ uint32_t credit)
+{
+ broker::QueuedMessage m;
+ broker::Queue::shared_ptr queue = findQueue(qname);
+ if (!ended) { // Has a message
+ if (acquired) { // Message is on the update queue
+ m = getUpdateMessage();
+ m.queue = queue.get();
+ m.position = position;
+ if (enqueued) queue->updateEnqueued(m); //inform queue of the message
+ } else { // Message at original position in original queue
+ m = queue->find(position);
+ }
+ if (!m.payload)
+ throw Exception(QPID_MSG("deliveryRecord no update message"));
+ }
+
+ broker::DeliveryRecord dr(m, queue, tag, acquired, accepted, windowing, credit);
+ dr.setId(id);
+ if (cancelled) dr.cancel(dr.getTag());
+ if (completed) dr.complete();
+ if (ended) dr.setEnded(); // Exsitance of message
+ semanticState().record(dr); // Part of the session's unacked list.
+}
+
+void Connection::queuePosition(const string& qname, const SequenceNumber& position) {
+ findQueue(qname)->setPosition(position);
+}
+
+void Connection::queueFairshareState(const std::string& qname, const uint8_t priority, const uint8_t count)
+{
+ if (!qpid::broker::Fairshare::setState(findQueue(qname)->getMessages(), priority, count)) {
+ QPID_LOG(error, "Failed to set fair share state on queue " << qname << "; this will result in inconsistencies.");
+ }
+}
+
+
+namespace {
+ // find a StatefulQueueObserver that matches a given identifier
+ class ObserverFinder {
+ const std::string id;
+ boost::shared_ptr<broker::QueueObserver> target;
+ ObserverFinder(const ObserverFinder&) {}
+ public:
+ ObserverFinder(const std::string& _id) : id(_id) {}
+ broker::StatefulQueueObserver *getObserver()
+ {
+ if (target)
+ return dynamic_cast<broker::StatefulQueueObserver *>(target.get());
+ return 0;
+ }
+ void operator() (boost::shared_ptr<broker::QueueObserver> o)
+ {
+ if (!target) {
+ broker::StatefulQueueObserver *p = dynamic_cast<broker::StatefulQueueObserver *>(o.get());
+ if (p && p->getId() == id) {
+ target = o;
+ }
+ }
+ }
+ };
+}
+
+
+void Connection::queueObserverState(const std::string& qname, const std::string& observerId, const FieldTable& state)
+{
+ boost::shared_ptr<broker::Queue> queue(findQueue(qname));
+ ObserverFinder finder(observerId); // find this observer
+ queue->eachObserver<ObserverFinder &>(finder);
+ broker::StatefulQueueObserver *so = finder.getObserver();
+ if (so) {
+ so->setState( state );
+ QPID_LOG(debug, "updated queue observer " << observerId << "'s state on queue " << qname << "; ...");
+ return;
+ }
+ QPID_LOG(error, "Failed to find observer " << observerId << " state on queue " << qname << "; this will result in inconsistencies.");
+}
+
+void Connection::expiryId(uint64_t id) {
+ cluster.getExpiryPolicy().setId(id);
+}
+
+std::ostream& operator<<(std::ostream& o, const Connection& c) {
+ const char* type="unknown";
+ if (c.isLocal()) type = "local";
+ else if (c.isShadow()) type = "shadow";
+ else if (c.isUpdated()) type = "updated";
+ const broker::Connection* bc = c.getBrokerConnection();
+ if (bc) o << bc->getMgmtId();
+ else o << "<disconnected>";
+ return o << "(" << c.getId() << " " << type << (c.isCatchUp() ? ",catchup":"") << ")";
+}
+
+void Connection::txStart() {
+ txBuffer.reset(new broker::TxBuffer());
+}
+void Connection::txAccept(const framing::SequenceSet& acked) {
+ txBuffer->enlist(boost::shared_ptr<broker::TxAccept>(
+ new broker::TxAccept(acked, semanticState().getUnacked())));
+}
+
+void Connection::txDequeue(const std::string& queue) {
+ txBuffer->enlist(boost::shared_ptr<broker::RecoveredDequeue>(
+ new broker::RecoveredDequeue(findQueue(queue), getUpdateMessage().payload)));
+}
+
+void Connection::txEnqueue(const std::string& queue) {
+ txBuffer->enlist(boost::shared_ptr<broker::RecoveredEnqueue>(
+ new broker::RecoveredEnqueue(findQueue(queue), getUpdateMessage().payload)));
+}
+
+void Connection::txPublish(const framing::Array& queues, bool delivered) {
+ boost::shared_ptr<broker::TxPublish> txPub(new broker::TxPublish(getUpdateMessage().payload));
+ for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i)
+ txPub->deliverTo(findQueue((*i)->get<std::string>()));
+ txPub->delivered = delivered;
+ txBuffer->enlist(txPub);
+}
+
+void Connection::txEnd() {
+ semanticState().setTxBuffer(txBuffer);
+}
+
+void Connection::accumulatedAck(const qpid::framing::SequenceSet& s) {
+ semanticState().setAccumulatedAck(s);
+}
+
+void Connection::exchange(const std::string& encoded) {
+ Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
+ broker::Exchange::shared_ptr ex = broker::Exchange::decode(cluster.getBroker().getExchanges(), buf);
+ if(ex.get() && ex->isDurable() && !ex->getName().find("amq.") == 0 && !ex->getName().find("qpid.") == 0) {
+ cluster.getBroker().getStore().create(*(ex.get()), ex->getArgs());
+ }
+ QPID_LOG(debug, cluster << " updated exchange " << ex->getName());
+}
+
+void Connection::sessionError(uint16_t , const std::string& msg) {
+ // Ignore errors before isOpen(), we're not multicasting yet.
+ if (connection->isOpen())
+ cluster.flagError(*this, ERROR_TYPE_SESSION, msg);
+}
+
+void Connection::connectionError(const std::string& msg) {
+ // Ignore errors before isOpen(), we're not multicasting yet.
+ if (connection->isOpen())
+ cluster.flagError(*this, ERROR_TYPE_CONNECTION, msg);
+}
+
+void Connection::addQueueListener(const std::string& q, uint32_t listener) {
+ if (listener >= updateIn.consumerNumbering.size())
+ throw Exception(QPID_MSG("Invalid listener ID: " << listener));
+ findQueue(q)->getListeners().addListener(updateIn.consumerNumbering[listener]);
+}
+
+//
+// This is the handler for incoming managementsetup messages.
+//
+void Connection::managementSetupState(
+ uint64_t objectNum, uint16_t bootSequence, const framing::Uuid& id,
+ const std::string& vendor, const std::string& product, const std::string& instance)
+{
+ QPID_LOG(debug, cluster << " updated management: object number="
+ << objectNum << " boot sequence=" << bootSequence
+ << " broker-id=" << id
+ << " vendor=" << vendor
+ << " product=" << product
+ << " instance=" << instance);
+ management::ManagementAgent* agent = cluster.getBroker().getManagementAgent();
+ if (!agent)
+ throw Exception(QPID_MSG("Management schema update but management not enabled."));
+ agent->setNextObjectId(objectNum);
+ agent->setBootSequence(bootSequence);
+ agent->setUuid(id);
+ agent->setName(vendor, product, instance);
+}
+
+void Connection::config(const std::string& encoded) {
+ Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
+ string kind;
+ buf.getShortString (kind);
+ if (kind == "link") {
+ broker::Link::shared_ptr link =
+ broker::Link::decode(cluster.getBroker().getLinks(), buf);
+ QPID_LOG(debug, cluster << " updated link "
+ << link->getHost() << ":" << link->getPort());
+ }
+ else if (kind == "bridge") {
+ broker::Bridge::shared_ptr bridge =
+ broker::Bridge::decode(cluster.getBroker().getLinks(), buf);
+ QPID_LOG(debug, cluster << " updated bridge " << bridge->getName());
+ }
+ else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind));
+}
+
+void Connection::doCatchupIoCallbacks() {
+ // We need to process IO callbacks during the catch-up phase in
+ // order to service asynchronous completions for messages
+ // transferred during catch-up.
+
+ if (catchUp) getBrokerConnection()->doIoCallbacks();
+}
+}} // Namespace qpid::cluster
+
diff --git a/qpid/cpp/src/qpid/cluster/Connection.h b/qpid/cpp/src/qpid/cluster/Connection.h
new file mode 100644
index 0000000000..a0da9efbb8
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Connection.h
@@ -0,0 +1,276 @@
+#ifndef QPID_CLUSTER_CONNECTION_H
+#define QPID_CLUSTER_CONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "types.h"
+#include "OutputInterceptor.h"
+#include "McastFrameHandler.h"
+#include "UpdateReceiver.h"
+
+#include "qpid/RefCounted.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/SecureConnection.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/ConnectionInputHandler.h"
+#include "qpid/sys/ConnectionOutputHandler.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/FrameDecoder.h"
+
+#include <iosfwd>
+
+namespace qpid {
+
+namespace framing { class AMQFrame; }
+
+namespace broker {
+class SemanticState;
+struct QueuedMessage;
+class TxBuffer;
+class TxAccept;
+}
+
+namespace cluster {
+class Cluster;
+class Event;
+struct EventFrame;
+
+/** Intercept broker::Connection calls for shadow and local cluster connections. */
+class Connection :
+ public RefCounted,
+ public sys::ConnectionInputHandler,
+ public framing::AMQP_AllOperations::ClusterConnectionHandler,
+ private broker::Connection::ErrorListener
+
+{
+ public:
+
+ /** Local connection. */
+ Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, MemberId, bool catchUp, bool isLink,
+ const qpid::sys::SecuritySettings& external);
+ /** Shadow connection. */
+ Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, const ConnectionId& id,
+ const qpid::sys::SecuritySettings& external);
+ ~Connection();
+
+ ConnectionId getId() const { return self; }
+ broker::Connection* getBrokerConnection() { return connection.get(); }
+ const broker::Connection* getBrokerConnection() const { return connection.get(); }
+
+ /** Local connections may be clients or catch-up connections */
+ bool isLocal() const;
+
+ bool isLocalClient() const { return isLocal() && !isCatchUp(); }
+
+ /** True for connections that are shadowing remote broker connections */
+ bool isShadow() const;
+
+ /** True if the connection is in "catch-up" mode: building initial broker state. */
+ bool isCatchUp() const { return catchUp; }
+
+ /** True if the connection is a completed shared update connection */
+ bool isUpdated() const;
+
+ Cluster& getCluster() { return cluster; }
+
+ // ConnectionInputHandler methods
+ void received(framing::AMQFrame&);
+ void closed();
+ bool doOutput();
+ void idleOut() { if (connection.get()) connection->idleOut(); }
+ void idleIn() { if (connection.get()) connection->idleIn(); }
+
+ // ConnectionCodec methods - called by IO layer with a read buffer.
+ size_t decode(const char* buffer, size_t size);
+
+ // Called for data delivered from the cluster.
+ void deliveredFrame(const EventFrame&);
+
+ void consumerState(const std::string& name, bool blocked, bool notifyEnabled, const qpid::framing::SequenceNumber& position);
+
+ // ==== Used in catch-up mode to build initial state.
+ //
+ // State update methods.
+ void shadowPrepare(const std::string&);
+
+ void shadowSetUser(const std::string&);
+
+ void sessionState(const framing::SequenceNumber& replayStart,
+ const framing::SequenceNumber& sendCommandPoint,
+ const framing::SequenceSet& sentIncomplete,
+ const framing::SequenceNumber& expected,
+ const framing::SequenceNumber& received,
+ const framing::SequenceSet& unknownCompleted,
+ const SequenceSet& receivedIncomplete);
+
+ void outputTask(uint16_t channel, const std::string& name);
+
+ void shadowReady(uint64_t memberId,
+ uint64_t connectionId,
+ const std::string& managementId,
+ const std::string& username,
+ const std::string& fragment,
+ uint32_t sendMax);
+
+ void membership(const framing::FieldTable&, const framing::FieldTable&,
+ const framing::SequenceNumber& frameSeq);
+
+ void retractOffer();
+
+ void deliveryRecord(const std::string& queue,
+ const framing::SequenceNumber& position,
+ const std::string& tag,
+ const framing::SequenceNumber& id,
+ bool acquired,
+ bool accepted,
+ bool cancelled,
+ bool completed,
+ bool ended,
+ bool windowing,
+ bool enqueued,
+ uint32_t credit);
+
+ void queuePosition(const std::string&, const framing::SequenceNumber&);
+ void queueFairshareState(const std::string&, const uint8_t priority, const uint8_t count);
+ void queueObserverState(const std::string&, const std::string&, const framing::FieldTable&);
+ void expiryId(uint64_t);
+
+ void txStart();
+ void txAccept(const framing::SequenceSet&);
+ void txDequeue(const std::string&);
+ void txEnqueue(const std::string&);
+ void txPublish(const framing::Array&, bool);
+ void txEnd();
+ void accumulatedAck(const framing::SequenceSet&);
+
+ // Encoded exchange replication.
+ void exchange(const std::string& encoded);
+
+ void giveReadCredit(int credit);
+ void announce(const std::string& mgmtId, uint32_t ssf, const std::string& authid,
+ bool nodict, const std::string& username,
+ const std::string& initFrames);
+ void close();
+ void abort();
+ void deliverClose();
+
+ OutputInterceptor& getOutput() { return output; }
+
+ void addQueueListener(const std::string& queue, uint32_t listener);
+ void managementSetupState(uint64_t objectNum,
+ uint16_t bootSequence,
+ const framing::Uuid&,
+ const std::string& vendor,
+ const std::string& product,
+ const std::string& instance);
+
+ void config(const std::string& encoded);
+
+ void setSecureConnection ( broker::SecureConnection * sc );
+
+ void doCatchupIoCallbacks();
+
+ private:
+ struct NullFrameHandler : public framing::FrameHandler {
+ void handle(framing::AMQFrame&) {}
+ };
+
+ // Arguments to construct a broker::Connection
+ struct ConnectionCtor {
+ sys::ConnectionOutputHandler* out;
+ broker::Broker& broker;
+ std::string mgmtId;
+ qpid::sys::SecuritySettings external;
+ bool isLink;
+ uint64_t objectId;
+ bool shadow;
+ bool delayManagement;
+
+ ConnectionCtor(
+ sys::ConnectionOutputHandler* out_,
+ broker::Broker& broker_,
+ const std::string& mgmtId_,
+ const qpid::sys::SecuritySettings& external_,
+ bool isLink_=false,
+ uint64_t objectId_=0,
+ bool shadow_=false,
+ bool delayManagement_=false
+ ) : out(out_), broker(broker_), mgmtId(mgmtId_), external(external_),
+ isLink(isLink_), objectId(objectId_), shadow(shadow_),
+ delayManagement(delayManagement_)
+ {}
+
+ std::auto_ptr<broker::Connection> construct() {
+ return std::auto_ptr<broker::Connection>(
+ new broker::Connection(
+ out, broker, mgmtId, external, isLink, objectId,
+ shadow, delayManagement)
+ );
+ }
+ };
+
+ static NullFrameHandler nullFrameHandler;
+
+ // Error listener functions
+ void connectionError(const std::string&);
+ void sessionError(uint16_t channel, const std::string&);
+
+ void init();
+ bool checkUnsupported(const framing::AMQBody& body);
+ void deliverDoOutput(uint32_t limit);
+
+ bool checkProtocolHeader(const char*& data, size_t size);
+ void processInitialFrames(const char*& data, size_t size);
+ boost::shared_ptr<broker::Queue> findQueue(const std::string& qname);
+ broker::SessionState& sessionState();
+ broker::SemanticState& semanticState();
+ broker::QueuedMessage getUpdateMessage();
+ void closeUpdated();
+
+ Cluster& cluster;
+ ConnectionId self;
+ bool catchUp;
+ bool announced;
+ OutputInterceptor output;
+ framing::FrameDecoder localDecoder;
+ ConnectionCtor connectionCtor;
+ std::auto_ptr<broker::Connection> connection;
+ framing::SequenceNumber deliverSeq;
+ framing::ChannelId currentChannel;
+ boost::shared_ptr<broker::TxBuffer> txBuffer;
+ bool expectProtocolHeader;
+ McastFrameHandler mcastFrameHandler;
+ UpdateReceiver& updateIn;
+ qpid::broker::SecureConnection* secureConnection;
+ std::string initialFrames;
+
+ static qpid::sys::AtomicValue<uint64_t> catchUpId;
+
+ friend std::ostream& operator<<(std::ostream&, const Connection&);
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_CONNECTION_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp
new file mode 100644
index 0000000000..d0ba8abfb3
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/ConnectionCodec.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/ProxyInputHandler.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/framing/ConnectionCloseBody.h"
+#include "qpid/framing/ConnectionCloseOkBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/memory.h"
+#include <stdexcept>
+#include <boost/utility/in_place_factory.hpp>
+
+namespace qpid {
+namespace cluster {
+
+using namespace framing;
+
+sys::ConnectionCodec*
+ConnectionCodec::Factory::create(ProtocolVersion v, sys::OutputControl& out,
+ const std::string& id,
+ const qpid::sys::SecuritySettings& external)
+{
+ broker::Broker& broker = cluster.getBroker();
+ if (broker.getConnectionCounter().allowConnection())
+ {
+ QPID_LOG(error, "Client max connection count limit exceeded: "
+ << broker.getOptions().maxConnections << " connection refused");
+ return 0;
+ }
+ if (v == ProtocolVersion(0, 10))
+ return new ConnectionCodec(v, out, id, cluster, false, false, external);
+ else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) // Catch-up connection
+ return new ConnectionCodec(v, out, id, cluster, true, false, external);
+ return 0;
+}
+
+// Used for outgoing Link connections
+sys::ConnectionCodec*
+ConnectionCodec::Factory::create(sys::OutputControl& out, const std::string& logId,
+ const qpid::sys::SecuritySettings& external) {
+ return new ConnectionCodec(ProtocolVersion(0,10), out, logId, cluster, false, true, external);
+}
+
+ConnectionCodec::ConnectionCodec(
+ const ProtocolVersion& v, sys::OutputControl& out,
+ const std::string& logId, Cluster& cluster, bool catchUp, bool isLink, const qpid::sys::SecuritySettings& external
+) : codec(out, logId, isLink),
+ interceptor(new Connection(cluster, codec, logId, cluster.getId(), catchUp, isLink, external))
+{
+ cluster.addLocalConnection(interceptor);
+ std::auto_ptr<sys::ConnectionInputHandler> ih(new ProxyInputHandler(interceptor));
+ codec.setInputHandler(ih);
+ codec.setVersion(v);
+}
+
+ConnectionCodec::~ConnectionCodec() {}
+
+size_t ConnectionCodec::decode(const char* buffer, size_t size) {
+ return interceptor->decode(buffer, size);
+}
+
+bool ConnectionCodec::isClosed() const { return codec.isClosed(); }
+
+size_t ConnectionCodec::encode(const char* buffer, size_t size) { return codec.encode(buffer, size); }
+
+bool ConnectionCodec::canEncode() { return codec.canEncode(); }
+
+void ConnectionCodec::closed() { codec.closed(); }
+
+ProtocolVersion ConnectionCodec::getVersion() const { return codec.getVersion(); }
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/ConnectionCodec.h b/qpid/cpp/src/qpid/cluster/ConnectionCodec.h
new file mode 100644
index 0000000000..17a08904d9
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ConnectionCodec.h
@@ -0,0 +1,82 @@
+#ifndef QPID_CLUSTER_CONNCTIONCODEC_H
+#define QPID_CLUSTER_CONNCTIONCODEC_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/cluster/Connection.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+
+namespace broker {
+class Connection;
+}
+
+namespace cluster {
+class Cluster;
+
+/**
+ * Encapsulates the standard amqp_0_10::ConnectionCodec and sets up
+ * a cluster::Connection for the connection.
+ *
+ * The ConnectionCodec is deleted by the network layer when the
+ * connection closes. The cluster::Connection needs to be kept
+ * around until all cluster business on the connection is complete.
+ *
+ */
+class ConnectionCodec : public sys::ConnectionCodec {
+ public:
+ struct Factory : public sys::ConnectionCodec::Factory {
+ boost::shared_ptr<sys::ConnectionCodec::Factory> next;
+ Cluster& cluster;
+ Factory(boost::shared_ptr<sys::ConnectionCodec::Factory> f, Cluster& c)
+ : next(f), cluster(c) {}
+ sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id,
+ const qpid::sys::SecuritySettings& external);
+ sys::ConnectionCodec* create(sys::OutputControl&, const std::string& id,
+ const qpid::sys::SecuritySettings& external);
+ };
+
+ ConnectionCodec(const framing::ProtocolVersion&, sys::OutputControl& out,
+ const std::string& logId, Cluster& c, bool catchUp, bool isLink,
+ const qpid::sys::SecuritySettings& external);
+ ~ConnectionCodec();
+
+ // ConnectionCodec functions.
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+ void closed();
+ bool isClosed() const;
+ framing::ProtocolVersion getVersion() const;
+ void setSecureConnection(broker::SecureConnection* sc) { interceptor->setSecureConnection(sc); }
+
+ private:
+ amqp_0_10::Connection codec;
+ boost::intrusive_ptr<cluster::Connection> interceptor;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_CONNCTIONCODEC_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.cpp b/qpid/cpp/src/qpid/cluster/Cpg.cpp
new file mode 100644
index 0000000000..0856bcd824
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Cpg.cpp
@@ -0,0 +1,280 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/cluster/Cpg.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/log/Statement.h"
+
+#include <vector>
+#include <limits>
+#include <iterator>
+#include <sstream>
+
+#include <unistd.h>
+
+// This is a macro instead of a function because we don't want to
+// evaluate the MSG argument unless there is an error.
+#define CPG_CHECK(RESULT, MSG) \
+ if ((RESULT) != CPG_OK) throw Exception(errorStr((RESULT), (MSG)))
+
+namespace qpid {
+namespace cluster {
+
+using namespace std;
+
+
+
+Cpg* Cpg::cpgFromHandle(cpg_handle_t handle) {
+ void* cpg=0;
+ CPG_CHECK(cpg_context_get(handle, &cpg), "Cannot get CPG instance.");
+ if (!cpg) throw Exception("Cannot get CPG instance.");
+ return reinterpret_cast<Cpg*>(cpg);
+}
+
+// Applies the same retry-logic to all cpg calls that need it.
+void Cpg::callCpg ( CpgOp & c ) {
+ cpg_error_t result;
+ unsigned int snooze = 10;
+ for ( unsigned int nth_try = 0; nth_try < cpgRetries; ++ nth_try ) {
+ if ( CPG_OK == (result = c.op(handle, & group))) {
+ break;
+ }
+ else if ( result == CPG_ERR_TRY_AGAIN ) {
+ QPID_LOG(info, "Retrying " << c.opName );
+ sys::usleep ( snooze );
+ snooze *= 10;
+ snooze = (snooze <= maxCpgRetrySleep) ? snooze : maxCpgRetrySleep;
+ }
+ else break; // Don't retry unless CPG tells us to.
+ }
+
+ if ( result != CPG_OK )
+ CPG_CHECK(result, c.msg(group));
+}
+
+// Global callback functions.
+void Cpg::globalDeliver (
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ size_t msg_len)
+{
+ cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len);
+}
+
+void Cpg::globalConfigChange(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ const struct cpg_address *members, size_t nMembers,
+ const struct cpg_address *left, size_t nLeft,
+ const struct cpg_address *joined, size_t nJoined
+)
+{
+ cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined);
+}
+
+void Cpg::globalDeliver (
+ cpg_handle_t handle,
+ struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ int msg_len)
+{
+ cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len);
+}
+
+void Cpg::globalConfigChange(
+ cpg_handle_t handle,
+ struct cpg_name *group,
+ struct cpg_address *members, int nMembers,
+ struct cpg_address *left, int nLeft,
+ struct cpg_address *joined, int nJoined
+)
+{
+ cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined);
+}
+
+int Cpg::getFd() {
+ int fd;
+ CPG_CHECK(cpg_fd_get(handle, &fd), "Cannot get CPG file descriptor");
+ return fd;
+}
+
+Cpg::Cpg(Handler& h) : IOHandle(new sys::IOHandlePrivate), handler(h), isShutdown(false) {
+ cpg_callbacks_t callbacks;
+ ::memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.cpg_deliver_fn = &globalDeliver;
+ callbacks.cpg_confchg_fn = &globalConfigChange;
+
+ QPID_LOG(notice, "Initializing CPG");
+ cpg_error_t err = cpg_initialize(&handle, &callbacks);
+ int retries = 6; // FIXME aconway 2009-08-06: make this configurable.
+ while (err == CPG_ERR_TRY_AGAIN && --retries) {
+ QPID_LOG(notice, "Re-trying CPG initialization.");
+ sys::sleep(5);
+ err = cpg_initialize(&handle, &callbacks);
+ }
+ CPG_CHECK(err, "Failed to initialize CPG.");
+ CPG_CHECK(cpg_context_set(handle, this), "Cannot set CPG context");
+ // Note: CPG is currently unix-specific. If CPG is ported to
+ // windows then this needs to be refactored into
+ // qpid::sys::<platform>
+ IOHandle::impl->fd = getFd();
+}
+
+Cpg::~Cpg() {
+ try {
+ shutdown();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error during CPG shutdown: " << e.what());
+ }
+}
+
+void Cpg::join(const std::string& name) {
+ group = name;
+ callCpg ( cpgJoinOp );
+}
+
+void Cpg::leave() {
+ callCpg ( cpgLeaveOp );
+}
+
+
+
+
+bool Cpg::mcast(const iovec* iov, int iovLen) {
+ // Check for flow control
+ cpg_flow_control_state_t flowState;
+ CPG_CHECK(cpg_flow_control_state_get(handle, &flowState), "Cannot get CPG flow control status.");
+ if (flowState == CPG_FLOW_CONTROL_ENABLED)
+ return false;
+
+ cpg_error_t result;
+ do {
+ result = cpg_mcast_joined(handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen);
+ if (result != CPG_ERR_TRY_AGAIN) CPG_CHECK(result, cantMcastMsg(group));
+ } while(result == CPG_ERR_TRY_AGAIN);
+ return true;
+}
+
+void Cpg::shutdown() {
+ if (!isShutdown) {
+ QPID_LOG(debug,"Shutting down CPG");
+ isShutdown=true;
+
+ callCpg ( cpgFinalizeOp );
+ }
+}
+
+void Cpg::dispatchOne() {
+ CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_ONE), "Error in CPG dispatch");
+}
+
+void Cpg::dispatchAll() {
+ CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_ALL), "Error in CPG dispatch");
+}
+
+void Cpg::dispatchBlocking() {
+ CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_BLOCKING), "Error in CPG dispatch");
+}
+
+string Cpg::errorStr(cpg_error_t err, const std::string& msg) {
+ std::ostringstream os;
+ os << msg << ": ";
+ switch (err) {
+ case CPG_OK: os << "ok"; break;
+ case CPG_ERR_LIBRARY: os << "library"; break;
+ case CPG_ERR_TIMEOUT: os << "timeout"; break;
+ case CPG_ERR_TRY_AGAIN: os << "try again"; break;
+ case CPG_ERR_INVALID_PARAM: os << "invalid param"; break;
+ case CPG_ERR_NO_MEMORY: os << "no memory"; break;
+ case CPG_ERR_BAD_HANDLE: os << "bad handle"; break;
+ case CPG_ERR_ACCESS: os << "access denied. You may need to set your group ID to 'ais'"; break;
+ case CPG_ERR_NOT_EXIST: os << "not exist"; break;
+ case CPG_ERR_EXIST: os << "exist"; break;
+ case CPG_ERR_NOT_SUPPORTED: os << "not supported"; break;
+ case CPG_ERR_SECURITY: os << "security"; break;
+ case CPG_ERR_TOO_MANY_GROUPS: os << "too many groups"; break;
+ default: os << ": unknown cpg error " << err;
+ };
+ os << " (" << err << ")";
+ return os.str();
+}
+
+std::string Cpg::cantJoinMsg(const Name& group) {
+ return "Cannot join CPG group "+group.str();
+}
+
+std::string Cpg::cantFinalizeMsg(const Name& group) {
+ return "Cannot finalize CPG group "+group.str();
+}
+
+std::string Cpg::cantLeaveMsg(const Name& group) {
+ return "Cannot leave CPG group "+group.str();
+}
+
+std::string Cpg::cantMcastMsg(const Name& group) {
+ return "Cannot mcast to CPG group "+group.str();
+}
+
+MemberId Cpg::self() const {
+ unsigned int nodeid;
+ CPG_CHECK(cpg_local_get(handle, &nodeid), "Cannot get local CPG identity");
+ return MemberId(nodeid, getpid());
+}
+
+namespace { int byte(uint32_t value, int i) { return (value >> (i*8)) & 0xff; } }
+
+ostream& operator<<(ostream& out, const MemberId& id) {
+ if (id.first) {
+ out << byte(id.first, 0) << "."
+ << byte(id.first, 1) << "."
+ << byte(id.first, 2) << "."
+ << byte(id.first, 3)
+ << ":";
+ }
+ return out << id.second;
+}
+
+ostream& operator<<(ostream& o, const ConnectionId& c) {
+ return o << c.first << "-" << c.second;
+}
+
+std::string MemberId::str() const {
+ char s[8];
+ uint32_t x;
+ x = htonl(first);
+ ::memcpy(s, &x, 4);
+ x = htonl(second);
+ ::memcpy(s+4, &x, 4);
+ return std::string(s,8);
+}
+
+MemberId::MemberId(const std::string& s) {
+ uint32_t x;
+ memcpy(&x, &s[0], 4);
+ first = ntohl(x);
+ memcpy(&x, &s[4], 4);
+ second = ntohl(x);
+}
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.h b/qpid/cpp/src/qpid/cluster/Cpg.h
new file mode 100644
index 0000000000..6b81c602bd
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Cpg.h
@@ -0,0 +1,236 @@
+#ifndef CPG_H
+#define CPG_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/cluster/types.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/scoped_ptr.hpp>
+
+#include <cassert>
+#include <string.h>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Lightweight C++ interface to cpg.h operations.
+ *
+ * Manages a single CPG handle, initialized in ctor, finialzed in destructor.
+ * On error all functions throw Cpg::Exception.
+ *
+ */
+
+class Cpg : public sys::IOHandle {
+ public:
+ struct Exception : public ::qpid::Exception {
+ Exception(const std::string& msg) : ::qpid::Exception(msg) {}
+ };
+
+ struct Name : public cpg_name {
+ Name() { length = 0; }
+ Name(const char* s) { copy(s, strlen(s)); }
+ Name(const char* s, size_t n) { copy(s,n); }
+ Name(const std::string& s) { copy(s.data(), s.size()); }
+ void copy(const char* s, size_t n) {
+ assert(n < CPG_MAX_NAME_LENGTH);
+ memcpy(value, s, n);
+ length=n;
+ }
+
+ std::string str() const { return std::string(value, length); }
+ };
+
+ static std::string str(const cpg_name& n) {
+ return std::string(n.value, n.length);
+ }
+
+ struct Handler {
+ virtual ~Handler() {};
+ virtual void deliver(
+ cpg_handle_t /*handle*/,
+ const struct cpg_name *group,
+ uint32_t /*nodeid*/,
+ uint32_t /*pid*/,
+ void* /*msg*/,
+ int /*msg_len*/) = 0;
+
+ virtual void configChange(
+ cpg_handle_t /*handle*/,
+ const struct cpg_name */*group*/,
+ const struct cpg_address */*members*/, int /*nMembers*/,
+ const struct cpg_address */*left*/, int /*nLeft*/,
+ const struct cpg_address */*joined*/, int /*nJoined*/
+ ) = 0;
+ };
+
+ /** Open a CPG handle.
+ *@param handler for CPG events.
+ */
+ Cpg(Handler&);
+
+ /** Destructor calls shutdown if not already calledx. */
+ ~Cpg();
+
+ /** Disconnect from CPG */
+ void shutdown();
+
+ void dispatchOne();
+ void dispatchAll();
+ void dispatchBlocking();
+
+ void join(const std::string& group);
+ void leave();
+
+ /** Multicast to the group. NB: must not be called concurrently.
+ *
+ *@return true if the message was multi-cast, false if
+ * it was not sent due to flow control.
+ */
+ bool mcast(const iovec* iov, int iovLen);
+
+ cpg_handle_t getHandle() const { return handle; }
+
+ MemberId self() const;
+
+ int getFd();
+
+ private:
+
+ // Maximum number of retries for cog functions that can tell
+ // us to "try again later".
+ static const unsigned int cpgRetries = 5;
+
+ // Don't let sleep-time between cpg retries to go above 0.1 second.
+ static const unsigned int maxCpgRetrySleep = 100000;
+
+
+ // Base class for the Cpg operations that need retry capability.
+ struct CpgOp {
+ std::string opName;
+
+ CpgOp ( std::string opName )
+ : opName(opName) { }
+
+ virtual cpg_error_t op ( cpg_handle_t handle, struct cpg_name * ) = 0;
+ virtual std::string msg(const Name&) = 0;
+ virtual ~CpgOp ( ) { }
+ };
+
+
+ struct CpgJoinOp : public CpgOp {
+ CpgJoinOp ( )
+ : CpgOp ( std::string("cpg_join") ) { }
+
+ cpg_error_t op(cpg_handle_t handle, struct cpg_name * group) {
+ return cpg_join ( handle, group );
+ }
+
+ std::string msg(const Name& name) { return cantJoinMsg(name); }
+ };
+
+ struct CpgLeaveOp : public CpgOp {
+ CpgLeaveOp ( )
+ : CpgOp ( std::string("cpg_leave") ) { }
+
+ cpg_error_t op(cpg_handle_t handle, struct cpg_name * group) {
+ return cpg_leave ( handle, group );
+ }
+
+ std::string msg(const Name& name) { return cantLeaveMsg(name); }
+ };
+
+ struct CpgFinalizeOp : public CpgOp {
+ CpgFinalizeOp ( )
+ : CpgOp ( std::string("cpg_finalize") ) { }
+
+ cpg_error_t op(cpg_handle_t handle, struct cpg_name *) {
+ return cpg_finalize ( handle );
+ }
+
+ std::string msg(const Name& name) { return cantFinalizeMsg(name); }
+ };
+
+ // This fn standardizes retry policy across all Cpg ops that need it.
+ void callCpg ( CpgOp & );
+
+ CpgJoinOp cpgJoinOp;
+ CpgLeaveOp cpgLeaveOp;
+ CpgFinalizeOp cpgFinalizeOp;
+
+ static std::string errorStr(cpg_error_t err, const std::string& msg);
+ static std::string cantJoinMsg(const Name&);
+ static std::string cantLeaveMsg(const Name&);
+ static std::string cantMcastMsg(const Name&);
+ static std::string cantFinalizeMsg(const Name&);
+
+ static Cpg* cpgFromHandle(cpg_handle_t);
+
+ // New versions for corosync 1.0 and higher
+ static void globalDeliver(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ size_t msg_len);
+
+ static void globalConfigChange(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ const struct cpg_address *members, size_t nMembers,
+ const struct cpg_address *left, size_t nLeft,
+ const struct cpg_address *joined, size_t nJoined
+ );
+
+ // Old versions for openais
+ static void globalDeliver(
+ cpg_handle_t handle,
+ struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ int msg_len);
+
+ static void globalConfigChange(
+ cpg_handle_t handle,
+ struct cpg_name *group,
+ struct cpg_address *members, int nMembers,
+ struct cpg_address *left, int nLeft,
+ struct cpg_address *joined, int nJoined
+ );
+
+ cpg_handle_t handle;
+ Handler& handler;
+ bool isShutdown;
+ Name group;
+ sys::Mutex dispatchLock;
+};
+
+inline bool operator==(const cpg_name& a, const cpg_name& b) {
+ return a.length==b.length && strncmp(a.value, b.value, a.length) == 0;
+}
+inline bool operator!=(const cpg_name& a, const cpg_name& b) { return !(a == b); }
+
+}} // namespace qpid::cluster
+
+#endif /*!CPG_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Decoder.cpp b/qpid/cpp/src/qpid/cluster/Decoder.cpp
new file mode 100644
index 0000000000..23ba372d78
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Decoder.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/Decoder.h"
+#include "qpid/cluster/EventFrame.h"
+#include "qpid/framing/ClusterConnectionDeliverCloseBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/AMQFrame.h"
+
+
+namespace qpid {
+namespace cluster {
+
+void Decoder::decode(const EventHeader& eh, const char* data) {
+ sys::Mutex::ScopedLock l(lock);
+ assert(eh.getType() == DATA); // Only handle connection data events.
+ const char* cp = static_cast<const char*>(data);
+ framing::Buffer buf(const_cast<char*>(cp), eh.getSize());
+ framing::FrameDecoder& decoder = map[eh.getConnectionId()];
+ if (decoder.decode(buf)) { // Decoded a frame
+ framing::AMQFrame frame(decoder.getFrame());
+ while (decoder.decode(buf)) {
+ callback(EventFrame(eh, frame));
+ frame = decoder.getFrame();
+ }
+ // Set read-credit on the last frame ending in this event.
+ // Credit will be given when this frame is processed.
+ callback(EventFrame(eh, frame, 1));
+ }
+ else {
+ // We must give 1 unit read credit per event.
+ // This event does not complete any frames so
+ // send an empty frame with the read credit.
+ callback(EventFrame(eh, framing::AMQFrame(), 1));
+ }
+}
+
+void Decoder::erase(const ConnectionId& c) {
+ sys::Mutex::ScopedLock l(lock);
+ map.erase(c);
+}
+
+framing::FrameDecoder& Decoder::get(const ConnectionId& c) {
+ sys::Mutex::ScopedLock l(lock);
+ return map[c];
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Decoder.h b/qpid/cpp/src/qpid/cluster/Decoder.h
new file mode 100644
index 0000000000..3b5ada4a81
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Decoder.h
@@ -0,0 +1,59 @@
+#ifndef QPID_CLUSTER_DECODER_H
+#define QPID_CLUSTER_DECODER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/framing/FrameDecoder.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/function.hpp>
+#include <map>
+
+namespace qpid {
+namespace cluster {
+
+struct EventFrame;
+class EventHeader;
+
+/**
+ * A map of decoders for connections.
+ */
+class Decoder
+{
+ public:
+ typedef boost::function<void(const EventFrame&)> FrameHandler;
+
+ Decoder(FrameHandler fh) : callback(fh) {}
+ void decode(const EventHeader& eh, const char* data);
+ void erase(const ConnectionId&);
+ framing::FrameDecoder& get(const ConnectionId& c);
+
+ private:
+ typedef std::map<ConnectionId, framing::FrameDecoder> Map;
+ sys::Mutex lock;
+ Map map;
+ void process(const EventFrame&);
+ FrameHandler callback;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_DECODER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Dispatchable.h b/qpid/cpp/src/qpid/cluster/Dispatchable.h
new file mode 100644
index 0000000000..e7f0df4218
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Dispatchable.h
@@ -0,0 +1,52 @@
+#ifndef QPID_CLUSTER_DISPATCHABLE_H
+#define QPID_CLUSTER_DISPATCHABLE_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Interface for classes that have some "events" that need dispatching
+ * in a thread.
+ */
+class Dispatchable
+{
+ public:
+ virtual ~Dispatchable() {}
+
+ /** Dispatch one event in current thread. */
+ virtual void dispatchOne() = 0;
+ /** Dispatch all available events, don't block. */
+ virtual void dispatchAll() = 0;
+ /** Blocking loop to dispatch cluster events */
+ virtual void dispatchBlocking() = 0;
+
+ /** Wait for at least one event, then dispatch all available events.
+ * Don't block. Useful for tests.
+ */
+ virtual void dispatchSome() { dispatchOne(); dispatchAll(); }
+
+};
+
+}} // namespace qpid::cluster
+
+
+
+#endif /*!QPID_CLUSTER_DISPATCHABLE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp b/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp
new file mode 100644
index 0000000000..be671c0f48
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ErrorCheck.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/ErrorCheck.h"
+#include "qpid/cluster/EventFrame.h"
+#include "qpid/cluster/ClusterMap.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/framing/ClusterErrorCheckBody.h"
+#include "qpid/framing/ClusterConfigChangeBody.h"
+#include "qpid/log/Statement.h"
+
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+
+using namespace std;
+using namespace framing;
+using namespace framing::cluster;
+
+ErrorCheck::ErrorCheck(Cluster& c)
+ : cluster(c), mcast(c.getMulticast()), frameSeq(0), type(ERROR_TYPE_NONE), connection(0)
+{}
+
+void ErrorCheck::error(
+ Connection& c, ErrorType t, framing::SequenceNumber seq, const MemberSet& ms,
+ const std::string& msg)
+{
+ // Detected a local error, inform cluster and set error state.
+ assert(t != ERROR_TYPE_NONE); // Must be an error.
+ assert(type == ERROR_TYPE_NONE); // Can't be called when already in an error state.
+ type = t;
+ unresolved = ms;
+ frameSeq = seq;
+ connection = &c;
+ message = msg;
+ QPID_LOG(debug, cluster<< (type == ERROR_TYPE_SESSION ? " channel" : " connection")
+ << " error " << frameSeq << " on " << c
+ << " must be resolved with: " << unresolved
+ << ": " << message);
+ mcast.mcastControl(
+ ClusterErrorCheckBody(ProtocolVersion(), type, frameSeq), cluster.getId());
+ // If there are already frames queued up by a previous error, review
+ // them with respect to this new error.
+ for (FrameQueue::iterator i = frames.begin(); i != frames.end(); i = review(i))
+ ;
+}
+
+void ErrorCheck::delivered(const EventFrame& e) {
+ frames.push_back(e);
+ review(frames.end()-1);
+}
+
+// Review a frame in the queue with respect to the current error.
+ErrorCheck::FrameQueue::iterator ErrorCheck::review(const FrameQueue::iterator& i) {
+ FrameQueue::iterator next = i+1;
+ if(!isUnresolved() || !i->frame.getBody() || !i->frame.getMethod())
+ return next; // Only interested in control frames while unresolved.
+ const AMQMethodBody* method = i->frame.getMethod();
+ if (method->isA<const ClusterErrorCheckBody>()) {
+ const ClusterErrorCheckBody* errorCheck =
+ static_cast<const ClusterErrorCheckBody*>(method);
+
+ if (errorCheck->getFrameSeq() == frameSeq) { // Addresses current error
+ next = frames.erase(i); // Drop matching error check controls
+ if (errorCheck->getType() < type) { // my error is worse than his
+ QPID_LOG(critical, cluster
+ << " local error " << frameSeq << " did not occur on member "
+ << i->getMemberId()
+ << ": " << message);
+ throw Exception(
+ QPID_MSG("local error did not occur on all cluster members " << ": " << message));
+ }
+ else { // his error is worse/same as mine.
+ QPID_LOG(debug, cluster << " error " << frameSeq
+ << " resolved with " << i->getMemberId());
+ unresolved.erase(i->getMemberId());
+ checkResolved();
+ }
+ }
+ else if (errorCheck->getFrameSeq() < frameSeq && errorCheck->getType() != NONE
+ && i->connectionId.getMember() != cluster.getId())
+ {
+ // This error occured before the current error so we
+ // have processed past it.
+ next = frames.erase(i); // Drop the error check control
+ respondNone(i->connectionId.getMember(), errorCheck->getType(),
+ errorCheck->getFrameSeq());
+ }
+ // if errorCheck->getFrameSeq() > frameSeq then leave it in the queue.
+ }
+ else if (method->isA<const ClusterConfigChangeBody>()) {
+ const ClusterConfigChangeBody* configChange =
+ static_cast<const ClusterConfigChangeBody*>(method);
+ if (configChange) {
+ MemberSet members(decodeMemberSet(configChange->getMembers()));
+ QPID_LOG(debug, cluster << " apply config change to error "
+ << frameSeq << ": " << members);
+ MemberSet intersect;
+ set_intersection(members.begin(), members.end(),
+ unresolved.begin(), unresolved.end(),
+ inserter(intersect, intersect.begin()));
+ unresolved.swap(intersect);
+ checkResolved();
+ }
+ }
+ return next;
+}
+
+void ErrorCheck::checkResolved() {
+ if (unresolved.empty()) { // No more potentially conflicted members, we're clear.
+ type = ERROR_TYPE_NONE;
+ QPID_LOG(debug, cluster << " error " << frameSeq << " resolved.");
+ }
+ else
+ QPID_LOG(debug, cluster << " error " << frameSeq
+ << " must be resolved with " << unresolved);
+}
+
+EventFrame ErrorCheck::getNext() {
+ assert(canProcess());
+ EventFrame e(frames.front());
+ frames.pop_front();
+ return e;
+}
+
+void ErrorCheck::respondNone(const MemberId& from, uint8_t type, framing::SequenceNumber frameSeq) {
+ // Don't respond to non-errors or to my own errors.
+ if (type == ERROR_TYPE_NONE || from == cluster.getId())
+ return;
+ QPID_LOG(debug, cluster << " error " << frameSeq << " did not occur locally.");
+ mcast.mcastControl(
+ ClusterErrorCheckBody(ProtocolVersion(), ERROR_TYPE_NONE, frameSeq),
+ cluster.getId()
+ );
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/ErrorCheck.h b/qpid/cpp/src/qpid/cluster/ErrorCheck.h
new file mode 100644
index 0000000000..a417b2ec25
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ErrorCheck.h
@@ -0,0 +1,90 @@
+#ifndef QPID_CLUSTER_ERRORCHECK_H
+#define QPID_CLUSTER_ERRORCHECK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/MemberSet.h"
+#include "qpid/cluster/Multicaster.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/SequenceNumber.h"
+#include <boost/function.hpp>
+#include <deque>
+#include <set>
+
+namespace qpid {
+namespace cluster {
+
+struct EventFrame;
+class Cluster;
+class Multicaster;
+class Connection;
+
+/**
+ * Error checking logic.
+ *
+ * When an error occurs queue up frames until we can determine if all
+ * nodes experienced the error. If not, we shut down.
+ */
+class ErrorCheck
+{
+ public:
+ typedef framing::cluster::ErrorType ErrorType;
+ typedef framing::SequenceNumber SequenceNumber;
+
+ ErrorCheck(Cluster&);
+
+ /** A local error has occured */
+ void error(Connection&, ErrorType, SequenceNumber frameSeq, const MemberSet&,
+ const std::string& msg);
+
+ /** Called when a frame is delivered */
+ void delivered(const EventFrame&);
+
+ /**@pre canProcess **/
+ EventFrame getNext();
+
+ bool canProcess() const { return type == NONE && !frames.empty(); }
+
+ bool isUnresolved() const { return type != NONE; }
+
+ /** Respond to an error check saying we had no error. */
+ void respondNone(const MemberId&, uint8_t type, SequenceNumber frameSeq);
+
+ private:
+ static const ErrorType NONE = framing::cluster::ERROR_TYPE_NONE;
+ typedef std::deque<EventFrame> FrameQueue;
+ FrameQueue::iterator review(const FrameQueue::iterator&);
+ void checkResolved();
+
+ Cluster& cluster;
+ Multicaster& mcast;
+ FrameQueue frames;
+ MemberSet unresolved;
+ SequenceNumber frameSeq;
+ ErrorType type;
+ Connection* connection;
+ std::string message;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_ERRORCHECK_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Event.cpp b/qpid/cpp/src/qpid/cluster/Event.cpp
new file mode 100644
index 0000000000..da2bc89d8c
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Event.cpp
@@ -0,0 +1,134 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/cluster/types.h"
+#include "qpid/cluster/Event.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/RefCountedBuffer.h"
+#include "qpid/assert.h"
+#include <ostream>
+#include <iterator>
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+
+using framing::Buffer;
+using framing::AMQFrame;
+
+const size_t EventHeader::HEADER_SIZE =
+ sizeof(uint8_t) + // type
+ sizeof(uint64_t) + // connection pointer only, CPG provides member ID.
+ sizeof(uint32_t) // payload size
+ ;
+
+EventHeader::EventHeader(EventType t, const ConnectionId& c, size_t s)
+ : type(t), connectionId(c), size(s) {}
+
+
+Event::Event() {}
+
+Event::Event(EventType t, const ConnectionId& c, size_t s)
+ : EventHeader(t,c,s), store(RefCountedBuffer::create(s+HEADER_SIZE))
+{}
+
+void EventHeader::decode(const MemberId& m, framing::Buffer& buf) {
+ QPID_ASSERT(buf.available() >= HEADER_SIZE);
+ type = (EventType)buf.getOctet();
+ QPID_ASSERT(type == DATA || type == CONTROL);
+ connectionId = ConnectionId(m, buf.getLongLong());
+ size = buf.getLong();
+}
+
+Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) {
+ Event e;
+ e.decode(m, buf); // Header
+ QPID_ASSERT(buf.available() >= e.size);
+ e.store = RefCountedBuffer::create(e.size + HEADER_SIZE);
+ memcpy(e.getData(), buf.getPointer() + buf.getPosition(), e.size);
+ return e;
+}
+
+Event Event::control(const framing::AMQFrame& f, const ConnectionId& cid) {
+ Event e(CONTROL, cid, f.encodedSize());
+ Buffer buf(e);
+ f.encode(buf);
+ return e;
+}
+
+Event Event::control(const framing::AMQBody& body, const ConnectionId& cid) {
+ return control(framing::AMQFrame(body), cid);
+}
+
+iovec Event::toIovec() const {
+ encodeHeader();
+ iovec iov = { const_cast<char*>(getStore()), getStoreSize() };
+ return iov;
+}
+
+void EventHeader::encode(Buffer& b) const {
+ b.putOctet(type);
+ b.putLongLong(connectionId.getNumber());
+ b.putLong(size);
+}
+
+// Encode my header in my buffer.
+void Event::encodeHeader () const {
+ Buffer b(const_cast<char*>(getStore()), HEADER_SIZE);
+ encode(b);
+ assert(b.getPosition() == HEADER_SIZE);
+}
+
+Event::operator Buffer() const {
+ return Buffer(const_cast<char*>(getData()), getSize());
+}
+
+const AMQFrame& Event::getFrame() const {
+ assert(type == CONTROL);
+ if (!frame.getBody()) {
+ Buffer buf(*this);
+ QPID_ASSERT(frame.decode(buf));
+ }
+ return frame;
+}
+
+static const char* EVENT_TYPE_NAMES[] = { "data", "control" };
+
+std::ostream& operator<< (std::ostream& o, EventType t) {
+ return o << EVENT_TYPE_NAMES[t];
+}
+
+std::ostream& operator<< (std::ostream& o, const EventHeader& e) {
+ return o << "Event[" << e.getConnectionId() << " " << e.getType()
+ << " " << e.getSize() << " bytes]";
+}
+
+std::ostream& operator<< (std::ostream& o, const Event& e) {
+ o << "Event[" << e.getConnectionId() << " ";
+ if (e.getType() == CONTROL)
+ o << e.getFrame();
+ else
+ o << " data " << e.getSize() << " bytes";
+ return o << "]";
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Event.h b/qpid/cpp/src/qpid/cluster/Event.h
new file mode 100644
index 0000000000..13283edff7
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Event.h
@@ -0,0 +1,116 @@
+#ifndef QPID_CLUSTER_EVENT_H
+#define QPID_CLUSTER_EVENT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/BufferRef.h"
+#include "qpid/framing/AMQFrame.h"
+#include <sys/uio.h> // For iovec
+#include <iosfwd>
+
+#include "qpid/cluster/types.h"
+
+namespace qpid {
+
+namespace framing {
+class AMQBody;
+class AMQFrame;
+class Buffer;
+}
+
+namespace cluster {
+
+/** Header data for a multicast event */
+class EventHeader {
+ public:
+ EventHeader(EventType t=DATA, const ConnectionId& c=ConnectionId(), size_t size=0);
+ void decode(const MemberId& m, framing::Buffer&);
+ void encode(framing::Buffer&) const;
+
+ EventType getType() const { return type; }
+ ConnectionId getConnectionId() const { return connectionId; }
+ MemberId getMemberId() const { return connectionId.getMember(); }
+
+ /** Size of payload data, excluding header. */
+ size_t getSize() const { return size; }
+ /** Size of header + payload. */
+ size_t getStoreSize() const { return size + HEADER_SIZE; }
+
+ bool isCluster() const { return connectionId.getNumber() == 0; }
+ bool isConnection() const { return connectionId.getNumber() != 0; }
+ bool isControl() const { return type == CONTROL; }
+
+ protected:
+ static const size_t HEADER_SIZE;
+
+ EventType type;
+ ConnectionId connectionId;
+ size_t size;
+};
+
+/**
+ * Events are sent to/received from the cluster.
+ * Refcounted so they can be stored on queues.
+ */
+class Event : public EventHeader {
+ public:
+ Event();
+ /** Create an event with a buffer that can hold size bytes plus an event header. */
+ Event(EventType t, const ConnectionId& c, size_t);
+
+ /** Create an event copied from delivered data. */
+ static Event decodeCopy(const MemberId& m, framing::Buffer&);
+
+ /** Create a control event. */
+ static Event control(const framing::AMQBody&, const ConnectionId&);
+
+ /** Create a control event. */
+ static Event control(const framing::AMQFrame&, const ConnectionId&);
+
+ // Data excluding header.
+ char* getData() { return store.begin() + HEADER_SIZE; }
+ const char* getData() const { return store.begin() + HEADER_SIZE; }
+
+ // Store including header
+ char* getStore() { return store.begin(); }
+ const char* getStore() const { return store.begin(); }
+
+ const framing::AMQFrame& getFrame() const;
+
+ operator framing::Buffer() const;
+
+ iovec toIovec() const;
+
+ private:
+ void encodeHeader() const;
+
+ BufferRef store;
+ mutable framing::AMQFrame frame;
+};
+
+std::ostream& operator << (std::ostream&, const Event&);
+std::ostream& operator << (std::ostream&, const EventHeader&);
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_EVENT_H*/
diff --git a/qpid/cpp/src/qpid/cluster/EventFrame.cpp b/qpid/cpp/src/qpid/cluster/EventFrame.cpp
new file mode 100644
index 0000000000..5fbe1fe57c
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/EventFrame.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/EventFrame.h"
+#include "qpid/cluster/Connection.h"
+
+namespace qpid {
+namespace cluster {
+
+EventFrame::EventFrame() {}
+
+EventFrame::EventFrame(const EventHeader& e, const framing::AMQFrame& f, int rc)
+ : connectionId(e.getConnectionId()), frame(f), readCredit(rc), type(e.getType())
+{}
+
+std::ostream& operator<<(std::ostream& o, const EventFrame& e) {
+ if (e.frame.getBody()) o << e.frame;
+ else o << "null-frame";
+ o << " " << e.type << " " << e.connectionId;
+ if (e.readCredit) o << " read-credit=" << e.readCredit;
+ return o;
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/EventFrame.h b/qpid/cpp/src/qpid/cluster/EventFrame.h
new file mode 100644
index 0000000000..6b702a9bf8
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/EventFrame.h
@@ -0,0 +1,60 @@
+#ifndef QPID_CLUSTER_EVENTFRAME_H
+#define QPID_CLUSTER_EVENTFRAME_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/cluster/Event.h"
+#include "qpid/framing/AMQFrame.h"
+#include <boost/intrusive_ptr.hpp>
+#include <iosfwd>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * A frame decoded from an Event.
+ */
+struct EventFrame
+{
+ public:
+ EventFrame();
+
+ EventFrame(const EventHeader& e, const framing::AMQFrame& f, int rc=0);
+
+ bool isCluster() const { return connectionId.getNumber() == 0; }
+ bool isConnection() const { return connectionId.getNumber() != 0; }
+ bool isLastInEvent() const { return readCredit; }
+ MemberId getMemberId() const { return connectionId.getMember(); }
+
+
+ ConnectionId connectionId;
+ framing::AMQFrame frame;
+ int readCredit; ///< last frame in an event, give credit when processed.
+ EventType type;
+};
+
+std::ostream& operator<<(std::ostream& o, const EventFrame& e);
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_EVENTFRAME_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ExpiryPolicy.cpp b/qpid/cpp/src/qpid/cluster/ExpiryPolicy.cpp
new file mode 100644
index 0000000000..d9a7b0122a
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ExpiryPolicy.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Message.h"
+#include "qpid/cluster/ExpiryPolicy.h"
+#include "qpid/cluster/Multicaster.h"
+#include "qpid/framing/ClusterMessageExpiredBody.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace cluster {
+
+ExpiryPolicy::ExpiryPolicy(Multicaster& m, const MemberId& id, sys::Timer& t)
+ : expiryId(1), expiredPolicy(new Expired), mcast(m), memberId(id), timer(t) {}
+
+struct ExpiryTask : public sys::TimerTask {
+ ExpiryTask(const boost::intrusive_ptr<ExpiryPolicy>& policy, uint64_t id, sys::AbsTime when)
+ : TimerTask(when,"ExpiryPolicy"), expiryPolicy(policy), expiryId(id) {}
+ void fire() { expiryPolicy->sendExpire(expiryId); }
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+ const uint64_t expiryId;
+};
+
+// Called while receiving an update
+void ExpiryPolicy::setId(uint64_t id) {
+ sys::Mutex::ScopedLock l(lock);
+ expiryId = id;
+}
+
+// Called while giving an update
+uint64_t ExpiryPolicy::getId() const {
+ sys::Mutex::ScopedLock l(lock);
+ return expiryId;
+}
+
+// Called in enqueuing connection thread
+void ExpiryPolicy::willExpire(broker::Message& m) {
+ uint64_t id;
+ {
+ // When messages are fanned out to multiple queues, update sends
+ // them as independenty messages so we can have multiple messages
+ // with the same expiry ID.
+ //
+ sys::Mutex::ScopedLock l(lock);
+ id = expiryId++;
+ if (!id) { // This is an update of an already-expired message.
+ m.setExpiryPolicy(expiredPolicy);
+ }
+ else {
+ assert(unexpiredByMessage.find(&m) == unexpiredByMessage.end());
+ // If this is an update, the id may already exist
+ unexpiredById.insert(IdMessageMap::value_type(id, &m));
+ unexpiredByMessage[&m] = id;
+ }
+ }
+ timer.add(new ExpiryTask(this, id, m.getExpiration()));
+}
+
+// Called in dequeueing connection thread
+void ExpiryPolicy::forget(broker::Message& m) {
+ sys::Mutex::ScopedLock l(lock);
+ MessageIdMap::iterator i = unexpiredByMessage.find(&m);
+ assert(i != unexpiredByMessage.end());
+ unexpiredById.erase(i->second);
+ unexpiredByMessage.erase(i);
+}
+
+// Called in dequeueing connection or cleanup thread.
+bool ExpiryPolicy::hasExpired(broker::Message& m) {
+ sys::Mutex::ScopedLock l(lock);
+ return unexpiredByMessage.find(&m) == unexpiredByMessage.end();
+}
+
+// Called in timer thread
+void ExpiryPolicy::sendExpire(uint64_t id) {
+ {
+ sys::Mutex::ScopedLock l(lock);
+ // Don't multicast an expiry notice if message is already forgotten.
+ if (unexpiredById.find(id) == unexpiredById.end()) return;
+ }
+ mcast.mcastControl(framing::ClusterMessageExpiredBody(framing::ProtocolVersion(), id), memberId);
+}
+
+// Called in CPG deliver thread.
+void ExpiryPolicy::deliverExpire(uint64_t id) {
+ sys::Mutex::ScopedLock l(lock);
+ std::pair<IdMessageMap::iterator, IdMessageMap::iterator> expired = unexpiredById.equal_range(id);
+ IdMessageMap::iterator i = expired.first;
+ while (i != expired.second) {
+ i->second->setExpiryPolicy(expiredPolicy); // hasExpired() == true;
+ unexpiredByMessage.erase(i->second);
+ unexpiredById.erase(i++);
+ }
+}
+
+// Called in update thread on the updater.
+boost::optional<uint64_t> ExpiryPolicy::getId(broker::Message& m) {
+ sys::Mutex::ScopedLock l(lock);
+ MessageIdMap::iterator i = unexpiredByMessage.find(&m);
+ return i == unexpiredByMessage.end() ? boost::optional<uint64_t>() : i->second;
+}
+
+bool ExpiryPolicy::Expired::hasExpired(broker::Message&) { return true; }
+void ExpiryPolicy::Expired::willExpire(broker::Message&) { }
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/ExpiryPolicy.h b/qpid/cpp/src/qpid/cluster/ExpiryPolicy.h
new file mode 100644
index 0000000000..77a656aa68
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ExpiryPolicy.h
@@ -0,0 +1,93 @@
+#ifndef QPID_CLUSTER_EXPIRYPOLICY_H
+#define QPID_CLUSTER_EXPIRYPOLICY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/optional.hpp>
+#include <map>
+
+namespace qpid {
+
+namespace broker {
+class Message;
+}
+
+namespace sys {
+class Timer;
+}
+
+namespace cluster {
+class Multicaster;
+
+/**
+ * Cluster expiry policy
+ */
+class ExpiryPolicy : public broker::ExpiryPolicy
+{
+ public:
+ ExpiryPolicy(Multicaster&, const MemberId&, sys::Timer&);
+
+ void willExpire(broker::Message&);
+ bool hasExpired(broker::Message&);
+ void forget(broker::Message&);
+
+ // Send expiration notice to cluster.
+ void sendExpire(uint64_t);
+
+ // Cluster delivers expiry notice.
+ void deliverExpire(uint64_t);
+
+ void setId(uint64_t id);
+ uint64_t getId() const;
+
+ boost::optional<uint64_t> getId(broker::Message&);
+
+ private:
+ typedef std::map<broker::Message*, uint64_t> MessageIdMap;
+ // When messages are fanned out to multiple queues, update sends
+ // them as independenty messages so we can have multiple messages
+ // with the same expiry ID.
+ typedef std::multimap<uint64_t, broker::Message*> IdMessageMap;
+
+ struct Expired : public broker::ExpiryPolicy {
+ bool hasExpired(broker::Message&);
+ void willExpire(broker::Message&);
+ };
+
+ mutable sys::Mutex lock;
+ MessageIdMap unexpiredByMessage;
+ IdMessageMap unexpiredById;
+ uint64_t expiryId;
+ boost::intrusive_ptr<Expired> expiredPolicy;
+ Multicaster& mcast;
+ MemberId memberId;
+ sys::Timer& timer;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_EXPIRYPOLICY_H*/
diff --git a/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp b/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp
new file mode 100644
index 0000000000..84232dac1b
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/FailoverExchange.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/FailoverExchange.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/Array.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+using namespace std;
+
+using namespace broker;
+using namespace framing;
+
+const string FailoverExchange::typeName("amq.failover");
+
+FailoverExchange::FailoverExchange(management::Manageable* parent, Broker* b) : Exchange(typeName, parent, b ) {
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type(typeName);
+}
+
+void FailoverExchange::setUrls(const vector<Url>& u) {
+ Lock l(lock);
+ urls = u;
+}
+
+void FailoverExchange::updateUrls(const vector<Url>& u) {
+ Lock l(lock);
+ urls=u;
+ if (urls.empty()) return;
+ std::for_each(queues.begin(), queues.end(),
+ boost::bind(&FailoverExchange::sendUpdate, this, _1));
+}
+
+string FailoverExchange::getType() const { return typeName; }
+
+bool FailoverExchange::bind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) {
+ Lock l(lock);
+ sendUpdate(queue);
+ return queues.insert(queue).second;
+}
+
+bool FailoverExchange::unbind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) {
+ Lock l(lock);
+ return queues.erase(queue);
+}
+
+bool FailoverExchange::isBound(Queue::shared_ptr queue, const string* const, const framing::FieldTable*) {
+ Lock l(lock);
+ return queues.find(queue) != queues.end();
+}
+
+void FailoverExchange::route(Deliverable&, const string& , const framing::FieldTable* ) {
+ QPID_LOG(warning, "Message received by exchange " << typeName << " ignoring");
+}
+
+void FailoverExchange::sendUpdate(const Queue::shared_ptr& queue) {
+ // Called with lock held.
+ if (urls.empty()) return;
+ framing::Array array(0x95);
+ for (Urls::const_iterator i = urls.begin(); i != urls.end(); ++i)
+ array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str())));
+ const ProtocolVersion v;
+ boost::intrusive_ptr<Message> msg(new Message);
+ AMQFrame command(MessageTransferBody(v, typeName, 1, 0));
+ command.setLastSegment(false);
+ msg->getFrames().append(command);
+ AMQHeaderBody header;
+ header.get<MessageProperties>(true)->setContentLength(0);
+ header.get<MessageProperties>(true)->getApplicationHeaders().setArray(typeName, array);
+ AMQFrame headerFrame(header);
+ headerFrame.setFirstSegment(false);
+ msg->getFrames().append(headerFrame);
+ DeliverableMessage(msg).deliverTo(queue);
+}
+
+
+}} // namespace cluster
diff --git a/qpid/cpp/src/qpid/cluster/FailoverExchange.h b/qpid/cpp/src/qpid/cluster/FailoverExchange.h
new file mode 100644
index 0000000000..2e1edfc0ae
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/FailoverExchange.h
@@ -0,0 +1,71 @@
+#ifndef QPID_CLUSTER_FAILOVEREXCHANGE_H
+#define QPID_CLUSTER_FAILOVEREXCHANGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/Url.h"
+
+#include <vector>
+#include <set>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Failover exchange provides failover host list, as specified in AMQP 0-10.
+ */
+class FailoverExchange : public broker::Exchange
+{
+ public:
+ static const std::string typeName;
+
+ FailoverExchange(management::Manageable* parent, broker::Broker* b);
+
+ /** Set the URLs but don't send an update.*/
+ void setUrls(const std::vector<Url>&);
+ /** Set the URLs and send an update.*/
+ void updateUrls(const std::vector<Url>&);
+
+ // Exchange overrides
+ std::string getType() const;
+ bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args);
+ bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args);
+ bool isBound(boost::shared_ptr<broker::Queue> queue, const std::string* const routingKey, const framing::FieldTable* const args);
+ void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args);
+
+ private:
+ void sendUpdate(const boost::shared_ptr<broker::Queue>&);
+
+ typedef sys::Mutex::ScopedLock Lock;
+ typedef std::vector<Url> Urls;
+ typedef std::set<boost::shared_ptr<broker::Queue> > Queues;
+
+ sys::Mutex lock;
+ Urls urls;
+ Queues queues;
+
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_FAILOVEREXCHANGE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp
new file mode 100644
index 0000000000..c8ecc13f2c
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp
@@ -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.
+ *
+ */
+#include "InitialStatusMap.h"
+#include "StoreStatus.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+#include <vector>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace cluster {
+
+using namespace std;
+using namespace boost;
+using namespace framing::cluster;
+using namespace framing;
+
+InitialStatusMap::InitialStatusMap(const MemberId& self_, size_t size_)
+ : self(self_), completed(), resendNeeded(), size(size_)
+{}
+
+void InitialStatusMap::configChange(const MemberSet& members) {
+ resendNeeded = false;
+ bool wasComplete = isComplete();
+ if (firstConfig.empty()) firstConfig = members;
+ MemberSet::const_iterator i = members.begin();
+ Map::iterator j = map.begin();
+ while (i != members.end() || j != map.end()) {
+ if (i == members.end()) { // j not in members, member left
+ firstConfig.erase(j->first);
+ Map::iterator k = j++;
+ map.erase(k);
+ }
+ else if (j == map.end()) { // i not in map, member joined
+ resendNeeded = true;
+ map[*i] = optional<Status>();
+ ++i;
+ }
+ else if (*i < j->first) { // i not in map, member joined
+ resendNeeded = true;
+ map[*i] = optional<Status>();
+ ++i;
+ }
+ else if (*i > j->first) { // j not in members, member left
+ firstConfig.erase(j->first);
+ Map::iterator k = j++;
+ map.erase(k);
+ }
+ else {
+ i++; j++;
+ }
+ }
+ if (resendNeeded) { // Clear all status
+ for (Map::iterator i = map.begin(); i != map.end(); ++i)
+ i->second = optional<Status>();
+ }
+ completed = isComplete() && !wasComplete; // Set completed on the transition.
+}
+
+void InitialStatusMap::received(const MemberId& m, const Status& s){
+ bool wasComplete = isComplete();
+ map[m] = s;
+ completed = isComplete() && !wasComplete; // Set completed on the transition.
+}
+
+bool InitialStatusMap::notInitialized(const Map::value_type& v) {
+ return !v.second;
+}
+
+bool InitialStatusMap::isComplete() const {
+ return !map.empty() && find_if(map.begin(), map.end(), &notInitialized) == map.end();
+}
+
+bool InitialStatusMap::transitionToComplete() {
+ return completed;
+}
+
+bool InitialStatusMap::isResendNeeded() {
+ bool ret = resendNeeded;
+ resendNeeded = false;
+ return ret;
+}
+
+bool InitialStatusMap::isActiveEntry(const Map::value_type& v) {
+ return v.second && v.second->getActive();
+}
+
+bool InitialStatusMap::hasStore(const Map::value_type& v) {
+ return v.second &&
+ (v.second->getStoreState() == STORE_STATE_CLEAN_STORE ||
+ v.second->getStoreState() == STORE_STATE_DIRTY_STORE);
+}
+
+bool InitialStatusMap::isActive() {
+ assert(isComplete());
+ return (find_if(map.begin(), map.end(), &isActiveEntry) != map.end());
+}
+
+bool InitialStatusMap::isUpdateNeeded() {
+ assert(isComplete());
+ // We need an update if there are any active members.
+ if (isActive()) return true;
+
+ // Otherwise it depends on store status, get my own status:
+ Map::iterator me = map.find(self);
+ assert(me != map.end());
+ assert(me->second);
+ switch (me->second->getStoreState()) {
+ case STORE_STATE_NO_STORE:
+ case STORE_STATE_EMPTY_STORE:
+ // If anybody has a store then we need an update.
+ return find_if(map.begin(), map.end(), &hasStore) != map.end();
+ case STORE_STATE_DIRTY_STORE: return true;
+ case STORE_STATE_CLEAN_STORE: return false; // Use our own store
+ }
+ return false;
+}
+
+MemberSet InitialStatusMap::getElders() const {
+ assert(isComplete());
+ MemberSet elders;
+ for (MemberSet::const_iterator i = firstConfig.begin(); i != firstConfig.end(); ++i) {
+ // *i is in my first config, so a potential elder.
+ if (*i == self) continue; // Not my own elder
+ Map::const_iterator j = map.find(*i);
+ assert(j != map.end());
+ assert(j->second);
+ const Status& s = *j->second;
+ // If I'm not in i's first config then i is older than me.
+ // Otherwise we were born in the same configuration so use
+ // member ID to break the tie.
+ MemberSet iFirstConfig = decodeMemberSet(s.getFirstConfig());
+ if (iFirstConfig.find(self) == iFirstConfig.end() || *i > self)
+ elders.insert(*i);
+ }
+ return elders;
+}
+
+// Get cluster ID from an active member or the youngest newcomer.
+Uuid InitialStatusMap::getClusterId() {
+ assert(isComplete());
+ assert(!map.empty());
+ Map::iterator i = find_if(map.begin(), map.end(), &isActiveEntry);
+ if (i != map.end())
+ return i->second->getClusterId(); // An active member
+ else
+ return map.begin()->second->getClusterId(); // Youngest newcomer in node-id order
+}
+
+void checkId(Uuid& expect, const Uuid& actual, const string& msg) {
+ if (!expect) expect = actual;
+ assert(expect);
+ if (expect != actual)
+ throw Exception(msg);
+}
+
+void InitialStatusMap::checkConsistent() {
+ assert(isComplete());
+ int clean = 0;
+ int dirty = 0;
+ int empty = 0;
+ int none = 0;
+ int active = 0;
+ Uuid clusterId;
+ Uuid shutdownId;
+
+ bool initialCluster = !isActive();
+ for (Map::iterator i = map.begin(); i != map.end(); ++i) {
+ assert(i->second);
+ if (i->second->getActive()) ++active;
+ switch (i->second->getStoreState()) {
+ case STORE_STATE_NO_STORE: ++none; break;
+ case STORE_STATE_EMPTY_STORE: ++empty; break;
+ case STORE_STATE_DIRTY_STORE:
+ ++dirty;
+ checkId(clusterId, i->second->getClusterId(),
+ "Cluster-ID mismatch. Stores belong to different clusters.");
+ break;
+ case STORE_STATE_CLEAN_STORE:
+ ++clean;
+ checkId(clusterId, i->second->getClusterId(),
+ "Cluster-ID mismatch. Stores belong to different clusters.");
+ // Only need shutdownId to match if we are in an initially forming cluster.
+ if (initialCluster)
+ checkId(shutdownId, i->second->getShutdownId(),
+ "Shutdown-ID mismatch. Stores were not shut down together");
+ break;
+ }
+ }
+ // Can't mix transient and persistent members.
+ if (none && (clean+dirty+empty))
+ throw Exception("Mixing transient and persistent brokers in a cluster");
+
+ if (map.size() >= size) {
+ // All initial members are present. If there are no active
+ // members and there are dirty stores there must be at least
+ // one clean store.
+ if (!active && dirty && !clean)
+ throw Exception("Cannot recover, no clean store.");
+ }
+}
+
+std::string InitialStatusMap::getFirstConfigStr() const {
+ assert(!firstConfig.empty());
+ return encodeMemberSet(firstConfig);
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/InitialStatusMap.h b/qpid/cpp/src/qpid/cluster/InitialStatusMap.h
new file mode 100644
index 0000000000..a5a600365e
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/InitialStatusMap.h
@@ -0,0 +1,91 @@
+#ifndef QPID_CLUSTER_INITIALSTATUSMAP_H
+#define QPID_CLUSTER_INITIALSTATUSMAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "MemberSet.h"
+#include <qpid/framing/ClusterInitialStatusBody.h>
+#include <boost/optional.hpp>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Track status of cluster members during initialization.
+ *
+ * When a new member joins the CPG cluster, all members send an initial-status
+ * control. This map tracks those controls and provides data to make descisions
+ * about joining the cluster.
+ *
+ */
+class InitialStatusMap
+{
+ public:
+ typedef framing::ClusterInitialStatusBody Status;
+
+ InitialStatusMap(const MemberId& self, size_t size);
+ /** Process a config change. May make isResendNeeded() true. */
+ void configChange(const MemberSet& newConfig);
+ /** @return true if we need to re-send status */
+ bool isResendNeeded();
+
+ /** Process received status */
+ void received(const MemberId&, const Status& is);
+
+ /**@return true if the map has an entry for all current cluster members. */
+ bool isComplete() const;
+
+ size_t getActualSize() const { return map.size(); }
+ size_t getRequiredSize() const { return size; }
+
+ /**@return true if the map was completed by the last config change or received. */
+ bool transitionToComplete();
+ /**@pre isComplete(). @return this node's elders */
+ MemberSet getElders() const;
+ /**@pre isComplete(). @return True if there are active members of the cluster. */
+ bool isActive();
+ /**@pre isComplete(). @return True if we need to request an update. */
+ bool isUpdateNeeded();
+ /**@pre isComplete(). @return Cluster-wide cluster ID. */
+ framing::Uuid getClusterId();
+ /**@pre isComplete(). @throw Exception if there are any inconsistencies. */
+ void checkConsistent();
+
+ /** Get first config-change for this member, encoded as a string.
+ *@pre configChange has been called at least once.
+ */
+ std::string getFirstConfigStr() const;
+ private:
+ typedef std::map<MemberId, boost::optional<Status> > Map;
+ static bool notInitialized(const Map::value_type&);
+ static bool isActiveEntry(const Map::value_type&);
+ static bool hasStore(const Map::value_type&);
+
+ Map map;
+ MemberSet firstConfig;
+ MemberId self;
+ bool completed, resendNeeded;
+ size_t size;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_INITIALSTATUSMAP_H*/
diff --git a/qpid/cpp/src/qpid/cluster/LockedConnectionMap.h b/qpid/cpp/src/qpid/cluster/LockedConnectionMap.h
new file mode 100644
index 0000000000..ac744d4f94
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/LockedConnectionMap.h
@@ -0,0 +1,65 @@
+#ifndef QPID_CLUSTER_LOCKEDCONNECTIONMAP_H
+#define QPID_CLUSTER_LOCKEDCONNECTIONMAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/cluster/Connection.h"
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Thread safe map of connections.
+ */
+class LockedConnectionMap
+{
+ public:
+ void insert(const ConnectionPtr& c) {
+ sys::Mutex::ScopedLock l(lock);
+ assert(map.find(c->getId()) == map.end());
+ map[c->getId()] = c;
+ }
+
+ ConnectionPtr getErase(const ConnectionId& c) {
+ sys::Mutex::ScopedLock l(lock);
+ Map::iterator i = map.find(c);
+ if (i != map.end()) {
+ ConnectionPtr cp = i->second;
+ map.erase(i);
+ return cp;
+ }
+ else
+ return 0;
+ }
+
+ void clear() { sys::Mutex::ScopedLock l(lock); map.clear(); }
+
+ private:
+ typedef std::map<ConnectionId, ConnectionPtr> Map;
+ mutable sys::Mutex lock;
+ Map map;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_LOCKEDCONNECTIONMAP_H*/
diff --git a/qpid/cpp/src/qpid/cluster/McastFrameHandler.h b/qpid/cpp/src/qpid/cluster/McastFrameHandler.h
new file mode 100644
index 0000000000..17e4c2e9f0
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/McastFrameHandler.h
@@ -0,0 +1,46 @@
+#ifndef QPID_CLUSTER_MCASTFRAMEHANDLER_H
+#define QPID_CLUSTER_MCASTFRAMEHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/cluster/Multicaster.h"
+#include "qpid/framing/FrameHandler.h"
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * A frame handler that multicasts frames as CONTROL events.
+ */
+class McastFrameHandler : public framing::FrameHandler
+{
+ public:
+ McastFrameHandler(Multicaster& m, const ConnectionId& cid) : mcast(m), connection(cid) {}
+ void handle(framing::AMQFrame& frame) { mcast.mcastControl(frame, connection); }
+ private:
+ Multicaster& mcast;
+ ConnectionId connection;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_MCASTFRAMEHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/MemberSet.cpp b/qpid/cpp/src/qpid/cluster/MemberSet.cpp
new file mode 100644
index 0000000000..97748947b3
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/MemberSet.cpp
@@ -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.
+ *
+ */
+#include "MemberSet.h"
+#include <ostream>
+#include <iterator>
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+
+std::string encodeMemberSet(const MemberSet& m) {
+ std::string addresses;
+ for (MemberSet::const_iterator i = m.begin(); i != m.end(); ++i)
+ addresses.append(i->str());
+ return addresses;
+}
+
+MemberSet decodeMemberSet(const std::string& s) {
+ MemberSet set;
+ for (std::string::const_iterator i = s.begin(); i < s.end(); i += 8) {
+ assert(size_t(i-s.begin())+8 <= s.size());
+ set.insert(MemberId(std::string(i, i+8)));
+ }
+ return set;
+}
+
+MemberSet intersection(const MemberSet& a, const MemberSet& b)
+{
+ MemberSet intersection;
+ std::set_intersection(a.begin(), a.end(),
+ b.begin(), b.end(),
+ std::inserter(intersection, intersection.begin()));
+ return intersection;
+
+}
+
+std::ostream& operator<<(std::ostream& o, const MemberSet& ms) {
+ copy(ms.begin(), ms.end(), std::ostream_iterator<MemberId>(o, " "));
+ return o;
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/MemberSet.h b/qpid/cpp/src/qpid/cluster/MemberSet.h
new file mode 100644
index 0000000000..7c97145dc1
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/MemberSet.h
@@ -0,0 +1,45 @@
+#ifndef QPID_CLUSTER_MEMBERSET_H
+#define QPID_CLUSTER_MEMBERSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "types.h"
+#include <set>
+#include <iosfwd>
+
+namespace qpid {
+namespace cluster {
+
+typedef std::set<MemberId> MemberSet;
+
+std::string encodeMemberSet(const MemberSet&);
+
+MemberSet decodeMemberSet(const std::string&);
+
+MemberSet intersection(const MemberSet& a, const MemberSet& b);
+
+std::ostream& operator<<(std::ostream& o, const MemberSet& ms);
+
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_MEMBERSET_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
new file mode 100644
index 0000000000..8916de9628
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/cluster/Multicaster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/AMQFrame.h"
+
+namespace qpid {
+namespace cluster {
+
+Multicaster::Multicaster(Cpg& cpg_,
+ const boost::shared_ptr<sys::Poller>& poller,
+ boost::function<void()> onError_) :
+ onError(onError_), cpg(cpg_),
+ queue(boost::bind(&Multicaster::sendMcast, this, _1), poller),
+ ready(false), bypass(true)
+{}
+
+void Multicaster::mcastControl(const framing::AMQBody& body, const ConnectionId& id) {
+ mcast(Event::control(body, id));
+}
+
+void Multicaster::mcastControl(const framing::AMQFrame& frame, const ConnectionId& id) {
+ mcast(Event::control(frame, id));
+}
+
+void Multicaster::mcastBuffer(const char* data, size_t size, const ConnectionId& id) {
+ Event e(DATA, id, size);
+ memcpy(e.getData(), data, size);
+ mcast(e);
+}
+
+void Multicaster::mcast(const Event& e) {
+ {
+ sys::Mutex::ScopedLock l(lock);
+ if (!ready && e.isConnection()) {
+ holdingQueue.push_back(e);
+ return;
+ }
+ }
+ QPID_LOG(trace, "MCAST " << e);
+ if (bypass) { // direct, don't queue
+ iovec iov = e.toIovec();
+ while (!cpg.mcast(&iov, 1))
+ ;
+ }
+ else
+ queue.push(e);
+}
+
+Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(const PollableEventQueue::Batch& values) {
+ try {
+ PollableEventQueue::Batch::const_iterator i = values.begin();
+ while( i != values.end()) {
+ iovec iov = i->toIovec();
+ if (!cpg.mcast(&iov, 1)) {
+ // cpg didn't send because of CPG flow control.
+ break;
+ }
+ ++i;
+ }
+ return i;
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(critical, "Multicast error: " << e.what());
+ queue.stop();
+ onError();
+ return values.end();
+ }
+}
+
+void Multicaster::start() {
+ queue.start();
+ bypass = false;
+}
+
+void Multicaster::setReady() {
+ sys::Mutex::ScopedLock l(lock);
+ ready = true;
+ std::for_each(holdingQueue.begin(), holdingQueue.end(), boost::bind(&Multicaster::mcast, this, _1));
+ holdingQueue.clear();
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.h b/qpid/cpp/src/qpid/cluster/Multicaster.h
new file mode 100644
index 0000000000..f70bd5ca31
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.h
@@ -0,0 +1,92 @@
+#ifndef QPID_CLUSTER_MULTICASTER_H
+#define QPID_CLUSTER_MULTICASTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/types.h"
+#include "qpid/cluster/Event.h"
+#include "qpid/sys/PollableQueue.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
+#include <deque>
+
+namespace qpid {
+
+namespace sys {
+class Poller;
+}
+
+namespace cluster {
+
+class Cpg;
+
+/**
+ * Multicast to the cluster. Shared, thread safe object.
+ *
+ * holding mode: Hold connection events for later multicast. Cluster
+ * events are never held. Used during PRE_INIT/INIT state when we
+ * want to hold any connection traffic till we are read in the
+ * cluster.
+ *
+ * bypass mode: Multicast cluster events directly in the calling
+ * thread. This mode is used by cluster in PRE_INIT state the poller
+ * is not yet be active.
+ *
+ * Multicaster is created in bypass+holding mode, they are disabled by
+ * start and setReady respectively.
+ */
+class Multicaster
+{
+ public:
+ /** Starts in initializing mode. */
+ Multicaster(Cpg& cpg_,
+ const boost::shared_ptr<sys::Poller>&,
+ boost::function<void()> onError
+ );
+ void mcastControl(const framing::AMQBody& controlBody, const ConnectionId&);
+ void mcastControl(const framing::AMQFrame& controlFrame, const ConnectionId&);
+ void mcastBuffer(const char*, size_t, const ConnectionId&);
+ void mcast(const Event& e);
+
+ /** Start the pollable queue, turn off bypass mode. */
+ void start();
+ /** Switch to ready mode, release held messages. */
+ void setReady();
+
+ private:
+ typedef sys::PollableQueue<Event> PollableEventQueue;
+ typedef std::deque<Event> PlainEventQueue;
+
+ PollableEventQueue::Batch::const_iterator sendMcast(const PollableEventQueue::Batch& );
+
+ sys::Mutex lock;
+ boost::function<void()> onError;
+ Cpg& cpg;
+ PollableEventQueue queue;
+ bool ready;
+ PlainEventQueue holdingQueue;
+ std::vector<struct ::iovec> ioVector;
+ bool bypass;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_MULTICASTER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h b/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h
new file mode 100644
index 0000000000..566a82476e
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h
@@ -0,0 +1,47 @@
+#ifndef QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H
+#define QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <qpid/sys/ConnectionOutputHandler.h>
+
+namespace qpid {
+
+namespace framing { class AMQFrame; }
+
+namespace cluster {
+
+/**
+ * Output handler shadow connections, simply discards frames.
+ */
+class NoOpConnectionOutputHandler : public sys::ConnectionOutputHandler
+{
+ public:
+ virtual void send(framing::AMQFrame&) {}
+ virtual void close() {}
+ virtual void abort() {}
+ virtual void activateOutput() {}
+ virtual void giveReadCredit(int32_t) {}
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Numbering.h b/qpid/cpp/src/qpid/cluster/Numbering.h
new file mode 100644
index 0000000000..99e152c212
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Numbering.h
@@ -0,0 +1,68 @@
+#ifndef QPID_CLUSTER_NUMBERING_H
+#define QPID_CLUSTER_NUMBERING_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <map>
+#include <vector>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * A set of numbered T, with two way mapping number->T T->number
+ * Used to construct numberings of objects by code sending and receiving updates.
+ */
+template <class T> class Numbering
+{
+ public:
+ size_t size() const { return byNumber.size(); }
+
+ size_t add(const T& t) {
+ size_t n = (*this)[t]; // Already in the set?
+ if (n == size()) {
+ byObject[t] = n;
+ byNumber.push_back(t);
+ }
+ return n;
+ }
+
+ void clear() { byObject.clear(); byNumber.clear(); }
+
+ /**@return object at index n or T() if n > size() */
+ T operator[](size_t n) const { return(n < size()) ? byNumber[n] : T(); }
+
+ /**@return index of t or size() if t is not in the map */
+ size_t operator[](const T& t) const {
+ typename Map::const_iterator i = byObject.find(t);
+ return (i != byObject.end()) ? i->second : size();
+ }
+
+ private:
+ typedef std::map<T, size_t> Map;
+ Map byObject;
+ std::vector<T> byNumber;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_NUMBERING_H*/
diff --git a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp
new file mode 100644
index 0000000000..4bf03eefa2
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/OutputInterceptor.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/log/Statement.h"
+#include <boost/current_function.hpp>
+
+
+namespace qpid {
+namespace cluster {
+
+using namespace framing;
+using namespace std;
+
+NoOpConnectionOutputHandler OutputInterceptor::discardHandler;
+
+OutputInterceptor::OutputInterceptor(Connection& p, sys::ConnectionOutputHandler& h)
+ : parent(p), closing(false), next(&h), sendMax(2048), sent(0), sentDoOutput(false)
+{}
+
+void OutputInterceptor::send(framing::AMQFrame& f) {
+ sys::Mutex::ScopedLock l(lock);
+ next->send(f);
+}
+
+void OutputInterceptor::activateOutput() {
+ sys::Mutex::ScopedLock l(lock);
+ if (parent.isCatchUp())
+ next->activateOutput();
+ else
+ sendDoOutput(sendMax, l);
+}
+
+void OutputInterceptor::abort() {
+ sys::Mutex::ScopedLock l(lock);
+ if (parent.isLocal()) {
+ next->abort();
+ }
+}
+
+void OutputInterceptor::giveReadCredit(int32_t credit) {
+ sys::Mutex::ScopedLock l(lock);
+ next->giveReadCredit(credit);
+}
+
+// Called in write thread when the IO layer has no more data to write.
+// We only process IO callbacks in the write thread during catch-up.
+// Normally we run doOutput only on delivery of doOutput requests.
+bool OutputInterceptor::doOutput() {
+ parent.doCatchupIoCallbacks();
+ return false;
+}
+
+// Send output up to limit, calculate new limit.
+void OutputInterceptor::deliverDoOutput(uint32_t limit) {
+ sys::Mutex::ScopedLock l(lock);
+ sentDoOutput = false;
+ sendMax = limit;
+ size_t newLimit = limit;
+ if (parent.isLocal()) {
+ size_t buffered = next->getBuffered();
+ if (buffered == 0 && sent == sendMax) // Could have sent more, increase the limit.
+ newLimit = sendMax*2;
+ else if (buffered > 0 && sent > 1) // Data left unsent, reduce the limit.
+ newLimit = (sendMax + sent) / 2;
+ }
+ sent = 0;
+ while (sent < limit) {
+ {
+ sys::Mutex::ScopedUnlock u(lock);
+ if (!parent.getBrokerConnection()->doOutput()) break;
+ }
+ ++sent;
+ }
+ if (sent == limit) sendDoOutput(newLimit, l);
+}
+
+void OutputInterceptor::sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&) {
+ if (parent.isLocal() && !sentDoOutput && !closing) {
+ sentDoOutput = true;
+ parent.getCluster().getMulticast().mcastControl(
+ ClusterConnectionDeliverDoOutputBody(ProtocolVersion(), newLimit),
+ parent.getId());
+ }
+}
+
+// Called in connection thread when local connection closes.
+void OutputInterceptor::closeOutput() {
+ sys::Mutex::ScopedLock l(lock);
+ closing = true;
+ next = &discardHandler;
+}
+
+void OutputInterceptor::close() {
+ sys::Mutex::ScopedLock l(lock);
+ next->close();
+}
+
+size_t OutputInterceptor::getBuffered() const {
+ sys::Mutex::ScopedLock l(lock);
+ return next->getBuffered();
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/OutputInterceptor.h b/qpid/cpp/src/qpid/cluster/OutputInterceptor.h
new file mode 100644
index 0000000000..3abf5273a0
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/OutputInterceptor.h
@@ -0,0 +1,79 @@
+#ifndef QPID_CLUSTER_OUTPUTINTERCEPTOR_H
+#define QPID_CLUSTER_OUTPUTINTERCEPTOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/NoOpConnectionOutputHandler.h"
+#include "qpid/sys/ConnectionOutputHandler.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/ConnectionFactory.h"
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace framing { class AMQFrame; }
+namespace cluster {
+
+class Connection;
+
+/**
+ * Interceptor for connection OutputHandler, manages outgoing message replication.
+ */
+class OutputInterceptor : public sys::ConnectionOutputHandler {
+ public:
+ OutputInterceptor(cluster::Connection& p, sys::ConnectionOutputHandler& h);
+
+ // sys::ConnectionOutputHandler functions
+ void send(framing::AMQFrame& f);
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t);
+ void close();
+ size_t getBuffered() const;
+
+ // Delivery point for doOutput requests.
+ void deliverDoOutput(uint32_t limit);
+ // Intercept doOutput requests on Connection.
+ bool doOutput();
+
+ void closeOutput();
+
+ uint32_t getSendMax() const { return sendMax; }
+ void setSendMax(uint32_t sendMax_) { sendMax=sendMax_; }
+
+ cluster::Connection& parent;
+
+ private:
+ typedef sys::Mutex::ScopedLock Locker;
+
+ void sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&);
+
+ mutable sys::Mutex lock;
+ bool closing;
+ sys::ConnectionOutputHandler* next;
+ static NoOpConnectionOutputHandler discardHandler;
+ uint32_t sendMax, sent;
+ bool sentDoOutput;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_OUTPUTINTERCEPTOR_H*/
diff --git a/qpid/cpp/src/qpid/cluster/PollableQueue.h b/qpid/cpp/src/qpid/cluster/PollableQueue.h
new file mode 100644
index 0000000000..10e2ed6ac3
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/PollableQueue.h
@@ -0,0 +1,89 @@
+#ifndef QPID_CLUSTER_POLLABLEQUEUE_H
+#define QPID_CLUSTER_POLLABLEQUEUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/PollableQueue.h"
+#include <qpid/log/Statement.h>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * More convenient version of PollableQueue that handles iterating
+ * over the batch and error handling.
+ *
+ * Constructed in "bypass" mode where items are processed directly
+ * rather than put on the queue. This is important for the
+ * PRE_INIT stage when Cluster is pumping CPG dispatch directly
+ * before the poller has started.
+ *
+ * Calling start() starts the pollable queue and disabled bypass mode.
+ */
+template <class T> class PollableQueue : public sys::PollableQueue<T> {
+ public:
+ typedef boost::function<void (const T&)> Callback;
+ typedef boost::function<void()> ErrorCallback;
+
+ PollableQueue(Callback f, ErrorCallback err, const std::string& msg,
+ const boost::shared_ptr<sys::Poller>& poller)
+ : sys::PollableQueue<T>(boost::bind(&PollableQueue<T>::handleBatch, this, _1),
+ poller),
+ callback(f), error(err), message(msg), bypass(true)
+ {}
+
+ typename sys::PollableQueue<T>::Batch::const_iterator
+ handleBatch(const typename sys::PollableQueue<T>::Batch& values) {
+ try {
+ typename sys::PollableQueue<T>::Batch::const_iterator i = values.begin();
+ while (i != values.end() && !this->isStopped()) {
+ callback(*i);
+ ++i;
+ }
+ return i;
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(critical, message << ": " << e.what());
+ this->stop();
+ error();
+ return values.end();
+ }
+ }
+
+ void push(const T& t) {
+ if (bypass) callback(t);
+ else sys::PollableQueue<T>::push(t);
+ }
+
+ void bypassOff() { bypass = false; }
+
+ private:
+ Callback callback;
+ ErrorCallback error;
+ std::string message;
+ bool bypass;
+};
+
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_POLLABLEQUEUE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp b/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp
new file mode 100644
index 0000000000..b8d94b95a5
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp
@@ -0,0 +1,69 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/cluster/PollerDispatch.h"
+
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace cluster {
+
+PollerDispatch::PollerDispatch(Cpg& c, boost::shared_ptr<sys::Poller> p,
+ boost::function<void()> e)
+ : cpg(c), poller(p), onError(e),
+ dispatchHandle(cpg,
+ boost::bind(&PollerDispatch::dispatch, this, _1), // read
+ 0, // write
+ boost::bind(&PollerDispatch::disconnect, this, _1) // disconnect
+ ),
+ started(false)
+{}
+
+PollerDispatch::~PollerDispatch() {
+ if (started)
+ dispatchHandle.stopWatch();
+}
+
+void PollerDispatch::start() {
+ dispatchHandle.startWatch(poller);
+ started = true;
+}
+
+// Entry point: called by IO to dispatch CPG events.
+void PollerDispatch::dispatch(sys::DispatchHandle& h) {
+ try {
+ cpg.dispatchAll();
+ h.rewatch();
+ } catch (const std::exception& e) {
+ QPID_LOG(critical, "Error in cluster dispatch: " << e.what());
+ onError();
+ }
+}
+
+// Entry point: called if disconnected from CPG.
+void PollerDispatch::disconnect(sys::DispatchHandle& ) {
+ if (!poller->hasShutdown()) {
+ QPID_LOG(critical, "Disconnected from cluster");
+ onError();
+ }
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/PollerDispatch.h b/qpid/cpp/src/qpid/cluster/PollerDispatch.h
new file mode 100644
index 0000000000..63801e0de9
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/PollerDispatch.h
@@ -0,0 +1,60 @@
+#ifndef QPID_CLUSTER_POLLERDISPATCH_H
+#define QPID_CLUSTER_POLLERDISPATCH_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/Cpg.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/DispatchHandle.h"
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Dispatch CPG events via the poller.
+ */
+class PollerDispatch {
+ public:
+ PollerDispatch(Cpg&, boost::shared_ptr<sys::Poller> poller,
+ boost::function<void()> onError) ;
+
+ ~PollerDispatch();
+
+ void start();
+
+ private:
+ // Poller callbacks
+ void dispatch(sys::DispatchHandle&); // Dispatch CPG events.
+ void disconnect(sys::DispatchHandle&); // CPG was disconnected
+
+ Cpg& cpg;
+ boost::shared_ptr<sys::Poller> poller;
+ boost::function<void()> onError;
+ sys::DispatchHandleRef dispatchHandle;
+ bool started;
+
+
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_POLLERDISPATCH_H*/
diff --git a/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h b/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h
new file mode 100644
index 0000000000..ad7f2c44bd
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/ProxyInputHandler.h
@@ -0,0 +1,56 @@
+#ifndef QPID_CLUSTER_PROXYINPUTHANDLER_H
+#define QPID_CLUSTER_PROXYINPUTHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ConnectionInputHandler.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+
+namespace framing { class AMQFrame; }
+
+namespace cluster {
+
+/**
+ * Proxies ConnectionInputHandler functions and ensures target.closed()
+ * is called, on deletion if not before.
+ */
+class ProxyInputHandler : public sys::ConnectionInputHandler
+{
+ public:
+ ProxyInputHandler(boost::intrusive_ptr<cluster::Connection> t) : target(t) {}
+ ~ProxyInputHandler() { closed(); }
+
+ void received(framing::AMQFrame& f) { target->received(f); }
+ void closed() { if (target) target->closed(); target = 0; }
+ void idleOut() { target->idleOut(); }
+ void idleIn() { target->idleIn(); }
+ bool doOutput() { return target->doOutput(); }
+
+ private:
+ boost::intrusive_ptr<cluster::Connection> target;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_PROXYINPUTHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Quorum.h b/qpid/cpp/src/qpid/cluster/Quorum.h
new file mode 100644
index 0000000000..bbfa473f94
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Quorum.h
@@ -0,0 +1,32 @@
+#ifndef QPID_CLUSTER_QUORUM_H
+#define QPID_CLUSTER_QUORUM_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "config.h"
+
+#if HAVE_LIBCMAN_H
+#include "qpid/cluster/Quorum_cman.h"
+#else
+#include "qpid/cluster/Quorum_null.h"
+#endif
+
+#endif /*!QPID_CLUSTER_QUORUM_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp
new file mode 100644
index 0000000000..728f824b16
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/cluster/Quorum_cman.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Options.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+
+namespace qpid {
+namespace cluster {
+
+namespace {
+
+boost::function<void()> errorFn;
+
+void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int /*arg*/) {
+ if (reason == CMAN_REASON_STATECHANGE && !cman_is_quorate(handle)) {
+ QPID_LOG(critical, "Lost contact with cluster quorum.");
+ if (errorFn) errorFn();
+ cman_stop_notification(handle);
+ }
+}
+}
+
+Quorum::Quorum(boost::function<void()> err) : cman(0), cmanFd(0) {
+ errorFn = err;
+}
+
+Quorum::~Quorum() {
+ if (dispatchHandle.get()) dispatchHandle->stopWatch();
+ dispatchHandle.reset();
+ if (cman) cman_finish(cman);
+}
+
+void Quorum::start(boost::shared_ptr<sys::Poller> p) {
+ poller = p;
+ QPID_LOG(debug, "Connecting to quorum service.");
+ cman = cman_init(0);
+ if (cman == 0) throw ErrnoException("Can't connect to cman service");
+ if (!cman_is_quorate(cman)) {
+ QPID_LOG(notice, "Waiting for cluster quorum.");
+ while(!cman_is_quorate(cman)) sys::sleep(5);
+ }
+ int err = cman_start_notification(cman, cmanCallbackFn);
+ if (err != 0) throw ErrnoException("Can't register for cman notifications");
+ watch(getFd());
+}
+
+void Quorum::watch(int fd) {
+ cmanFd = fd;
+ if (dispatchHandle.get()) dispatchHandle->stopWatch();
+ ioHandle.reset(new sys::PosixIOHandle(cmanFd));
+ dispatchHandle.reset(
+ new sys::DispatchHandleRef(
+ *ioHandle, // This must outlive the dispatchHandleRef
+ boost::bind(&Quorum::dispatch, this, _1), // read
+ 0, // write
+ boost::bind(&Quorum::disconnect, this, _1) // disconnect
+ ));
+ dispatchHandle->startWatch(poller);
+}
+
+int Quorum::getFd() {
+ int fd = cman_get_fd(cman);
+ if (fd == 0) throw ErrnoException("Can't get cman file descriptor");
+ return fd;
+}
+
+void Quorum::dispatch(sys::DispatchHandle&) {
+ try {
+ cman_dispatch(cman, CMAN_DISPATCH_ALL);
+ int fd = getFd();
+ if (fd != cmanFd) watch(fd);
+ } catch (const std::exception& e) {
+ QPID_LOG(critical, "Error in quorum dispatch: " << e.what());
+ errorFn();
+ }
+}
+
+void Quorum::disconnect(sys::DispatchHandle&) {
+ QPID_LOG(critical, "Disconnected from quorum service");
+ errorFn();
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.h b/qpid/cpp/src/qpid/cluster/Quorum_cman.h
new file mode 100644
index 0000000000..98e6baee89
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.h
@@ -0,0 +1,65 @@
+#ifndef QPID_CLUSTER_QUORUM_CMAN_H
+#define QPID_CLUSTER_QUORUM_CMAN_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/sys/DispatchHandle.h>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <memory>
+
+extern "C" {
+#include <libcman.h>
+}
+
+namespace qpid {
+namespace sys {
+class Poller;
+class PosixIOHandle;
+}
+
+namespace cluster {
+class Cluster;
+
+class Quorum {
+ public:
+ Quorum(boost::function<void ()> onError);
+ ~Quorum();
+ void start(boost::shared_ptr<sys::Poller>);
+
+ private:
+ void dispatch(sys::DispatchHandle&);
+ void disconnect(sys::DispatchHandle&);
+ int getFd();
+ void watch(int fd);
+
+ cman_handle_t cman;
+ int cmanFd;
+ std::auto_ptr<sys::PosixIOHandle> ioHandle;
+ std::auto_ptr<sys::DispatchHandleRef> dispatchHandle;
+ boost::shared_ptr<sys::Poller> poller;
+};
+
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_QUORUM_CMAN_H*/
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_null.h b/qpid/cpp/src/qpid/cluster/Quorum_null.h
new file mode 100644
index 0000000000..dc27f0a43b
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/Quorum_null.h
@@ -0,0 +1,42 @@
+#ifndef QPID_CLUSTER_QUORUM_NULL_H
+#define QPID_CLUSTER_QUORUM_NULL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace cluster {
+class Cluster;
+
+/** Null implementation of quorum. */
+
+class Quorum {
+ public:
+ Quorum(boost::function<void ()>) {}
+ void start(boost::shared_ptr<sys::Poller>) {}
+};
+
+#endif /*!QPID_CLUSTER_QUORUM_NULL_H*/
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/RetractClient.cpp b/qpid/cpp/src/qpid/cluster/RetractClient.cpp
new file mode 100644
index 0000000000..a8c4b0d543
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/RetractClient.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/RetractClient.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/ClusterConnectionRetractOfferBody.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace cluster {
+
+using namespace framing;
+
+namespace {
+
+struct AutoClose {
+ client::Connection& connection;
+ AutoClose(client::Connection& c) : connection(c) {}
+ ~AutoClose() { connection.close(); }
+};
+}
+
+RetractClient::RetractClient(const Url& u, const client::ConnectionSettings& cs)
+ : url(u), connectionSettings(cs)
+{}
+
+RetractClient::~RetractClient() { delete this; }
+
+
+void RetractClient::run() {
+ try {
+ client::Connection c = UpdateClient::catchUpConnection();
+ c.open(url, connectionSettings);
+ AutoClose ac(c);
+ AMQFrame retract((ClusterConnectionRetractOfferBody()));
+ client::ConnectionAccess::getImpl(c)->expand(retract.encodedSize(), false);
+ client::ConnectionAccess::getImpl(c)->handle(retract);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, " while retracting retract to " << url << ": " << e.what());
+ }
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/RetractClient.h b/qpid/cpp/src/qpid/cluster/RetractClient.h
new file mode 100644
index 0000000000..533fc3f7ef
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/RetractClient.h
@@ -0,0 +1,50 @@
+#ifndef QPID_CLUSTER_RETRACTCLIENT_H
+#define QPID_CLUSTER_RETRACTCLIENT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/Url.h"
+
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * A client that retracts an offer to a remote broker using AMQP. @see UpdateClient
+ */
+class RetractClient : public sys::Runnable {
+ public:
+
+ RetractClient(const Url&, const client::ConnectionSettings&);
+ ~RetractClient();
+ void run(); // Will delete this when finished.
+
+ private:
+ Url url;
+ client::ConnectionSettings connectionSettings;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_RETRACTCLIENT_H*/
diff --git a/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.cpp b/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.cpp
new file mode 100644
index 0000000000..6ddef66226
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.cpp
@@ -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.
+ *
+ */
+#include "qpid/cluster/SecureConnectionFactory.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/cluster/ConnectionCodec.h"
+#include "qpid/broker/SecureConnection.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/log/Statement.h"
+#include <memory>
+
+
+namespace qpid {
+namespace cluster {
+
+using framing::ProtocolVersion;
+using qpid::sys::SecuritySettings;
+using qpid::broker::SecureConnection;
+
+typedef std::auto_ptr<qpid::broker::SecureConnection> SecureConnectionPtr;
+typedef std::auto_ptr<qpid::sys::ConnectionCodec> CodecPtr;
+
+SecureConnectionFactory::SecureConnectionFactory(CodecFactoryPtr f) : codecFactory(f) {
+}
+
+sys::ConnectionCodec*
+SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ CodecPtr codec(codecFactory->create(v, out, id, external));
+ ConnectionCodec* clusterCodec = dynamic_cast<qpid::cluster::ConnectionCodec*>(codec.get());
+ if (clusterCodec) {
+ SecureConnectionPtr sc(new SecureConnection());
+ clusterCodec->setSecureConnection(sc.get());
+ sc->setCodec(codec);
+ return sc.release();
+ }
+ return 0;
+}
+
+sys::ConnectionCodec*
+SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id,
+ const SecuritySettings& external) {
+ // used to create connections from one broker to another
+ CodecPtr codec(codecFactory->create(out, id, external));
+ ConnectionCodec* clusterCodec = dynamic_cast<qpid::cluster::ConnectionCodec*>(codec.get());
+ if (clusterCodec) {
+ SecureConnectionPtr sc(new SecureConnection());
+ clusterCodec->setSecureConnection(sc.get());
+ sc->setCodec(codec);
+ return sc.release();
+ }
+ return 0;
+}
+
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.h b/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.h
new file mode 100644
index 0000000000..24d1fcfee5
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/SecureConnectionFactory.h
@@ -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.
+ *
+ */
+#ifndef QPID_CLUSTER_SecureconnectionFactory
+#define QPID_CLUSTER_SecureconnectionFactory
+
+#include "qpid/sys/ConnectionCodec.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+
+namespace broker {
+ class Broker;
+}
+
+namespace cluster {
+
+class SecureConnectionFactory : public qpid::sys::ConnectionCodec::Factory
+{
+ public:
+ typedef boost::shared_ptr<qpid::sys::ConnectionCodec::Factory> CodecFactoryPtr;
+ SecureConnectionFactory(CodecFactoryPtr f);
+
+ qpid::sys::ConnectionCodec* create(
+ framing::ProtocolVersion, qpid::sys::OutputControl&, const std::string& id,
+ const qpid::sys::SecuritySettings&
+ );
+
+ /** Return "preferred" codec for outbound connections. */
+ qpid::sys::ConnectionCodec* create(
+ qpid::sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&
+ );
+
+ private:
+ CodecFactoryPtr codecFactory;
+};
+
+}} // namespace qpid::cluster
+
+
+#endif // QPID_CLUSTER_SecureconnectionFactory
diff --git a/qpid/cpp/src/qpid/cluster/StoreStatus.cpp b/qpid/cpp/src/qpid/cluster/StoreStatus.cpp
new file mode 100644
index 0000000000..14c999bb05
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/StoreStatus.cpp
@@ -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.
+ *
+ */
+#include "StoreStatus.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include "qpid/log/Statement.h"
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/scoped_array.hpp>
+#include <fstream>
+#include <sstream>
+
+namespace qpid {
+namespace cluster {
+
+using framing::Uuid;
+using namespace framing::cluster;
+namespace fs=boost::filesystem;
+using namespace std;
+
+StoreStatus::StoreStatus(const std::string& d)
+ : state(STORE_STATE_NO_STORE), dataDir(d)
+{}
+
+namespace {
+
+const char* SUBDIR="cluster";
+const char* STORE_STATUS="store.status";
+
+string readFile(const fs::path& path) {
+ fs::ifstream is;
+ is.exceptions(std::ios::badbit | std::ios::failbit);
+ is.open(path);
+ // get length of file:
+ is.seekg (0, ios::end);
+ size_t length = is.tellg();
+ is.seekg (0, ios::beg);
+ // load data
+ boost::scoped_array<char> buffer(new char[length]);
+ is.read(buffer.get(), length);
+ is.close();
+ return string(buffer.get(), length);
+}
+
+void writeFile(const fs::path& path, const string& data) {
+ fs::ofstream os;
+ os.exceptions(std::ios::badbit | std::ios::failbit);
+ os.open(path);
+ os.write(data.data(), data.size());
+ os.close();
+}
+
+} // namespace
+
+
+void StoreStatus::load() {
+ if (dataDir.empty()) {
+ throw Exception(QPID_MSG("No data-dir: When a store is loaded together with clustering, --data-dir must be specified."));
+ }
+ try {
+ fs::path dir = fs::path(dataDir, fs::native)/SUBDIR;
+ create_directory(dir);
+ fs::path file = dir/STORE_STATUS;
+ if (fs::exists(file)) {
+ string data = readFile(file);
+ istringstream is(data);
+ is.exceptions(std::ios::badbit | std::ios::failbit);
+ is >> ws >> clusterId >> ws >> shutdownId;
+ if (!clusterId)
+ throw Exception(QPID_MSG("Invalid cluster store state, no cluster-id"));
+ if (shutdownId) state = STORE_STATE_CLEAN_STORE;
+ else state = STORE_STATE_DIRTY_STORE;
+ }
+ else { // Starting from empty store
+ clusterId = Uuid(true);
+ save();
+ state = STORE_STATE_EMPTY_STORE;
+ }
+ }
+ catch (const std::exception&e) {
+ throw Exception(QPID_MSG("Cannot load cluster store status: " << e.what()));
+ }
+}
+
+void StoreStatus::save() {
+ if (dataDir.empty()) return;
+ try {
+ ostringstream os;
+ os << clusterId << endl << shutdownId << endl;
+ fs::path file = fs::path(dataDir, fs::native)/SUBDIR/STORE_STATUS;
+ writeFile(file, os.str());
+ }
+ catch (const std::exception& e) {
+ throw Exception(QPID_MSG("Cannot save cluster store status: " << e.what()));
+ }
+}
+
+bool StoreStatus::hasStore() const {
+ return state != framing::cluster::STORE_STATE_NO_STORE;
+}
+
+void StoreStatus::dirty() {
+ assert(hasStore());
+ if (shutdownId) {
+ shutdownId = Uuid();
+ save();
+ }
+ state = STORE_STATE_DIRTY_STORE;
+}
+
+void StoreStatus::clean(const Uuid& shutdownId_) {
+ assert(hasStore());
+ assert(shutdownId_);
+ if (shutdownId_ != shutdownId) {
+ shutdownId = shutdownId_;
+ save();
+ }
+ state = STORE_STATE_CLEAN_STORE;
+}
+
+void StoreStatus::setClusterId(const Uuid& clusterId_) {
+ clusterId = clusterId_;
+ save();
+}
+
+const char* stateName(StoreState s) {
+ switch (s) {
+ case STORE_STATE_NO_STORE: return "none";
+ case STORE_STATE_EMPTY_STORE: return "empty";
+ case STORE_STATE_DIRTY_STORE: return "dirty";
+ case STORE_STATE_CLEAN_STORE: return "clean";
+ }
+ assert(0);
+ return "unknown";
+}
+
+ostream& operator<<(ostream& o, framing::cluster::StoreState s) { return o << stateName(s); }
+
+ostream& operator<<(ostream& o, const StoreStatus& s) {
+ o << s.getState();
+ if (s.getState() == STORE_STATE_DIRTY_STORE)
+ o << " cluster-id=" << s.getClusterId();
+ if (s.getState() == STORE_STATE_CLEAN_STORE) {
+ o << " cluster-id=" << s.getClusterId()
+ << " shutdown-id=" << s.getShutdownId();
+ }
+ return o;
+}
+
+}} // namespace qpid::cluster
+
diff --git a/qpid/cpp/src/qpid/cluster/StoreStatus.h b/qpid/cpp/src/qpid/cluster/StoreStatus.h
new file mode 100644
index 0000000000..7442fcf02c
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/StoreStatus.h
@@ -0,0 +1,68 @@
+#ifndef QPID_CLUSTER_STORESTATE_H
+#define QPID_CLUSTER_STORESTATE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/enum.h"
+#include <iosfwd>
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * State of the store for cluster purposes.
+ */
+class StoreStatus
+{
+ public:
+ typedef framing::Uuid Uuid;
+ typedef framing::cluster::StoreState StoreState;
+
+ StoreStatus(const std::string& dir);
+
+ framing::cluster::StoreState getState() const { return state; }
+
+ const Uuid& getClusterId() const { return clusterId; }
+ void setClusterId(const Uuid&);
+ const Uuid& getShutdownId() const { return shutdownId; }
+
+ void load();
+ void dirty(); // Mark the store in use.
+ void clean(const Uuid& shutdownId); // Mark the store clean.
+ bool hasStore() const;
+
+ private:
+ void save();
+
+ framing::cluster::StoreState state;
+ Uuid clusterId, shutdownId;
+ std::string dataDir;
+};
+
+const char* stateName(framing::cluster::StoreState);
+std::ostream& operator<<(std::ostream&, framing::cluster::StoreState);
+std::ostream& operator<<(std::ostream&, const StoreStatus&);
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_STORESTATE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
new file mode 100644
index 0000000000..a15c14ff48
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
@@ -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.
+ *
+ */
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/ClusterMap.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/Decoder.h"
+#include "qpid/cluster/ExpiryPolicy.h"
+#include "qpid/cluster/UpdateDataExchange.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/SessionImpl.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Future.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Fairshare.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/LinkRegistry.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/Link.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/SessionHandler.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/TxOpVisitor.h"
+#include "qpid/broker/DtxAck.h"
+#include "qpid/broker/TxAccept.h"
+#include "qpid/broker/TxPublish.h"
+#include "qpid/broker/RecoveredDequeue.h"
+#include "qpid/broker/RecoveredEnqueue.h"
+#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/ClusterConnectionMembershipBody.h"
+#include "qpid/framing/ClusterConnectionShadowReadyBody.h"
+#include "qpid/framing/ClusterConnectionSessionStateBody.h"
+#include "qpid/framing/ClusterConnectionConsumerStateBody.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/framing/TypeCode.h"
+#include "qpid/log/Statement.h"
+#include "qpid/types/Variant.h"
+#include "qpid/Url.h"
+#include "qmf/org/apache/qpid/broker/ManagementSetupState.h"
+#include <boost/bind.hpp>
+#include <boost/cast.hpp>
+#include <algorithm>
+#include <sstream>
+
+namespace qpid {
+namespace cluster {
+
+using amqp_0_10::ListCodec;
+using broker::Broker;
+using broker::Exchange;
+using broker::Queue;
+using broker::QueueBinding;
+using broker::Message;
+using broker::SemanticState;
+using types::Variant;
+
+using namespace framing;
+namespace arg=client::arg;
+using client::SessionBase_0_10Access;
+
+std::ostream& operator<<(std::ostream& o, const UpdateClient& c) {
+ return o << "cluster(" << c.updaterId << " UPDATER)";
+}
+
+struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection, public framing::FrameHandler
+{
+ boost::shared_ptr<qpid::client::ConnectionImpl> connection;
+
+ ClusterConnectionProxy(client::Connection c) :
+ AMQP_AllProxy::ClusterConnection(*static_cast<framing::FrameHandler*>(this)),
+ connection(client::ConnectionAccess::getImpl(c)) {}
+ ClusterConnectionProxy(client::AsyncSession s) :
+ AMQP_AllProxy::ClusterConnection(SessionBase_0_10Access(s).get()->out) {}
+
+ void handle(framing::AMQFrame& f)
+ {
+ assert(connection);
+ connection->expand(f.encodedSize(), false);
+ connection->handle(f);
+ }
+};
+
+// Create a connection with special version that marks it as a catch-up connection.
+client::Connection UpdateClient::catchUpConnection() {
+ client::Connection c;
+ client::ConnectionAccess::setVersion(c, ProtocolVersion(0x80 , 0x80 + 10));
+ return c;
+}
+
+// Send a control body directly to the session.
+void send(client::AsyncSession& s, const AMQBody& body) {
+ client::SessionBase_0_10Access sb(s);
+ sb.get()->send(body);
+}
+
+// TODO aconway 2008-09-24: optimization: update connections/sessions in parallel.
+
+UpdateClient::UpdateClient(const MemberId& updater, const MemberId& updatee, const Url& url,
+ broker::Broker& broker, const ClusterMap& m, ExpiryPolicy& expiry_,
+ const Cluster::ConnectionVector& cons, Decoder& decoder_,
+ const boost::function<void()>& ok,
+ const boost::function<void(const std::exception&)>& fail,
+ const client::ConnectionSettings& cs
+)
+ : updaterId(updater), updateeId(updatee), updateeUrl(url), updaterBroker(broker), map(m),
+ expiry(expiry_), connections(cons), decoder(decoder_),
+ connection(catchUpConnection()), shadowConnection(catchUpConnection()),
+ done(ok), failed(fail), connectionSettings(cs)
+{}
+
+UpdateClient::~UpdateClient() {}
+
+// Reserved exchange/queue name for catch-up, avoid clashes with user queues/exchanges.
+const std::string UpdateClient::UPDATE("qpid.cluster-update");
+
+void UpdateClient::run() {
+ try {
+ connection.open(updateeUrl, connectionSettings);
+ session = connection.newSession(UPDATE);
+ update();
+ done();
+ } catch (const std::exception& e) {
+ failed(e);
+ }
+ delete this;
+}
+
+void UpdateClient::update() {
+ QPID_LOG(debug, *this << " updating state to " << updateeId
+ << " at " << updateeUrl);
+ Broker& b = updaterBroker;
+
+ updateManagementSetupState();
+
+ b.getExchanges().eachExchange(boost::bind(&UpdateClient::updateExchange, this, _1));
+ b.getQueues().eachQueue(boost::bind(&UpdateClient::updateNonExclusiveQueue, this, _1));
+
+ // Update queue is used to transfer acquired messages that are no
+ // longer on their original queue.
+ session.queueDeclare(arg::queue=UPDATE, arg::autoDelete=true);
+ session.sync();
+ std::for_each(connections.begin(), connections.end(),
+ boost::bind(&UpdateClient::updateConnection, this, _1));
+ session.queueDelete(arg::queue=UPDATE);
+
+ // some Queue Observers need session state & msgs synced first, so sync observers now
+ b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueObservers, this, _1));
+
+ // Update queue listeners: must come after sessions so consumerNumbering is populated
+ b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueListeners, this, _1));
+
+ ClusterConnectionProxy(session).expiryId(expiry.getId());
+ updateLinks();
+ updateManagementAgent();
+
+ session.close();
+
+ ClusterConnectionMembershipBody membership;
+ map.toMethodBody(membership);
+ AMQFrame frame(membership);
+ client::ConnectionAccess::getImpl(connection)->expand(frame.encodedSize(), false);
+ client::ConnectionAccess::getImpl(connection)->handle(frame);
+
+ // NOTE: connection will be closed from the other end, don't close
+ // it here as that causes a race.
+
+ // TODO aconway 2010-03-15: This sleep avoids the race condition
+ // described in // https://bugzilla.redhat.com/show_bug.cgi?id=568831.
+ // It allows the connection to fully close before destroying the
+ // Connection object. Remove when the bug is fixed.
+ //
+ sys::usleep(10*1000);
+
+ QPID_LOG(debug, *this << " update completed to " << updateeId << " at " << updateeUrl);
+}
+
+namespace {
+template <class T> std::string encode(const T& t) {
+ std::string encoded;
+ encoded.resize(t.encodedSize());
+ framing::Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
+ t.encode(buf);
+ return encoded;
+}
+
+template <class T> std::string encode(const T& t, bool encodeKind) {
+ std::string encoded;
+ encoded.resize(t.encodedSize());
+ framing::Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
+ t.encode(buf, encodeKind);
+ return encoded;
+}
+} // namespace
+
+
+// Propagate the management state
+void UpdateClient::updateManagementSetupState()
+{
+ management::ManagementAgent* agent = updaterBroker.getManagementAgent();
+ if (!agent) return;
+
+ QPID_LOG(debug, *this << " updating management setup-state.");
+ std::string vendor, product, instance;
+ agent->getName(vendor, product, instance);
+ ClusterConnectionProxy(session).managementSetupState(
+ agent->getNextObjectId(), agent->getBootSequence(), agent->getUuid(),
+ vendor, product, instance);
+}
+
+void UpdateClient::updateManagementAgent()
+{
+ management::ManagementAgent* agent = updaterBroker.getManagementAgent();
+ if (!agent) return;
+ string data;
+
+ QPID_LOG(debug, *this << " updating management schemas. ")
+ agent->exportSchemas(data);
+ session.messageTransfer(
+ arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY),
+ arg::destination=UpdateDataExchange::EXCHANGE_NAME);
+
+ QPID_LOG(debug, *this << " updating management agents. ")
+ agent->exportAgents(data);
+ session.messageTransfer(
+ arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_AGENTS_KEY),
+ arg::destination=UpdateDataExchange::EXCHANGE_NAME);
+
+ QPID_LOG(debug, *this << " updating management deleted objects. ")
+ typedef management::ManagementAgent::DeletedObjectList DeletedObjectList;
+ DeletedObjectList deleted;
+ agent->exportDeletedObjects(deleted);
+ Variant::List list;
+ for (DeletedObjectList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+ string encoded;
+ (*i)->encode(encoded);
+ list.push_back(encoded);
+ }
+ ListCodec::encode(list, data);
+ session.messageTransfer(
+ arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY),
+ arg::destination=UpdateDataExchange::EXCHANGE_NAME);
+}
+
+void UpdateClient::updateExchange(const boost::shared_ptr<Exchange>& ex) {
+ QPID_LOG(debug, *this << " updating exchange " << ex->getName());
+ ClusterConnectionProxy(session).exchange(encode(*ex));
+}
+
+/** Bind a queue to the update exchange and update messges to it
+ * setting the message possition as needed.
+ */
+class MessageUpdater {
+ std::string queue;
+ bool haveLastPos;
+ framing::SequenceNumber lastPos;
+ client::AsyncSession session;
+ ExpiryPolicy& expiry;
+
+ public:
+
+ MessageUpdater(const string& q, const client::AsyncSession s, ExpiryPolicy& expiry_) : queue(q), haveLastPos(false), session(s), expiry(expiry_) {
+ session.exchangeBind(queue, UpdateClient::UPDATE);
+ }
+
+ ~MessageUpdater() {
+ try {
+ session.exchangeUnbind(queue, UpdateClient::UPDATE);
+ }
+ catch (const std::exception& e) {
+ // Don't throw in a destructor.
+ QPID_LOG(error, "Unbinding update queue " << queue << ": " << e.what());
+ }
+ }
+
+
+ void updateQueuedMessage(const broker::QueuedMessage& message) {
+ // Send the queue position if necessary.
+ if (!haveLastPos || message.position - lastPos != 1) {
+ ClusterConnectionProxy(session).queuePosition(queue, message.position.getValue()-1);
+ haveLastPos = true;
+ }
+ lastPos = message.position;
+
+ // Send the expiry ID if necessary.
+ if (message.payload->getProperties<DeliveryProperties>()->getTtl()) {
+ boost::optional<uint64_t> expiryId = expiry.getId(*message.payload);
+ ClusterConnectionProxy(session).expiryId(expiryId?*expiryId:0);
+ }
+
+ // We can't send a broker::Message via the normal client API,
+ // and it would be expensive to copy it into a client::Message
+ // so we go a bit under the client API covers here.
+ //
+ SessionBase_0_10Access sb(session);
+ // Disable client code that clears the delivery-properties.exchange
+ sb.get()->setDoClearDeliveryPropertiesExchange(false);
+ framing::MessageTransferBody transfer(
+ *message.payload->getFrames().as<framing::MessageTransferBody>());
+ transfer.setDestination(UpdateClient::UPDATE);
+
+ sb.get()->send(transfer, message.payload->getFrames(),
+ !message.payload->isContentReleased());
+ if (message.payload->isContentReleased()){
+ uint16_t maxFrameSize = sb.get()->getConnection()->getNegotiatedSettings().maxFrameSize;
+ uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
+ bool morecontent = true;
+ for (uint64_t offset = 0; morecontent; offset += maxContentSize)
+ {
+ AMQFrame frame((AMQContentBody()));
+ morecontent = message.payload->getContentFrame(*(message.queue), frame, maxContentSize, offset);
+ sb.get()->sendRawFrame(frame);
+ }
+ }
+ }
+
+ void updateMessage(const boost::intrusive_ptr<broker::Message>& message) {
+ updateQueuedMessage(broker::QueuedMessage(0, message, haveLastPos? lastPos.getValue()+1 : 1));
+ }
+};
+
+void UpdateClient::updateQueue(client::AsyncSession& s, const boost::shared_ptr<Queue>& q) {
+ broker::Exchange::shared_ptr alternateExchange = q->getAlternateExchange();
+ s.queueDeclare(
+ arg::queue = q->getName(),
+ arg::durable = q->isDurable(),
+ arg::autoDelete = q->isAutoDelete(),
+ arg::alternateExchange = alternateExchange ? alternateExchange->getName() : "",
+ arg::arguments = q->getSettings(),
+ arg::exclusive = q->hasExclusiveOwner()
+ );
+ MessageUpdater updater(q->getName(), s, expiry);
+ q->eachMessage(boost::bind(&MessageUpdater::updateQueuedMessage, &updater, _1));
+ q->eachBinding(boost::bind(&UpdateClient::updateBinding, this, s, q->getName(), _1));
+ ClusterConnectionProxy(s).queuePosition(q->getName(), q->getPosition());
+ uint priority, count;
+ if (qpid::broker::Fairshare::getState(q->getMessages(), priority, count)) {
+ ClusterConnectionProxy(s).queueFairshareState(q->getName(), priority, count);
+ }
+}
+
+void UpdateClient::updateExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) {
+ QPID_LOG(debug, *this << " updating exclusive queue " << q->getName() << " on " << shadowSession.getId());
+ updateQueue(shadowSession, q);
+}
+
+void UpdateClient::updateNonExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) {
+ if (!q->hasExclusiveOwner()) {
+ QPID_LOG(debug, *this << " updating queue " << q->getName());
+ updateQueue(session, q);
+ }//else queue will be updated as part of session state of owning session
+}
+
+void UpdateClient::updateBinding(client::AsyncSession& s, const std::string& queue, const QueueBinding& binding) {
+ s.exchangeBind(queue, binding.exchange, binding.key, binding.args);
+}
+
+void UpdateClient::updateOutputTask(const sys::OutputTask* task) {
+ const SemanticState::ConsumerImpl* cci =
+ boost::polymorphic_downcast<const SemanticState::ConsumerImpl*> (task);
+ SemanticState::ConsumerImpl* ci = const_cast<SemanticState::ConsumerImpl*>(cci);
+ uint16_t channel = ci->getParent().getSession().getChannel();
+ ClusterConnectionProxy(shadowConnection).outputTask(channel, ci->getName());
+ QPID_LOG(debug, *this << " updating output task " << ci->getName()
+ << " channel=" << channel);
+}
+
+void UpdateClient::updateConnection(const boost::intrusive_ptr<Connection>& updateConnection) {
+ QPID_LOG(debug, *this << " updating connection " << *updateConnection);
+ assert(updateConnection->getBrokerConnection());
+ broker::Connection& bc = *updateConnection->getBrokerConnection();
+
+ // Send the management ID first on the main connection.
+ std::string mgmtId = updateConnection->getBrokerConnection()->getMgmtId();
+ ClusterConnectionProxy(session).shadowPrepare(mgmtId);
+ // Make sure its received before opening shadow connection
+ session.sync();
+
+ // Open shadow connection and update it.
+ shadowConnection = catchUpConnection();
+
+ connectionSettings.maxFrameSize = bc.getFrameMax();
+ shadowConnection.open(updateeUrl, connectionSettings);
+ ClusterConnectionProxy(shadowConnection).shadowSetUser(bc.getUserId());
+
+ bc.eachSessionHandler(boost::bind(&UpdateClient::updateSession, this, _1));
+ // Safe to use decoder here because we are stalled for update.
+ std::pair<const char*, size_t> fragment = decoder.get(updateConnection->getId()).getFragment();
+ bc.getOutputTasks().eachOutput(
+ boost::bind(&UpdateClient::updateOutputTask, this, _1));
+ ClusterConnectionProxy(shadowConnection).shadowReady(
+ updateConnection->getId().getMember(),
+ updateConnection->getId().getNumber(),
+ bc.getMgmtId(),
+ bc.getUserId(),
+ string(fragment.first, fragment.second),
+ updateConnection->getOutput().getSendMax()
+ );
+ shadowConnection.close();
+ QPID_LOG(debug, *this << " updated connection " << *updateConnection);
+}
+
+void UpdateClient::updateSession(broker::SessionHandler& sh) {
+ broker::SessionState* ss = sh.getSession();
+ if (!ss) return; // no session.
+
+ QPID_LOG(debug, *this << " updating session " << ss->getId());
+
+ // Create a client session to update session state.
+ boost::shared_ptr<client::ConnectionImpl> cimpl = client::ConnectionAccess::getImpl(shadowConnection);
+ boost::shared_ptr<client::SessionImpl> simpl = cimpl->newSession(ss->getId().getName(), ss->getTimeout(), sh.getChannel());
+ simpl->disableAutoDetach();
+ client::SessionBase_0_10Access(shadowSession).set(simpl);
+ AMQP_AllProxy::ClusterConnection proxy(simpl->out);
+
+ // Re-create session state on remote connection.
+
+ QPID_LOG(debug, *this << " updating exclusive queues.");
+ ss->getSessionAdapter().eachExclusiveQueue(boost::bind(&UpdateClient::updateExclusiveQueue, this, _1));
+
+ QPID_LOG(debug, *this << " updating consumers.");
+ ss->getSemanticState().eachConsumer(
+ boost::bind(&UpdateClient::updateConsumer, this, _1));
+
+ QPID_LOG(debug, *this << " updating unacknowledged messages.");
+ broker::DeliveryRecords& drs = ss->getSemanticState().getUnacked();
+ std::for_each(drs.begin(), drs.end(),
+ boost::bind(&UpdateClient::updateUnacked, this, _1));
+
+ updateTxState(ss->getSemanticState()); // Tx transaction state.
+
+ // Adjust command counter for message in progress, will be sent after state update.
+ boost::intrusive_ptr<Message> inProgress = ss->getMessageInProgress();
+ SequenceNumber received = ss->receiverGetReceived().command;
+ if (inProgress)
+ --received;
+
+ // Sync the session to ensure all responses from broker have been processed.
+ shadowSession.sync();
+
+ // Reset command-sequence state.
+ proxy.sessionState(
+ ss->senderGetReplayPoint().command,
+ ss->senderGetCommandPoint().command,
+ ss->senderGetIncomplete(),
+ std::max(received, ss->receiverGetExpected().command),
+ received,
+ ss->receiverGetUnknownComplete(),
+ ss->receiverGetIncomplete()
+ );
+
+ // Send frames for partial message in progress.
+ if (inProgress) {
+ inProgress->getFrames().map(simpl->out);
+ }
+ QPID_LOG(debug, *this << " updated session " << sh.getSession()->getId());
+}
+
+void UpdateClient::updateConsumer(
+ const broker::SemanticState::ConsumerImpl::shared_ptr& ci)
+{
+ QPID_LOG(debug, *this << " updating consumer " << ci->getName() << " on "
+ << shadowSession.getId());
+
+ using namespace message;
+ shadowSession.messageSubscribe(
+ arg::queue = ci->getQueue()->getName(),
+ arg::destination = ci->getName(),
+ arg::acceptMode = ci->isAckExpected() ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE,
+ arg::acquireMode = ci->isAcquire() ? ACQUIRE_MODE_PRE_ACQUIRED : ACQUIRE_MODE_NOT_ACQUIRED,
+ arg::exclusive = ci->isExclusive(),
+ arg::resumeId = ci->getResumeId(),
+ arg::resumeTtl = ci->getResumeTtl(),
+ arg::arguments = ci->getArguments()
+ );
+ shadowSession.messageSetFlowMode(ci->getName(), ci->isWindowing() ? FLOW_MODE_WINDOW : FLOW_MODE_CREDIT);
+ shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_MESSAGE, ci->getMsgCredit());
+ shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_BYTE, ci->getByteCredit());
+ ClusterConnectionProxy(shadowSession).consumerState(
+ ci->getName(),
+ ci->isBlocked(),
+ ci->isNotifyEnabled(),
+ ci->position
+ );
+ consumerNumbering.add(ci.get());
+
+ QPID_LOG(debug, *this << " updated consumer " << ci->getName()
+ << " on " << shadowSession.getId());
+}
+
+void UpdateClient::updateUnacked(const broker::DeliveryRecord& dr) {
+ if (!dr.isEnded() && dr.isAcquired() && dr.getMessage().payload) {
+ // If the message is acquired then it is no longer on the
+ // updatees queue, put it on the update queue for updatee to pick up.
+ //
+ MessageUpdater(UPDATE, shadowSession, expiry).updateQueuedMessage(dr.getMessage());
+ }
+ ClusterConnectionProxy(shadowSession).deliveryRecord(
+ dr.getQueue()->getName(),
+ dr.getMessage().position,
+ dr.getTag(),
+ dr.getId(),
+ dr.isAcquired(),
+ dr.isAccepted(),
+ dr.isCancelled(),
+ dr.isComplete(),
+ dr.isEnded(),
+ dr.isWindowing(),
+ dr.getQueue()->isEnqueued(dr.getMessage()),
+ dr.getCredit()
+ );
+}
+
+class TxOpUpdater : public broker::TxOpConstVisitor, public MessageUpdater {
+ public:
+ TxOpUpdater(UpdateClient& dc, client::AsyncSession s, ExpiryPolicy& expiry)
+ : MessageUpdater(UpdateClient::UPDATE, s, expiry), parent(dc), session(s), proxy(s) {}
+
+ void operator()(const broker::DtxAck& ) {
+ throw InternalErrorException("DTX transactions not currently supported by cluster.");
+ }
+
+ void operator()(const broker::RecoveredDequeue& rdeq) {
+ updateMessage(rdeq.getMessage());
+ proxy.txEnqueue(rdeq.getQueue()->getName());
+ }
+
+ void operator()(const broker::RecoveredEnqueue& renq) {
+ updateMessage(renq.getMessage());
+ proxy.txEnqueue(renq.getQueue()->getName());
+ }
+
+ void operator()(const broker::TxAccept& txAccept) {
+ proxy.txAccept(txAccept.getAcked());
+ }
+
+ void operator()(const broker::TxPublish& txPub) {
+ updateMessage(txPub.getMessage());
+ typedef std::list<Queue::shared_ptr> QueueList;
+ const QueueList& qlist = txPub.getQueues();
+ Array qarray(TYPE_CODE_STR8);
+ for (QueueList::const_iterator i = qlist.begin(); i != qlist.end(); ++i)
+ qarray.push_back(Array::ValuePtr(new Str8Value((*i)->getName())));
+ proxy.txPublish(qarray, txPub.delivered);
+ }
+
+ private:
+ UpdateClient& parent;
+ client::AsyncSession session;
+ ClusterConnectionProxy proxy;
+};
+
+void UpdateClient::updateTxState(broker::SemanticState& s) {
+ QPID_LOG(debug, *this << " updating TX transaction state.");
+ ClusterConnectionProxy proxy(shadowSession);
+ proxy.accumulatedAck(s.getAccumulatedAck());
+ broker::TxBuffer::shared_ptr txBuffer = s.getTxBuffer();
+ if (txBuffer) {
+ proxy.txStart();
+ TxOpUpdater updater(*this, shadowSession, expiry);
+ txBuffer->accept(updater);
+ proxy.txEnd();
+ }
+}
+
+void UpdateClient::updateQueueListeners(const boost::shared_ptr<broker::Queue>& queue) {
+ queue->getListeners().eachListener(
+ boost::bind(&UpdateClient::updateQueueListener, this, queue->getName(), _1));
+}
+
+void UpdateClient::updateQueueListener(std::string& q,
+ const boost::shared_ptr<broker::Consumer>& c)
+{
+ SemanticState::ConsumerImpl* ci = dynamic_cast<SemanticState::ConsumerImpl*>(c.get());
+ size_t n = consumerNumbering[ci];
+ if (n >= consumerNumbering.size())
+ throw Exception(QPID_MSG("Unexpected listener on queue " << q));
+ ClusterConnectionProxy(session).addQueueListener(q, n);
+}
+
+void UpdateClient::updateLinks() {
+ broker::LinkRegistry& links = updaterBroker.getLinks();
+ links.eachLink(boost::bind(&UpdateClient::updateLink, this, _1));
+ links.eachBridge(boost::bind(&UpdateClient::updateBridge, this, _1));
+}
+
+void UpdateClient::updateLink(const boost::shared_ptr<broker::Link>& link) {
+ QPID_LOG(debug, *this << " updating link "
+ << link->getHost() << ":" << link->getPort());
+ ClusterConnectionProxy(session).config(encode(*link));
+}
+
+void UpdateClient::updateBridge(const boost::shared_ptr<broker::Bridge>& bridge) {
+ QPID_LOG(debug, *this << " updating bridge " << bridge->getName());
+ ClusterConnectionProxy(session).config(encode(*bridge));
+}
+
+void UpdateClient::updateQueueObservers(const boost::shared_ptr<broker::Queue>& q)
+{
+ q->eachObserver(boost::bind(&UpdateClient::updateObserver, this, q, _1));
+}
+
+void UpdateClient::updateObserver(const boost::shared_ptr<broker::Queue>& q,
+ boost::shared_ptr<broker::QueueObserver> o)
+{
+ qpid::framing::FieldTable state;
+ broker::StatefulQueueObserver *so = dynamic_cast<broker::StatefulQueueObserver *>(o.get());
+ if (so) {
+ so->getState( state );
+ std::string id(so->getId());
+ QPID_LOG(debug, *this << " updating queue " << q->getName() << "'s observer " << id);
+ ClusterConnectionProxy(session).queueObserverState( q->getName(), id, state );
+ }
+}
+
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.h b/qpid/cpp/src/qpid/cluster/UpdateClient.h
new file mode 100644
index 0000000000..b72d090d73
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateClient.h
@@ -0,0 +1,133 @@
+#ifndef QPID_CLUSTER_UPDATECLIENT_H
+#define QPID_CLUSTER_UPDATECLIENT_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/ClusterMap.h"
+#include "qpid/cluster/Numbering.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/sys/Runnable.h"
+#include <boost/shared_ptr.hpp>
+#include <iosfwd>
+
+namespace qpid {
+
+struct Url;
+
+namespace broker {
+
+class Broker;
+class Queue;
+class Exchange;
+class QueueBindings;
+struct QueueBinding;
+struct QueuedMessage;
+class SessionHandler;
+class DeliveryRecord;
+class SessionState;
+class SemanticState;
+class Decoder;
+class Link;
+class Bridge;
+class QueueObserver;
+
+} // namespace broker
+
+namespace cluster {
+
+class Cluster;
+class Connection;
+class ClusterMap;
+class Decoder;
+class ExpiryPolicy;
+
+/**
+ * A client that updates the contents of a local broker to a remote one using AMQP.
+ */
+class UpdateClient : public sys::Runnable {
+ public:
+ static const std::string UPDATE; // Name for special update queue and exchange.
+ static client::Connection catchUpConnection();
+
+ UpdateClient(const MemberId& updater, const MemberId& updatee, const Url&,
+ broker::Broker& donor, const ClusterMap& map, ExpiryPolicy& expiry,
+ const std::vector<boost::intrusive_ptr<Connection> >&, Decoder&,
+ const boost::function<void()>& done,
+ const boost::function<void(const std::exception&)>& fail,
+ const client::ConnectionSettings&
+ );
+
+ ~UpdateClient();
+ void update();
+ void run(); // Will delete this when finished.
+
+ void updateUnacked(const broker::DeliveryRecord&);
+
+ private:
+ void updateQueue(client::AsyncSession&, const boost::shared_ptr<broker::Queue>&);
+ void updateNonExclusiveQueue(const boost::shared_ptr<broker::Queue>&);
+ void updateExclusiveQueue(const boost::shared_ptr<broker::Queue>&);
+ void updateExchange(const boost::shared_ptr<broker::Exchange>&);
+ void updateMessage(const broker::QueuedMessage&);
+ void updateMessageTo(const broker::QueuedMessage&, const std::string& queue, client::Session s);
+ void updateBinding(client::AsyncSession&, const std::string& queue, const broker::QueueBinding& binding);
+ void updateConnection(const boost::intrusive_ptr<Connection>& connection);
+ void updateSession(broker::SessionHandler& s);
+ void updateTxState(broker::SemanticState& s);
+ void updateOutputTask(const sys::OutputTask* task);
+ void updateConsumer(const broker::SemanticState::ConsumerImpl::shared_ptr&);
+ void updateQueueListeners(const boost::shared_ptr<broker::Queue>&);
+ void updateQueueListener(std::string& q, const boost::shared_ptr<broker::Consumer>& c);
+ void updateManagementSetupState();
+ void updateManagementAgent();
+ void updateLinks();
+ void updateLink(const boost::shared_ptr<broker::Link>&);
+ void updateBridge(const boost::shared_ptr<broker::Bridge>&);
+ void updateQueueObservers(const boost::shared_ptr<broker::Queue>&);
+ void updateObserver(const boost::shared_ptr<broker::Queue>&, boost::shared_ptr<broker::QueueObserver>);
+
+
+ Numbering<broker::SemanticState::ConsumerImpl*> consumerNumbering;
+ MemberId updaterId;
+ MemberId updateeId;
+ Url updateeUrl;
+ broker::Broker& updaterBroker;
+ ClusterMap map;
+ ExpiryPolicy& expiry;
+ std::vector<boost::intrusive_ptr<Connection> > connections;
+ Decoder& decoder;
+ client::Connection connection, shadowConnection;
+ client::AsyncSession session, shadowSession;
+ boost::function<void()> done;
+ boost::function<void(const std::exception& e)> failed;
+ client::ConnectionSettings connectionSettings;
+
+ friend std::ostream& operator<<(std::ostream&, const UpdateClient&);
+};
+
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATECLIENT_H*/
diff --git a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp
new file mode 100644
index 0000000000..e5cd82e3d3
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp
@@ -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.
+ *
+ */
+#include "UpdateDataExchange.h"
+#include "Cluster.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace cluster {
+
+const std::string UpdateDataExchange::EXCHANGE_NAME("qpid.cluster-update-data");
+const std::string UpdateDataExchange::EXCHANGE_TYPE("qpid.cluster-update-data");
+const std::string UpdateDataExchange::MANAGEMENT_AGENTS_KEY("management-agents");
+const std::string UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY("management-schemas");
+const std::string UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY("management-deleted-objects");
+
+UpdateDataExchange::UpdateDataExchange(Cluster& cluster) :
+ Exchange(EXCHANGE_NAME, &cluster)
+{}
+
+void UpdateDataExchange::route(broker::Deliverable& msg, const std::string& routingKey,
+ const qpid::framing::FieldTable* )
+{
+ std::string data = msg.getMessage().getFrames().getContent();
+ if (routingKey == MANAGEMENT_AGENTS_KEY) managementAgents = data;
+ else if (routingKey == MANAGEMENT_SCHEMAS_KEY) managementSchemas = data;
+ else if (routingKey == MANAGEMENT_DELETED_OBJECTS_KEY) managementDeletedObjects = data;
+ else throw Exception(
+ QPID_MSG("Cluster update-data exchange received unknown routing-key: "
+ << routingKey));
+}
+
+void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agent) {
+ if (!agent) return;
+
+ framing::Buffer buf1(const_cast<char*>(managementAgents.data()), managementAgents.size());
+ agent->importAgents(buf1);
+
+ framing::Buffer buf2(const_cast<char*>(managementSchemas.data()), managementSchemas.size());
+ agent->importSchemas(buf2);
+
+ using amqp_0_10::ListCodec;
+ using types::Variant;
+ Variant::List encoded;
+ ListCodec::decode(managementDeletedObjects, encoded);
+ management::ManagementAgent::DeletedObjectList objects;
+ for (Variant::List::iterator i = encoded.begin(); i != encoded.end(); ++i) {
+ objects.push_back(management::ManagementAgent::DeletedObject::shared_ptr(
+ new management::ManagementAgent::DeletedObject(*i)));
+ }
+ agent->importDeletedObjects(objects);
+}
+
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h
new file mode 100644
index 0000000000..d2f6c35ad0
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h
@@ -0,0 +1,84 @@
+#ifndef QPID_CLUSTER_UPDATEDATAEXCHANGE_H
+#define QPID_CLUSTER_UPDATEDATAEXCHANGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/Exchange.h"
+#include "types.h"
+#include <iosfwd>
+
+namespace qpid {
+
+namespace management {
+class ManagementAgent;
+}
+
+namespace cluster {
+class Cluster;
+
+/**
+ * An exchange used to send data that is to large for a control
+ * during update. The routing key indicates the type of data.
+ */
+class UpdateDataExchange : public broker::Exchange
+{
+ public:
+ static const std::string EXCHANGE_NAME;
+ static const std::string EXCHANGE_TYPE;
+ static const std::string MANAGEMENT_AGENTS_KEY;
+ static const std::string MANAGEMENT_SCHEMAS_KEY;
+ static const std::string MANAGEMENT_DELETED_OBJECTS_KEY;
+
+ UpdateDataExchange(Cluster& parent);
+
+ void route(broker::Deliverable& msg, const std::string& routingKey,
+ const framing::FieldTable* args);
+
+ // Not implemented
+ std::string getType() const { return EXCHANGE_TYPE; }
+
+ bool bind(boost::shared_ptr<broker::Queue>,
+ const std::string&,
+ const qpid::framing::FieldTable*)
+ { return false; }
+
+ bool unbind(boost::shared_ptr<broker::Queue>,
+ const std::string&,
+ const qpid::framing::FieldTable*)
+ { return false; }
+
+ bool isBound(boost::shared_ptr<broker::Queue>,
+ const std::string*,
+ const qpid::framing::FieldTable*)
+ { return false; }
+
+ void updateManagementAgent(management::ManagementAgent* agent);
+
+ private:
+ std::string managementAgents;
+ std::string managementSchemas;
+ std::string managementDeletedObjects;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATEDATAEXCHANGE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/UpdateExchange.cpp b/qpid/cpp/src/qpid/cluster/UpdateExchange.cpp
new file mode 100644
index 0000000000..11937f296f
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateExchange.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/broker/Message.h"
+#include "UpdateExchange.h"
+
+namespace qpid {
+namespace cluster {
+
+using framing::MessageTransferBody;
+using framing::DeliveryProperties;
+
+UpdateExchange::UpdateExchange(management::Manageable* parent)
+ : broker::Exchange(UpdateClient::UPDATE, parent),
+ broker::FanOutExchange(UpdateClient::UPDATE, parent) {}
+
+
+void UpdateExchange::setProperties(const boost::intrusive_ptr<broker::Message>& msg) {
+ MessageTransferBody* transfer = msg->getMethod<MessageTransferBody>();
+ assert(transfer);
+ const DeliveryProperties* props = msg->getProperties<DeliveryProperties>();
+ assert(props);
+ if (props->hasExchange())
+ transfer->setDestination(props->getExchange());
+ else
+ transfer->clearDestinationFlag();
+}
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/UpdateExchange.h b/qpid/cpp/src/qpid/cluster/UpdateExchange.h
new file mode 100644
index 0000000000..9d7d9ee5fc
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateExchange.h
@@ -0,0 +1,45 @@
+#ifndef QPID_CLUSTER_UPDATEEXCHANGE_H
+#define QPID_CLUSTER_UPDATEEXCHANGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/broker/FanOutExchange.h"
+
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * A keyless exchange (like fanout exchange) that does not modify
+ * delivery-properties.exchange but copies it to the MessageTransfer.
+ */
+class UpdateExchange : public broker::FanOutExchange
+{
+ public:
+ UpdateExchange(management::Manageable* parent);
+ void setProperties(const boost::intrusive_ptr<broker::Message>&);
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATEEXCHANGE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/UpdateReceiver.h b/qpid/cpp/src/qpid/cluster/UpdateReceiver.h
new file mode 100644
index 0000000000..7e8ce47662
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/UpdateReceiver.h
@@ -0,0 +1,45 @@
+#ifndef QPID_CLUSTER_UPDATESTATE_H
+#define QPID_CLUSTER_UPDATESTATE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "Numbering.h"
+#include "qpid/broker/SemanticState.h"
+
+namespace qpid {
+namespace cluster {
+
+/**
+ * Cluster-wide state used when receiving an update.
+ */
+class UpdateReceiver {
+ public:
+ /** Numbering used to identify Queue listeners as consumers */
+ typedef Numbering<boost::shared_ptr<broker::SemanticState::ConsumerImpl> > ConsumerNumbering;
+ ConsumerNumbering consumerNumbering;
+
+ /** Management-id for the next shadow connection */
+ std::string nextShadowMgmtId;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATESTATE_H*/
diff --git a/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
new file mode 100644
index 0000000000..57ba5cf2fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/**@file
+
+ The watchdog plug-in will kill the qpidd broker process if it
+ becomes stuck for longer than a configured interval.
+
+ If the watchdog plugin is loaded and the --watchdog-interval=N
+ option is set then the broker starts a watchdog process and signals
+ it every N/2 seconds.
+
+ The watchdog process runs a very simple program that starts a timer
+ for N seconds, and resets the timer to N seconds whenever it is
+ signalled by the broker. If the timer ever reaches 0 the watchdog
+ kills the broker process (with kill -9) and exits.
+
+ This is useful in a cluster setting because in some insttances
+ (e.g. while resolving an error) it's possible for a stuck process
+ to hang other cluster members that are waiting for it to send a
+ message. Using the watchdog, the stuck process is terminated and
+ removed fromt the cluster allowing other members to continue and
+ clients of the stuck process to fail over to other members.
+
+*/
+#include "config.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/log/Statement.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Fork.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+namespace qpid {
+namespace cluster {
+
+using broker::Broker;
+
+struct Settings {
+ Settings() : interval(0) {}
+ int interval;
+};
+
+struct WatchDogOptions : public qpid::Options {
+ Settings& settings;
+
+ WatchDogOptions(Settings& s) : settings(s) {
+ addOptions()
+ ("watchdog-interval", optValue(settings.interval, "N"),
+ "broker is automatically killed if it is hung for more than \
+ N seconds. 0 disables watchdog.");
+ }
+};
+
+struct WatchDogTask : public sys::TimerTask {
+ int pid;
+ sys::Timer& timer;
+ int interval;
+
+ WatchDogTask(int pid_, sys::Timer& t, int _interval)
+ : TimerTask(_interval*sys::TIME_SEC/2,"WatchDog"), pid(pid_), timer(t), interval(_interval) {}
+
+ void fire() {
+ timer.add (new WatchDogTask(pid, timer, interval));
+ QPID_LOG(debug, "Sending keepalive signal to watchdog");
+ ::kill(pid, SIGUSR1);
+ }
+};
+
+struct WatchDogPlugin : public qpid::Plugin, public qpid::sys::Fork {
+ Settings settings;
+ WatchDogOptions options;
+ Broker* broker;
+ int watchdogPid;
+
+ WatchDogPlugin() : options(settings), broker(0), watchdogPid(0) {}
+
+ ~WatchDogPlugin() {
+ if (watchdogPid) ::kill(watchdogPid, SIGTERM);
+ ::waitpid(watchdogPid, 0, 0);
+ }
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(qpid::Plugin::Target& target) {
+ broker = dynamic_cast<Broker*>(&target);
+ if (broker && settings.interval) {
+ QPID_LOG(notice, "Starting watchdog process with interval of " <<
+ settings.interval << " seconds");
+ fork();
+ }
+ }
+
+ void initialize(Target&) {}
+
+ protected:
+
+ void child() { // Child of fork
+ const char* watchdog = ::getenv("QPID_WATCHDOG_EXEC"); // For use in tests
+ if (!watchdog) watchdog=QPID_LIBEXEC_DIR "/qpidd_watchdog";
+ std::string interval = boost::lexical_cast<std::string>(settings.interval);
+ ::execl(watchdog, watchdog, interval.c_str(), NULL);
+ QPID_LOG(critical, "Failed to exec watchdog program " << watchdog );
+ ::kill(::getppid(), SIGKILL);
+ exit(1);
+ }
+
+ void parent(int pid) { // Parent of fork
+ watchdogPid = pid;
+ broker->getTimer().add(
+ new WatchDogTask(watchdogPid, broker->getTimer(), settings.interval));
+ // TODO aconway 2009-08-10: to be extra safe, we could monitor
+ // the watchdog child and re-start it if it exits.
+ }
+};
+
+static WatchDogPlugin instance; // Static initialization.
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/management-schema.xml b/qpid/cpp/src/qpid/cluster/management-schema.xml
new file mode 100644
index 0000000000..a6292e9113
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/management-schema.xml
@@ -0,0 +1,61 @@
+<schema package="org.apache.qpid.cluster">
+
+ <!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+ <!-- Type information:
+
+Numeric types with "_wm" suffix are watermarked numbers. These are compound
+values containing a current value, and a low and high water mark for the reporting
+interval. The low and high water marks are set to the current value at the
+beginning of each interval and track the minimum and maximum values of the statistic
+over the interval respectively.
+
+Access rights for configuration elements:
+
+RO => Read Only
+RC => Read/Create, can be set at create time only, read-only thereafter
+RW => Read/Write
+
+If access rights are omitted for a property, they are assumed to be RO.
+
+ -->
+
+ <class name="Cluster">
+ <property name="brokerRef" type="objId" references="Broker" access="RC" index="y" parentRef="y"/>
+ <property name="clusterName" type="sstr" access="RC" desc="Name of cluster this server is a member of"/>
+ <property name="clusterID" type="sstr" access="RO" desc="Globally unique ID (UUID) for this cluster instance"/>
+ <property name="memberID" type="sstr" access="RO" desc="ID of this member of the cluster"/>
+ <property name="publishedURL" type="sstr" access="RC" desc="URL this node advertizes itself as"/>
+ <property name="clusterSize" type="uint16" access="RO" desc="Number of brokers currently in the cluster"/>
+ <property name="status" type="sstr" access="RO" desc="Cluster node status (STALLED,ACTIVE,JOINING)"/>
+ <property name="members" type="lstr" access="RO" desc="List of member URLs delimited by ';'"/>
+ <property name="memberIDs" type="lstr" access="RO" desc="List of member IDs delimited by ';'"/>
+
+ <method name="stopClusterNode">
+ <arg name="brokerId" type="sstr" dir="I"/>
+ </method>
+ <method name="stopFullCluster"/>
+
+ </class>
+
+
+
+</schema>
+
diff --git a/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
new file mode 100644
index 0000000000..51c5ed4b3f
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
@@ -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.
+ *
+ */
+
+/** @file helper executable for WatchDogPlugin.cpp */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+long timeout;
+
+void killParent(int) {
+ ::kill(getppid(), SIGKILL);
+ ::fprintf(stderr, "Watchdog killed unresponsive broker, pid=%d\n", ::getppid());
+ ::exit(1);
+}
+
+void resetTimer(int) {
+ struct ::itimerval itval = { { 0, 0 }, { timeout, 0 } };
+ if (::setitimer(ITIMER_REAL, &itval, 0) !=0) {
+ ::perror("Watchdog failed to set timer");
+ killParent(0);
+ ::exit(1);
+ }
+}
+
+/** Simple watchdog program: kill parent process if timeout
+ * expires without a SIGUSR1.
+ * Will be killed with SIGHUP when parent shuts down.
+ * Args: timeout in seconds.
+ */
+int main(int argc, char** argv) {
+ if(argc != 2 || (timeout = atoi(argv[1])) == 0) {
+ ::fprintf(stderr, "Usage: %s <timeout_seconds>\n", argv[0]);
+ ::exit(1);
+ }
+ ::signal(SIGUSR1, resetTimer);
+ ::signal(SIGALRM, killParent);
+ resetTimer(0);
+ while (true) { sleep(INT_MAX); }
+}
diff --git a/qpid/cpp/src/qpid/cluster/types.h b/qpid/cpp/src/qpid/cluster/types.h
new file mode 100644
index 0000000000..bfb4fd5b9e
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/types.h
@@ -0,0 +1,84 @@
+#ifndef QPID_CLUSTER_TYPES_H
+#define QPID_CLUSTER_TYPES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "config.h"
+#include "qpid/Url.h"
+#include "qpid/RefCounted.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/intrusive_ptr.hpp>
+#include <utility>
+#include <iosfwd>
+#include <string>
+
+extern "C" {
+#if defined (HAVE_OPENAIS_CPG_H)
+# include <openais/cpg.h>
+#elif defined (HAVE_COROSYNC_CPG_H)
+# include <corosync/cpg.h>
+#else
+# error "No cpg.h header file available"
+#endif
+}
+
+namespace qpid {
+namespace cluster {
+
+class Connection;
+typedef boost::intrusive_ptr<Connection> ConnectionPtr;
+
+/** Types of cluster event. */
+enum EventType { DATA, CONTROL };
+
+/** first=node-id, second=pid */
+struct MemberId : std::pair<uint32_t, uint32_t> {
+ MemberId(uint64_t n=0) : std::pair<uint32_t,uint32_t>( n >> 32, n & 0xffffffff) {}
+ MemberId(uint32_t node, uint32_t pid) : std::pair<uint32_t,uint32_t>(node, pid) {}
+ MemberId(const cpg_address& caddr) : std::pair<uint32_t,uint32_t>(caddr.nodeid, caddr.pid) {}
+ MemberId(const std::string&); // Decode from string.
+ uint32_t getNode() const { return first; }
+ uint32_t getPid() const { return second; }
+ operator uint64_t() const { return (uint64_t(first)<<32ull) + second; }
+
+ // MemberId as byte string, network byte order. Not human readable.
+ std::string str() const;
+};
+
+inline bool operator==(const cpg_address& caddr, const MemberId& id) { return id == MemberId(caddr); }
+
+std::ostream& operator<<(std::ostream&, const MemberId&);
+
+struct ConnectionId : public std::pair<MemberId, uint64_t> {
+ ConnectionId(const MemberId& m=MemberId(), uint64_t c=0) : std::pair<MemberId, uint64_t> (m,c) {}
+ ConnectionId(uint64_t m, uint64_t c) : std::pair<MemberId, uint64_t>(MemberId(m), c) {}
+ MemberId getMember() const { return first; }
+ uint64_t getNumber() const { return second; }
+};
+
+std::ostream& operator<<(std::ostream&, const ConnectionId&);
+
+std::ostream& operator<<(std::ostream&, EventType);
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_TYPES_H*/
diff --git a/qpid/cpp/src/qpid/console/Agent.cpp b/qpid/cpp/src/qpid/console/Agent.cpp
new file mode 100644
index 0000000000..fa76a13583
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Agent.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/Agent.h"
+
+std::ostream& qpid::console::operator<<(std::ostream& o, const Agent& agent)
+{
+ o << "Agent at bank " << agent.getBrokerBank() << "." << agent.getAgentBank() <<
+ " (" << agent.getLabel() << ")";
+ return o;
+}
+
diff --git a/qpid/cpp/src/qpid/console/Broker.cpp b/qpid/cpp/src/qpid/console/Broker.cpp
new file mode 100644
index 0000000000..86a17d4a10
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Broker.cpp
@@ -0,0 +1,333 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/console/Broker.h"
+#include "qpid/console/Object.h"
+#include "qpid/console/Value.h"
+#include "qpid/console/SessionManager.h"
+#include "qpid/console/ConsoleListener.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/SystemInfo.h"
+
+using namespace qpid::client;
+using namespace qpid::console;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace std;
+
+Broker::Broker(SessionManager& sm, ConnectionSettings& settings) :
+ sessionManager(sm), connected(false), connectionSettings(settings),
+ reqsOutstanding(1), syncInFlight(false), topicBound(false), methodObject(0),
+ connThreadBody(*this), connThread(connThreadBody)
+{
+ string osName;
+ string nodeName;
+ string release;
+ string version;
+ string machine;
+
+ sys::SystemInfo::getSystemId(osName, nodeName, release, version, machine);
+ uint32_t pid = sys::SystemInfo::getParentProcessId();
+
+ stringstream text;
+
+ text << "qmfc-cpp-" << nodeName << "-" << pid;
+ amqpSessionId = string(text.str());
+
+ QPID_LOG(debug, "Broker::Broker: constructed, amqpSessionId=" << amqpSessionId);
+}
+
+Broker::~Broker()
+{
+ connThreadBody.shutdown();
+ connThread.join();
+ resetAgents();
+ // resetAgents() does not delete the broker agent...
+ for (AgentMap::iterator iter = agents.begin(); iter != agents.end(); iter++) {
+ delete iter->second;
+ }
+}
+
+string Broker::getUrl() const
+{
+ stringstream url;
+ url << connectionSettings.host << ":" << connectionSettings.port;
+ return url.str();
+}
+
+void Broker::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) const
+{
+ buf.putOctet('A');
+ buf.putOctet('M');
+ buf.putOctet('2');
+ buf.putOctet(opcode);
+ buf.putLong (seq);
+}
+
+bool Broker::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) const
+{
+ if (buf.getSize() < 8)
+ return false;
+
+ uint8_t h1 = buf.getOctet();
+ uint8_t h2 = buf.getOctet();
+ uint8_t h3 = buf.getOctet();
+
+ *opcode = buf.getOctet();
+ *seq = buf.getLong();
+
+ return h1 == 'A' && h2 == 'M' && h3 == '2';
+}
+
+void Broker::received(qpid::client::Message& msg)
+{
+#define QMF_HEADER_SIZE 8
+ string data = msg.getData();
+ Buffer inBuffer(const_cast<char*>(data.c_str()), data.size());
+ uint8_t opcode;
+ uint32_t sequence;
+
+ while (inBuffer.available() >= QMF_HEADER_SIZE) {
+ if (checkHeader(inBuffer, &opcode, &sequence)) {
+ QPID_LOG(trace, "Broker::received: opcode=" << opcode << " seq=" << sequence);
+
+ if (opcode == 'b') sessionManager.handleBrokerResp(this, inBuffer, sequence);
+ else if (opcode == 'p') sessionManager.handlePackageInd(this, inBuffer, sequence);
+ else if (opcode == 'z') sessionManager.handleCommandComplete(this, inBuffer, sequence);
+ else if (opcode == 'q') sessionManager.handleClassInd(this, inBuffer, sequence);
+ else if (opcode == 'm') sessionManager.handleMethodResp(this, inBuffer, sequence);
+ else if (opcode == 'h') sessionManager.handleHeartbeatInd(this, inBuffer, sequence);
+ else if (opcode == 'e') sessionManager.handleEventInd(this, inBuffer, sequence);
+ else if (opcode == 's') sessionManager.handleSchemaResp(this, inBuffer, sequence);
+ else if (opcode == 'c') sessionManager.handleContentInd(this, inBuffer, sequence, true, false);
+ else if (opcode == 'i') sessionManager.handleContentInd(this, inBuffer, sequence, false, true);
+ else if (opcode == 'g') sessionManager.handleContentInd(this, inBuffer, sequence, true, true);
+ } else
+ return;
+ }
+}
+
+void Broker::resetAgents()
+{
+ for (AgentMap::iterator iter = agents.begin(); iter != agents.end(); iter++) {
+ if (sessionManager.listener != 0)
+ sessionManager.listener->delAgent(*(iter->second));
+ delete iter->second;
+ }
+
+ agents.clear();
+ agents[0x0000000100000000LL] = new Agent(this, 0, "BrokerAgent");
+}
+
+void Broker::updateAgent(const Object& object)
+{
+ uint32_t brokerBank = object.attrUint("brokerBank");
+ uint32_t agentBank = object.attrUint("agentBank");
+ uint64_t agentKey = ((uint64_t) brokerBank << 32) | (uint64_t) agentBank;
+ AgentMap::iterator iter = agents.find(agentKey);
+
+ if (object.isDeleted()) {
+ if (iter != agents.end()) {
+ if (sessionManager.listener != 0)
+ sessionManager.listener->delAgent(*(iter->second));
+ delete iter->second;
+ agents.erase(iter);
+ }
+ } else {
+ if (iter == agents.end()) {
+ Agent* agent = new Agent(this, agentBank, object.attrString("label"));
+ agents[agentKey] = agent;
+ if (sessionManager.listener != 0)
+ sessionManager.listener->newAgent(*agent);
+ }
+ }
+}
+
+void Broker::ConnectionThread::run()
+{
+ static const int delayMin(1);
+ static const int delayMax(128);
+ static const int delayFactor(2);
+ int delay(delayMin);
+ string dest("qmfc");
+
+ sessionId.generate();
+ queueName << "qmfc-" << sessionId;
+
+ while (true) {
+ try {
+ broker.topicBound = false;
+ broker.reqsOutstanding = 1;
+ connection.open(broker.connectionSettings);
+ session = connection.newSession(queueName.str());
+ subscriptions = new client::SubscriptionManager(session);
+
+ session.queueDeclare(arg::queue=queueName.str(), arg::autoDelete=true,
+ arg::exclusive=true);
+ session.exchangeBind(arg::exchange="amq.direct", arg::queue=queueName.str(),
+ arg::bindingKey=queueName.str());
+
+ subscriptions->setAcceptMode(ACCEPT_MODE_NONE);
+ subscriptions->setAcquireMode(ACQUIRE_MODE_PRE_ACQUIRED);
+ subscriptions->subscribe(broker, queueName.str(), dest);
+ subscriptions->setFlowControl(dest, FlowControl::unlimited());
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (shuttingDown)
+ return;
+ operational = true;
+ broker.resetAgents();
+ broker.connected = true;
+ broker.sessionManager.handleBrokerConnect(&broker);
+ broker.sessionManager.startProtocol(&broker);
+ try {
+ Mutex::ScopedUnlock _unlock(connLock);
+ subscriptions->run();
+ } catch (std::exception) {}
+
+ operational = false;
+ broker.connected = false;
+ broker.sessionManager.handleBrokerDisconnect(&broker);
+ }
+ delay = delayMin;
+ connection.close();
+ delete subscriptions;
+ subscriptions = 0;
+ } catch (std::exception &e) {
+ QPID_LOG(debug, " outer exception: " << e.what());
+ if (delay < delayMax)
+ delay *= delayFactor;
+ }
+
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (shuttingDown)
+ return;
+ {
+ Mutex::ScopedUnlock _unlock(connLock);
+ ::sleep(delay);
+ }
+ if (shuttingDown)
+ return;
+ }
+ }
+}
+
+Broker::ConnectionThread::~ConnectionThread()
+{
+ if (subscriptions != 0) {
+ delete subscriptions;
+ }
+}
+
+void Broker::ConnectionThread::sendBuffer(Buffer& buf, uint32_t length,
+ const string& exchange, const string& routingKey)
+{
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (!operational)
+ return;
+ }
+
+ client::Message msg;
+ string data;
+
+ buf.getRawData(data, length);
+ msg.getDeliveryProperties().setRoutingKey(routingKey);
+ msg.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str()));
+ msg.setData(data);
+ try {
+ session.messageTransfer(arg::content=msg, arg::destination=exchange);
+ } catch(std::exception&) {}
+}
+
+void Broker::ConnectionThread::bindExchange(const std::string& exchange, const std::string& key)
+{
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (!operational)
+ return;
+ }
+
+ QPID_LOG(debug, "Broker::ConnectionThread::bindExchange: exchange=" << exchange << " key=" << key);
+ session.exchangeBind(arg::exchange=exchange, arg::queue=queueName.str(),
+ arg::bindingKey=key);
+}
+
+void Broker::ConnectionThread::shutdown()
+{
+ {
+ Mutex::ScopedLock _lock(connLock);
+ shuttingDown = true;
+ }
+ if (subscriptions)
+ subscriptions->stop();
+}
+
+void Broker::waitForStable()
+{
+ Mutex::ScopedLock l(lock);
+ if (reqsOutstanding == 0)
+ return;
+ syncInFlight = true;
+ while (reqsOutstanding != 0) {
+ bool result = cond.wait(lock, AbsTime(now(), TIME_SEC * sessionManager.settings.getTimeout));
+ if (!result)
+ throw(Exception("Timed out waiting for broker to synchronize"));
+ }
+}
+
+void Broker::incOutstanding()
+{
+ Mutex::ScopedLock l(lock);
+ reqsOutstanding++;
+}
+
+void Broker::decOutstanding()
+{
+ Mutex::ScopedLock l(lock);
+ reqsOutstanding--;
+ if (reqsOutstanding == 0) {
+ if (!topicBound) {
+ topicBound = true;
+ for (vector<string>::const_iterator iter = sessionManager.bindingKeyList.begin();
+ iter != sessionManager.bindingKeyList.end(); iter++)
+ connThreadBody.bindExchange("qpid.management", *iter);
+ }
+ if (syncInFlight) {
+ syncInFlight = false;
+ cond.notify();
+ }
+ }
+}
+
+void Broker::appendAgents(Agent::Vector& agentlist) const
+{
+ for (AgentMap::const_iterator iter = agents.begin(); iter != agents.end(); iter++) {
+ agentlist.push_back(iter->second);
+ }
+}
+
+ostream& qpid::console::operator<<(ostream& o, const Broker& k)
+{
+ o << "Broker: " << k.connectionSettings.host << ":" << k.connectionSettings.port;
+ return o;
+}
diff --git a/qpid/cpp/src/qpid/console/ClassKey.cpp b/qpid/cpp/src/qpid/console/ClassKey.cpp
new file mode 100644
index 0000000000..7a16113bae
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/ClassKey.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/ClassKey.h"
+#include <string.h>
+#include <cstdio>
+
+using namespace std;
+using namespace qpid::console;
+
+ClassKey::ClassKey(const string& _package, const string& _name, const uint8_t* _hash) :
+ package(_package), name(_name)
+{
+ ::memcpy(hash, _hash, HASH_SIZE);
+}
+
+string ClassKey::getHashString() const
+{
+ char cstr[36];
+ ::sprintf(cstr, "%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x%02x%02x",
+ hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
+ hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
+ return string(cstr);
+}
+
+string ClassKey::str() const
+{
+ string result(package + ":" + name + "(" + getHashString() + ")");
+ return result;
+}
+
+bool ClassKey::operator==(const ClassKey& other) const
+{
+ return ::memcmp(hash, other.hash, HASH_SIZE) == 0 &&
+ name == other.name &&
+ package == other.package;
+}
+
+bool ClassKey::operator!=(const ClassKey& other) const
+{
+ return !(*this == other);
+}
+
+bool ClassKey::operator<(const ClassKey& other) const
+{
+ int cmp = ::memcmp(hash, other.hash, HASH_SIZE);
+ if (cmp != 0)
+ return cmp < 0;
+ cmp = name.compare(other.name);
+ if (cmp != 0)
+ return cmp < 0;
+ return package < other.package;
+}
+
+bool ClassKey::operator>(const ClassKey& other) const
+{
+ int cmp = ::memcmp(hash, other.hash, HASH_SIZE);
+ if (cmp != 0)
+ return cmp > 0;
+ cmp = name.compare(other.name);
+ if (cmp != 0)
+ return cmp > 0;
+ return package > other.package;
+}
+
+bool ClassKey::operator<=(const ClassKey& other) const
+{
+ return !(*this > other);
+}
+
+bool ClassKey::operator>=(const ClassKey& other) const
+{
+ return !(*this < other);
+}
+
+void ClassKey::encode(qpid::framing::Buffer& buffer) const
+{
+ buffer.putShortString(package);
+ buffer.putShortString(name);
+ buffer.putBin128(const_cast<uint8_t*>(hash));
+}
+
+ostream& qpid::console::operator<<(ostream& o, const ClassKey& k)
+{
+ o << k.str();
+ return o;
+}
diff --git a/qpid/cpp/src/qpid/console/Event.cpp b/qpid/cpp/src/qpid/console/Event.cpp
new file mode 100644
index 0000000000..3e14804b35
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Event.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/Broker.h"
+#include "qpid/console/ClassKey.h"
+#include "qpid/console/Schema.h"
+#include "qpid/console/Event.h"
+#include "qpid/console/Value.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/Buffer.h"
+
+using namespace qpid::console;
+using namespace std;
+using qpid::framing::Uuid;
+using qpid::framing::FieldTable;
+
+Event::Event(Broker* _broker, SchemaClass* _schema, qpid::framing::Buffer& buffer) :
+ broker(_broker), schema(_schema)
+{
+ timestamp = buffer.getLongLong();
+ severity = (Severity) buffer.getOctet();
+ for (vector<SchemaArgument*>::const_iterator aIter = schema->arguments.begin();
+ aIter != schema->arguments.end(); aIter++) {
+ SchemaArgument* argument = *aIter;
+ attributes[argument->name] = argument->decodeValue(buffer);
+ }
+}
+
+const ClassKey& Event::getClassKey() const
+{
+ return schema->getClassKey();
+}
+
+string Event::getSeverityString() const
+{
+ switch (severity) {
+ case SEV_EMERGENCY : return string("EMER");
+ case SEV_ALERT : return string("ALERT");
+ case SEV_CRITICAL : return string("CRIT");
+ case SEV_ERROR : return string("ERROR");
+ case SEV_WARNING : return string("WARN");
+ case SEV_NOTICE : return string("NOTIC");
+ case SEV_INFO : return string("INFO");
+ case SEV_DEBUG : return string("DEBUG");
+ }
+ return string("<UNKNOWN>");
+}
+
+ObjectId Event::attrRef(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return ObjectId();
+ Value::Ptr val = iter->second;
+ if (!val->isObjectId())
+ return ObjectId();
+ return val->asObjectId();
+}
+
+uint32_t Event::attrUint(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isUint())
+ return 0;
+ return val->asUint();
+}
+
+int32_t Event::attrInt(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isInt())
+ return 0;
+ return val->asInt();
+}
+
+uint64_t Event::attrUint64(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isUint64())
+ return 0;
+ return val->asUint64();
+}
+
+int64_t Event::attrInt64(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isInt64())
+ return 0;
+ return val->asInt64();
+}
+
+string Event::attrString(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return string();
+ Value::Ptr val = iter->second;
+ if (!val->isString())
+ return string();
+ return val->asString();
+}
+
+bool Event::attrBool(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return false;
+ Value::Ptr val = iter->second;
+ if (!val->isBool())
+ return false;
+ return val->asBool();
+}
+
+float Event::attrFloat(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0.0;
+ Value::Ptr val = iter->second;
+ if (!val->isFloat())
+ return 0.0;
+ return val->asFloat();
+}
+
+double Event::attrDouble(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0.0;
+ Value::Ptr val = iter->second;
+ if (!val->isDouble())
+ return 0.0;
+ return val->asDouble();
+}
+
+Uuid Event::attrUuid(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return Uuid();
+ Value::Ptr val = iter->second;
+ if (!val->isUuid())
+ return Uuid();
+ return val->asUuid();
+}
+
+FieldTable Event::attrMap(const string& key) const
+{
+ Object::AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return FieldTable();
+ Value::Ptr val = iter->second;
+ if (!val->isMap())
+ return FieldTable();
+ return val->asMap();
+}
+
+
+std::ostream& qpid::console::operator<<(std::ostream& o, const Event& event)
+{
+ const ClassKey& key = event.getClassKey();
+ sys::AbsTime aTime(sys::AbsTime(), sys::Duration(event.getTimestamp()));
+ o << aTime << " " << event.getSeverityString() << " " <<
+ key.getPackageName() << ":" << key.getClassName() <<
+ " broker=" << event.getBroker()->getUrl();
+
+ const Object::AttributeMap& attributes = event.getAttributes();
+ for (Object::AttributeMap::const_iterator iter = attributes.begin();
+ iter != attributes.end(); iter++) {
+ o << " " << iter->first << "=" << iter->second->str();
+ }
+ return o;
+}
+
+
diff --git a/qpid/cpp/src/qpid/console/Object.cpp b/qpid/cpp/src/qpid/console/Object.cpp
new file mode 100644
index 0000000000..6570e293ab
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Object.cpp
@@ -0,0 +1,384 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/console/SessionManager.h"
+#include "qpid/console/Broker.h"
+#include "qpid/console/Object.h"
+#include "qpid/console/Schema.h"
+#include "qpid/console/ClassKey.h"
+#include "qpid/console/Value.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/sys/Mutex.h"
+
+using namespace qpid::console;
+using namespace qpid::sys;
+using namespace qpid;
+using namespace std;
+using qpid::framing::Uuid;
+using qpid::framing::FieldTable;
+
+void Object::AttributeMap::addRef(const string& key, const ObjectId& val)
+{
+ (*this)[key] = Value::Ptr(new RefValue(val));
+}
+
+void Object::AttributeMap::addUint(const string& key, uint32_t val)
+{
+ (*this)[key] = Value::Ptr(new UintValue(val));
+}
+
+void Object::AttributeMap::addInt(const string& key, int32_t val)
+{
+ (*this)[key] = Value::Ptr(new IntValue(val));
+}
+
+void Object::AttributeMap::addUint64(const string& key, uint64_t val)
+{
+ (*this)[key] = Value::Ptr(new Uint64Value(val));
+}
+
+void Object::AttributeMap::addInt64(const string& key, int64_t val)
+{
+ (*this)[key] = Value::Ptr(new Int64Value(val));
+}
+
+void Object::AttributeMap::addString(const string& key, const string& val)
+{
+ (*this)[key] = Value::Ptr(new StringValue(val));
+}
+
+void Object::AttributeMap::addBool(const string& key, bool val)
+{
+ (*this)[key] = Value::Ptr(new BoolValue(val));
+}
+
+void Object::AttributeMap::addFloat(const string& key, float val)
+{
+ (*this)[key] = Value::Ptr(new FloatValue(val));
+}
+
+void Object::AttributeMap::addDouble(const string& key, double val)
+{
+ (*this)[key] = Value::Ptr(new DoubleValue(val));
+}
+
+void Object::AttributeMap::addUuid(const string& key, const Uuid& val)
+{
+ (*this)[key] = Value::Ptr(new UuidValue(val));
+}
+
+void Object::AttributeMap::addMap(const string& key, const FieldTable& val)
+{
+ (*this)[key] = Value::Ptr(new MapValue(val));
+}
+
+Object::Object(Broker* b, SchemaClass* s, framing::Buffer& buffer, bool prop, bool stat) :
+ broker(b), schema(s), pendingMethod(0)
+{
+ currentTime = buffer.getLongLong();
+ createTime = buffer.getLongLong();
+ deleteTime = buffer.getLongLong();
+ objectId.decode(buffer);
+
+ if (prop) {
+ set<string> excludes;
+ parsePresenceMasks(buffer, excludes);
+ for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin();
+ pIter != schema->properties.end(); pIter++) {
+ SchemaProperty* property = *pIter;
+ if (excludes.count(property->name) != 0) {
+ attributes[property->name] = Value::Ptr(new NullValue());
+ } else {
+ attributes[property->name] = property->decodeValue(buffer);
+ }
+ }
+ }
+
+ if (stat) {
+ for (vector<SchemaStatistic*>::const_iterator sIter = schema->statistics.begin();
+ sIter != schema->statistics.end(); sIter++) {
+ SchemaStatistic* statistic = *sIter;
+ attributes[statistic->name] = statistic->decodeValue(buffer);
+ }
+ }
+}
+
+Object::~Object() {}
+
+const ClassKey& Object::getClassKey() const
+{
+ return schema->getClassKey();
+}
+
+string Object::getIndex() const
+{
+ string result;
+
+ for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin();
+ pIter != schema->properties.end(); pIter++) {
+ SchemaProperty* property = *pIter;
+ if (property->isIndex) {
+ AttributeMap::const_iterator vIter = attributes.find(property->name);
+ if (vIter != attributes.end()) {
+ if (!result.empty())
+ result += ":";
+ result += vIter->second->str();
+ }
+ }
+ }
+ return result;
+}
+
+void Object::mergeUpdate(const Object& /*updated*/)
+{
+ // TODO
+}
+
+void Object::invokeMethod(const string name, const AttributeMap& args, MethodResponse& result)
+{
+ for (vector<SchemaMethod*>::const_iterator iter = schema->methods.begin();
+ iter != schema->methods.end(); iter++) {
+ if ((*iter)->name == name) {
+ SchemaMethod* method = *iter;
+ char rawbuffer[65536];
+ framing::Buffer buffer(rawbuffer, 65536);
+ uint32_t sequence = broker->sessionManager.sequenceManager.reserve("method");
+ pendingMethod = method;
+ broker->methodObject = this;
+ broker->encodeHeader(buffer, 'M', sequence);
+ objectId.encode(buffer);
+ schema->key.encode(buffer);
+ buffer.putShortString(name);
+
+ for (vector<SchemaArgument*>::const_iterator aIter = method->arguments.begin();
+ aIter != method->arguments.end(); aIter++) {
+ SchemaArgument* arg = *aIter;
+ if (arg->dirInput) {
+ AttributeMap::const_iterator attr = args.find(arg->name);
+ if (attr != args.end()) {
+ ValueFactory::encodeValue(arg->typeCode, attr->second, buffer);
+ } else {
+ // TODO Use the default value instead of throwing
+ throw Exception("Missing arguments in method call");
+ }
+ }
+ }
+
+ uint32_t length = buffer.getPosition();
+ buffer.reset();
+ stringstream routingKey;
+ routingKey << "agent." << objectId.getBrokerBank() << "." << objectId.getAgentBank();
+ broker->connThreadBody.sendBuffer(buffer, length, "qpid.management", routingKey.str());
+
+ {
+ Mutex::ScopedLock l(broker->lock);
+ bool ok = true;
+ while (pendingMethod != 0 && ok) {
+ ok = broker->cond.wait(broker->lock, AbsTime(now(), broker->sessionManager.settings.methodTimeout * TIME_SEC));
+ }
+
+ if (!ok) {
+ result.code = 0x1001;
+ result.text.assign("Method call timed out");
+ result.arguments.clear();
+ } else {
+ result = methodResponse;
+ }
+ }
+ }
+ }
+}
+
+void Object::handleMethodResp(framing::Buffer& buffer, uint32_t sequence)
+{
+ broker->sessionManager.sequenceManager.release(sequence);
+ methodResponse.code = buffer.getLong();
+ buffer.getMediumString(methodResponse.text);
+ methodResponse.arguments.clear();
+
+ for (vector<SchemaArgument*>::const_iterator aIter = pendingMethod->arguments.begin();
+ aIter != pendingMethod->arguments.end(); aIter++) {
+ SchemaArgument* arg = *aIter;
+ if (arg->dirOutput) {
+ methodResponse.arguments[arg->name] = arg->decodeValue(buffer);
+ }
+ }
+
+ {
+ Mutex::ScopedLock l(broker->lock);
+ pendingMethod = 0;
+ broker->cond.notify();
+ }
+}
+
+ObjectId Object::attrRef(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return ObjectId();
+ Value::Ptr val = iter->second;
+ if (!val->isObjectId())
+ return ObjectId();
+ return val->asObjectId();
+}
+
+uint32_t Object::attrUint(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isUint())
+ return 0;
+ return val->asUint();
+}
+
+int32_t Object::attrInt(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isInt())
+ return 0;
+ return val->asInt();
+}
+
+uint64_t Object::attrUint64(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isUint64())
+ return 0;
+ return val->asUint64();
+}
+
+int64_t Object::attrInt64(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0;
+ Value::Ptr val = iter->second;
+ if (!val->isInt64())
+ return 0;
+ return val->asInt64();
+}
+
+string Object::attrString(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return string();
+ Value::Ptr val = iter->second;
+ if (!val->isString())
+ return string();
+ return val->asString();
+}
+
+bool Object::attrBool(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return false;
+ Value::Ptr val = iter->second;
+ if (!val->isBool())
+ return false;
+ return val->asBool();
+}
+
+float Object::attrFloat(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0.0;
+ Value::Ptr val = iter->second;
+ if (!val->isFloat())
+ return 0.0;
+ return val->asFloat();
+}
+
+double Object::attrDouble(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return 0.0;
+ Value::Ptr val = iter->second;
+ if (!val->isDouble())
+ return 0.0;
+ return val->asDouble();
+}
+
+Uuid Object::attrUuid(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return Uuid();
+ Value::Ptr val = iter->second;
+ if (!val->isUuid())
+ return Uuid();
+ return val->asUuid();
+}
+
+FieldTable Object::attrMap(const string& key) const
+{
+ AttributeMap::const_iterator iter = attributes.find(key);
+ if (iter == attributes.end())
+ return FieldTable();
+ Value::Ptr val = iter->second;
+ if (!val->isMap())
+ return FieldTable();
+ return val->asMap();
+}
+
+void Object::parsePresenceMasks(framing::Buffer& buffer, set<string>& excludeList)
+{
+ excludeList.clear();
+ uint8_t bit = 0;
+ uint8_t mask = 0;
+
+ for (vector<SchemaProperty*>::const_iterator pIter = schema->properties.begin();
+ pIter != schema->properties.end(); pIter++) {
+ SchemaProperty* property = *pIter;
+ if (property->isOptional) {
+ if (bit == 0) {
+ mask = buffer.getOctet();
+ bit = 1;
+ }
+ if ((mask & bit) == 0)
+ excludeList.insert(property->name);
+ if (bit == 0x80)
+ bit = 0;
+ else
+ bit = bit << 1;
+ }
+ }
+}
+
+ostream& qpid::console::operator<<(ostream& o, const Object& object)
+{
+ const ClassKey& key = object.getClassKey();
+ o << key.getPackageName() << ":" << key.getClassName() << "[" << object.getObjectId() << "] " <<
+ object.getIndex();
+ return o;
+}
+
diff --git a/qpid/cpp/src/qpid/console/ObjectId.cpp b/qpid/cpp/src/qpid/console/ObjectId.cpp
new file mode 100644
index 0000000000..fbaad20d57
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/ObjectId.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/ObjectId.h"
+#include "qpid/framing/Buffer.h"
+
+using namespace qpid::console;
+using namespace qpid;
+using namespace std;
+
+ObjectId::ObjectId(framing::Buffer& buffer)
+{
+ decode(buffer);
+}
+
+void ObjectId::decode(framing::Buffer& buffer)
+{
+ first = buffer.getLongLong();
+ second = buffer.getLongLong();
+}
+
+void ObjectId::encode(framing::Buffer& buffer)
+{
+ buffer.putLongLong(first);
+ buffer.putLongLong(second);
+}
+
+bool ObjectId::operator==(const ObjectId& other) const
+{
+ return second == other.second && first == other.first;
+}
+
+bool ObjectId::operator!=(const ObjectId& other) const
+{
+ return !(*this == other);
+}
+
+bool ObjectId::operator<(const ObjectId& other) const
+{
+ if (first < other.first)
+ return true;
+ if (first > other.first)
+ return false;
+ return second < other.second;
+}
+
+bool ObjectId::operator>(const ObjectId& other) const
+{
+ if (first > other.first)
+ return true;
+ if (first < other.first)
+ return false;
+ return second > other.second;
+}
+
+bool ObjectId::operator<=(const ObjectId& other) const
+{
+ return !(*this > other);
+}
+
+bool ObjectId::operator>=(const ObjectId& other) const
+{
+ return !(*this < other);
+}
+
+ostream& qpid::console::operator<<(ostream& o, const ObjectId& id)
+{
+ o << (int) id.getFlags() << "-" << id.getSequence() << "-" << id.getBrokerBank() << "-" <<
+ id.getAgentBank() << "-" << id.getObject();
+ return o;
+}
+
+
diff --git a/qpid/cpp/src/qpid/console/Package.cpp b/qpid/cpp/src/qpid/console/Package.cpp
new file mode 100644
index 0000000000..e5d6fa29fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Package.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/Package.h"
+
+using namespace qpid::console;
+
+SchemaClass* Package::getClass(const std::string& className, uint8_t* hash)
+{
+ NameHash key(className, hash);
+ ClassMap::iterator iter = classes.find(key);
+ if (iter != classes.end())
+ return iter->second;
+ return 0;
+}
+
+void Package::addClass(const std::string& className, uint8_t* hash, SchemaClass* schemaClass)
+{
+ NameHash key(className, hash);
+ ClassMap::iterator iter = classes.find(key);
+ if (iter == classes.end())
+ classes[key] = schemaClass;
+}
diff --git a/qpid/cpp/src/qpid/console/Schema.cpp b/qpid/cpp/src/qpid/console/Schema.cpp
new file mode 100644
index 0000000000..a3dbd91201
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Schema.cpp
@@ -0,0 +1,165 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/console/Schema.h"
+#include "qpid/console/Value.h"
+#include "qpid/framing/FieldTable.h"
+
+using namespace qpid::console;
+using namespace qpid;
+using std::string;
+using std::vector;
+
+SchemaArgument::SchemaArgument(framing::Buffer& buffer, bool forMethod)
+{
+ framing::FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typeCode = map.getAsInt("type");
+ unit = map.getAsString("unit");
+ min = map.getAsInt("min");
+ max = map.getAsInt("max");
+ maxLen = map.getAsInt("maxlen");
+ desc = map.getAsString("desc");
+
+ dirInput = false;
+ dirOutput = false;
+ if (forMethod) {
+ string dir(map.getAsString("dir"));
+ if (dir.find('I') != dir.npos || dir.find('i') != dir.npos)
+ dirInput = true;
+ if (dir.find('O') != dir.npos || dir.find('o') != dir.npos)
+ dirOutput = true;
+ }
+}
+
+Value::Ptr SchemaArgument::decodeValue(framing::Buffer& buffer)
+{
+ return ValueFactory::newValue(typeCode, buffer);
+}
+
+SchemaProperty::SchemaProperty(framing::Buffer& buffer)
+{
+ framing::FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typeCode = map.getAsInt("type");
+ accessCode = map.getAsInt("access");
+ isIndex = map.getAsInt("index") != 0;
+ isOptional = map.getAsInt("optional") != 0;
+ unit = map.getAsString("unit");
+ min = map.getAsInt("min");
+ max = map.getAsInt("max");
+ maxLen = map.getAsInt("maxlen");
+ desc = map.getAsString("desc");
+}
+
+Value::Ptr SchemaProperty::decodeValue(framing::Buffer& buffer)
+{
+ return ValueFactory::newValue(typeCode, buffer);
+}
+
+SchemaStatistic::SchemaStatistic(framing::Buffer& buffer)
+{
+ framing::FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ typeCode = map.getAsInt("type");
+ unit = map.getAsString("unit");
+ desc = map.getAsString("desc");
+}
+
+Value::Ptr SchemaStatistic::decodeValue(framing::Buffer& buffer)
+{
+ return ValueFactory::newValue(typeCode, buffer);
+}
+
+SchemaMethod::SchemaMethod(framing::Buffer& buffer)
+{
+ framing::FieldTable map;
+ map.decode(buffer);
+
+ name = map.getAsString("name");
+ desc = map.getAsString("desc");
+ int argCount = map.getAsInt("argCount");
+
+ for (int i = 0; i < argCount; i++)
+ arguments.push_back(new SchemaArgument(buffer, true));
+}
+
+SchemaMethod::~SchemaMethod()
+{
+ for (vector<SchemaArgument*>::iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ delete *iter;
+}
+
+SchemaClass::SchemaClass(const uint8_t _kind, const ClassKey& _key, framing::Buffer& buffer) :
+ kind(_kind), key(_key)
+{
+ if (kind == KIND_TABLE) {
+ uint8_t hasSupertype = 0; //buffer.getOctet();
+ uint16_t propCount = buffer.getShort();
+ uint16_t statCount = buffer.getShort();
+ uint16_t methodCount = buffer.getShort();
+
+ if (hasSupertype) {
+ string unused;
+ buffer.getShortString(unused);
+ buffer.getShortString(unused);
+ buffer.getLongLong();
+ buffer.getLongLong();
+ }
+
+ for (uint16_t idx = 0; idx < propCount; idx++)
+ properties.push_back(new SchemaProperty(buffer));
+ for (uint16_t idx = 0; idx < statCount; idx++)
+ statistics.push_back(new SchemaStatistic(buffer));
+ for (uint16_t idx = 0; idx < methodCount; idx++)
+ methods.push_back(new SchemaMethod(buffer));
+
+ } else if (kind == KIND_EVENT) {
+ uint16_t argCount = buffer.getShort();
+
+ for (uint16_t idx = 0; idx < argCount; idx++)
+ arguments.push_back(new SchemaArgument(buffer));
+ }
+}
+
+SchemaClass::~SchemaClass()
+{
+ for (vector<SchemaProperty*>::iterator iter = properties.begin();
+ iter != properties.end(); iter++)
+ delete *iter;
+ for (vector<SchemaStatistic*>::iterator iter = statistics.begin();
+ iter != statistics.end(); iter++)
+ delete *iter;
+ for (vector<SchemaMethod*>::iterator iter = methods.begin();
+ iter != methods.end(); iter++)
+ delete *iter;
+ for (vector<SchemaArgument*>::iterator iter = arguments.begin();
+ iter != arguments.end(); iter++)
+ delete *iter;
+}
+
diff --git a/qpid/cpp/src/qpid/console/SequenceManager.cpp b/qpid/cpp/src/qpid/console/SequenceManager.cpp
new file mode 100644
index 0000000000..86ea829749
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/SequenceManager.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/SequenceManager.h"
+
+using namespace qpid::console;
+using namespace qpid::sys;
+using std::string;
+using std::cout;
+using std::endl;
+
+uint32_t SequenceManager::reserve(const std::string& context)
+{
+ Mutex::ScopedLock l(lock);
+ uint32_t result = sequence++;
+ pending[result] = context;
+ return result;
+}
+
+std::string SequenceManager::release(uint32_t seq)
+{
+ Mutex::ScopedLock l(lock);
+ std::map<uint32_t, string>::iterator iter = pending.find(seq);
+ if (iter == pending.end())
+ return string();
+ string result(iter->second);
+ pending.erase(iter);
+ return result;
+}
+
diff --git a/qpid/cpp/src/qpid/console/SessionManager.cpp b/qpid/cpp/src/qpid/console/SessionManager.cpp
new file mode 100644
index 0000000000..80c5959417
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/SessionManager.cpp
@@ -0,0 +1,517 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/console/SessionManager.h"
+#include "qpid/console/Schema.h"
+#include "qpid/console/Agent.h"
+#include "qpid/console/ConsoleListener.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/FieldTable.h"
+
+using namespace qpid::console;
+using namespace qpid::sys;
+using namespace qpid;
+using namespace std;
+using qpid::framing::Buffer;
+using qpid::framing::FieldTable;
+
+SessionManager::SessionManager(ConsoleListener* _listener, Settings _settings) :
+ listener(_listener), settings(_settings)
+{
+ bindingKeys();
+}
+
+SessionManager::~SessionManager()
+{
+ for (vector<Broker*>::iterator iter = brokers.begin();
+ iter != brokers.end(); iter++)
+ delete *iter;
+
+ for (map<string, Package*>::iterator iter = packages.begin();
+ iter != packages.end(); iter++) {
+ for (Package::ClassMap::iterator citer = iter->second->classes.begin();
+ citer != iter->second->classes.end();
+ citer++)
+ delete citer->second;
+ delete iter->second;
+ }
+}
+
+Broker* SessionManager::addBroker(client::ConnectionSettings& settings)
+{
+ Broker* broker(new Broker(*this, settings));
+ {
+ Mutex::ScopedLock l(brokerListLock);
+ brokers.push_back(broker);
+ }
+ return broker;
+}
+
+void SessionManager::delBroker(Broker* broker)
+{
+ Mutex::ScopedLock l(brokerListLock);
+ for (vector<Broker*>::iterator iter = brokers.begin();
+ iter != brokers.end(); iter++)
+ if (*iter == broker) {
+ brokers.erase(iter);
+ delete broker;
+ return;
+ }
+}
+
+void SessionManager::getPackages(NameVector& packageNames)
+{
+ allBrokersStable();
+ packageNames.clear();
+ {
+ Mutex::ScopedLock l(lock);
+ for (map<string, Package*>::iterator iter = packages.begin();
+ iter != packages.end(); iter++)
+ packageNames.push_back(iter->first);
+ }
+}
+
+void SessionManager::getClasses(KeyVector& classKeys, const std::string& packageName)
+{
+ allBrokersStable();
+ classKeys.clear();
+ map<string, Package*>::iterator iter = packages.find(packageName);
+ if (iter == packages.end())
+ return;
+
+ Package& package = *(iter->second);
+ for (Package::ClassMap::const_iterator piter = package.classes.begin();
+ piter != package.classes.end(); piter++) {
+ ClassKey key(piter->second->getClassKey());
+ classKeys.push_back(key);
+ }
+}
+
+SchemaClass& SessionManager::getSchema(const ClassKey& classKey)
+{
+ allBrokersStable();
+ map<string, Package*>::iterator iter = packages.find(classKey.getPackageName());
+ if (iter == packages.end())
+ throw Exception("Unknown package");
+
+ Package& package = *(iter->second);
+ Package::NameHash key(classKey.getClassName(), classKey.getHash());
+ Package::ClassMap::iterator cIter = package.classes.find(key);
+ if (cIter == package.classes.end())
+ throw Exception("Unknown class");
+
+ return *(cIter->second);
+}
+
+void SessionManager::bindPackage(const std::string& packageName)
+{
+ stringstream key;
+ key << "console.obj.*.*." << packageName << ".#";
+ bindingKeyList.push_back(key.str());
+ for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++)
+ (*iter)->addBinding(key.str());
+}
+
+void SessionManager::bindClass(const ClassKey& classKey)
+{
+ bindClass(classKey.getPackageName(), classKey.getClassName());
+}
+
+void SessionManager::bindClass(const std::string& packageName, const std::string& className)
+{
+ stringstream key;
+ key << "console.obj.*.*." << packageName << "." << className << ".#";
+ bindingKeyList.push_back(key.str());
+ for (vector<Broker*>::iterator iter = brokers.begin();
+ iter != brokers.end(); iter++)
+ (*iter)->addBinding(key.str());
+}
+
+
+void SessionManager::bindEvent(const ClassKey& classKey)
+{
+ bindEvent(classKey.getPackageName(), classKey.getClassName());
+}
+
+
+void SessionManager::bindEvent(const std::string& packageName, const std::string& eventName)
+{
+ if (!settings.userBindings) throw Exception("Session not configured for userBindings.");
+ if (settings.rcvEvents) throw Exception("Session already configured to receive all events.");
+
+ stringstream key;
+ key << "console.event.*.*." << packageName;
+ if (eventName.length()) {
+ key << "." << eventName << ".#";
+ } else {
+ key << ".#";
+ }
+
+ bindingKeyList.push_back(key.str());
+ for (vector<Broker*>::iterator iter = brokers.begin();
+ iter != brokers.end(); iter++)
+ (*iter)->addBinding(key.str());
+}
+
+
+void SessionManager::getAgents(Agent::Vector& agents, Broker* broker)
+{
+ agents.clear();
+ if (broker != 0) {
+ broker->appendAgents(agents);
+ } else {
+ for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++) {
+ (*iter)->appendAgents(agents);
+ }
+ }
+}
+
+void SessionManager::getObjects(Object::Vector& objects, const std::string& className,
+ Broker* _broker, Agent* _agent)
+{
+ Agent::Vector agentList;
+
+ if (_agent != 0) {
+ agentList.push_back(_agent);
+ _agent->getBroker()->waitForStable();
+ } else {
+ if (_broker != 0) {
+ _broker->appendAgents(agentList);
+ _broker->waitForStable();
+ } else {
+ allBrokersStable();
+ Mutex::ScopedLock _lock(brokerListLock);
+ for (vector<Broker*>::iterator iter = brokers.begin(); iter != brokers.end(); iter++) {
+ (*iter)->appendAgents(agentList);
+ }
+ }
+ }
+
+ FieldTable ft;
+ uint32_t sequence;
+ ft.setString("_class", className);
+
+ getResult.clear();
+ syncSequenceList.clear();
+ error = string();
+
+ if (agentList.empty()) {
+ objects = getResult;
+ return;
+ }
+
+ for (Agent::Vector::iterator iter = agentList.begin(); iter != agentList.end(); iter++) {
+ Agent* agent = *iter;
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+ stringstream routingKey;
+ routingKey << "agent." << agent->getBrokerBank() << "." << agent->getAgentBank();
+ {
+ Mutex::ScopedLock _lock(lock);
+ sequence = sequenceManager.reserve("multiget");
+ syncSequenceList.insert(sequence);
+ }
+ agent->getBroker()->encodeHeader(buffer, 'G', sequence);
+ ft.encode(buffer);
+ uint32_t length = buffer.getPosition();
+ buffer.reset();
+ agent->getBroker()->connThreadBody.sendBuffer(buffer, length, "qpid.management", routingKey.str());
+ }
+
+ {
+ Mutex::ScopedLock _lock(lock);
+ sys::AbsTime startTime = sys::now();
+ while (!syncSequenceList.empty() && error.empty()) {
+ cv.wait(lock, AbsTime(now(), settings.getTimeout * TIME_SEC));
+ sys::AbsTime currTime = sys::now();
+ if (sys::Duration(startTime, currTime) > settings.getTimeout * TIME_SEC)
+ break;
+ }
+ }
+
+ objects = getResult;
+}
+
+void SessionManager::bindingKeys()
+{
+ bindingKeyList.push_back("schema.#");
+ if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) {
+ bindingKeyList.push_back("console.#");
+ } else {
+ if (settings.rcvObjects && !settings.userBindings)
+ bindingKeyList.push_back("console.obj.#");
+ else
+ bindingKeyList.push_back("console.obj.*.*.org.apache.qpid.broker.agent");
+ if (settings.rcvEvents)
+ bindingKeyList.push_back("console.event.#");
+ if (settings.rcvHeartbeats)
+ bindingKeyList.push_back("console.heartbeat");
+ }
+}
+
+void SessionManager::allBrokersStable()
+{
+ Mutex::ScopedLock l(brokerListLock);
+ for (vector<Broker*>::iterator iter = brokers.begin();
+ iter != brokers.end(); iter++)
+ if ((*iter)->isConnected())
+ (*iter)->waitForStable();
+}
+
+void SessionManager::startProtocol(Broker* broker)
+{
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ broker->encodeHeader(buffer, 'B');
+ uint32_t length = 512 - buffer.available();
+ buffer.reset();
+ broker->connThreadBody.sendBuffer(buffer, length);
+}
+
+
+void SessionManager::handleBrokerResp(Broker* broker, Buffer& inBuffer, uint32_t)
+{
+ framing::Uuid brokerId;
+
+ brokerId.decode(inBuffer);
+ broker->setBrokerId(brokerId);
+
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ uint32_t sequence = sequenceManager.reserve("startup");
+ broker->encodeHeader(buffer, 'P', sequence);
+ uint32_t length = 512 - buffer.available();
+ buffer.reset();
+ broker->connThreadBody.sendBuffer(buffer, length);
+
+ if (listener != 0) {
+ listener->brokerInfo(*broker);
+ }
+}
+
+void SessionManager::handlePackageInd(Broker* broker, Buffer& inBuffer, uint32_t)
+{
+ string packageName;
+ inBuffer.getShortString(packageName);
+
+ {
+ Mutex::ScopedLock l(lock);
+ map<string, Package*>::iterator iter = packages.find(packageName);
+ if (iter == packages.end()) {
+ packages[packageName] = new Package(packageName);
+ if (listener != 0)
+ listener->newPackage(packageName);
+ }
+ }
+
+ broker->incOutstanding();
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ uint32_t sequence = sequenceManager.reserve("startup");
+ broker->encodeHeader(buffer, 'Q', sequence);
+ buffer.putShortString(packageName);
+ uint32_t length = 512 - buffer.available();
+ buffer.reset();
+ broker->connThreadBody.sendBuffer(buffer, length);
+}
+
+void SessionManager::handleCommandComplete(Broker* broker, Buffer& inBuffer, uint32_t sequence)
+{
+ Mutex::ScopedLock l(lock);
+ uint32_t resultCode = inBuffer.getLong();
+ string resultText;
+ inBuffer.getShortString(resultText);
+ string context = sequenceManager.release(sequence);
+ if (resultCode != 0)
+ QPID_LOG(debug, "Received error in completion: " << resultCode << " " << resultText);
+ if (context == "startup") {
+ broker->decOutstanding();
+ } else if (context == "multiget") {
+ if (syncSequenceList.count(sequence) == 1) {
+ syncSequenceList.erase(sequence);
+ if (syncSequenceList.empty()) {
+ cv.notify();
+ }
+ }
+ }
+ // TODO: Other context cases
+}
+
+void SessionManager::handleClassInd(Broker* broker, Buffer& inBuffer, uint32_t)
+{
+ uint8_t kind;
+ string packageName;
+ string className;
+ uint8_t hash[16];
+
+ kind = inBuffer.getOctet();
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+
+ {
+ Mutex::ScopedLock l(lock);
+ map<string, Package*>::iterator pIter = packages.find(packageName);
+ if (pIter == packages.end() || pIter->second->getClass(className, hash))
+ return;
+ }
+
+ broker->incOutstanding();
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ uint32_t sequence = sequenceManager.reserve("startup");
+ broker->encodeHeader(buffer, 'S', sequence);
+ buffer.putShortString(packageName);
+ buffer.putShortString(className);
+ buffer.putBin128(hash);
+ uint32_t length = 512 - buffer.available();
+ buffer.reset();
+ broker->connThreadBody.sendBuffer(buffer, length);
+}
+
+void SessionManager::handleMethodResp(Broker* broker, Buffer& buffer, uint32_t sequence)
+{
+ if (broker->methodObject) {
+ broker->methodObject->handleMethodResp(buffer, sequence);
+ }
+}
+
+void SessionManager::handleHeartbeatInd(Broker* /*broker*/, Buffer& /*inBuffer*/, uint32_t /*sequence*/)
+{
+}
+
+void SessionManager::handleEventInd(Broker* broker, Buffer& buffer, uint32_t /*sequence*/)
+{
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ SchemaClass* schemaClass;
+
+ buffer.getShortString(packageName);
+ buffer.getShortString(className);
+ buffer.getBin128(hash);
+
+ {
+ Mutex::ScopedLock l(lock);
+ map<string, Package*>::iterator pIter = packages.find(packageName);
+ if (pIter == packages.end())
+ return;
+ schemaClass = pIter->second->getClass(className, hash);
+ if (schemaClass == 0)
+ return;
+ }
+
+ Event event(broker, schemaClass, buffer);
+
+ if (listener)
+ listener->event(event);
+}
+
+void SessionManager::handleSchemaResp(Broker* broker, Buffer& inBuffer, uint32_t sequence)
+{
+ uint8_t kind;
+ string packageName;
+ string className;
+ uint8_t hash[16];
+
+ kind = inBuffer.getOctet();
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+
+ {
+ Mutex::ScopedLock l(lock);
+ map<string, Package*>::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end() && !pIter->second->getClass(className, hash)) {
+ ClassKey key(packageName, className, hash);
+ SchemaClass* schemaClass(new SchemaClass(kind, key, inBuffer));
+ pIter->second->addClass(className, hash, schemaClass);
+ if (listener != 0) {
+ listener->newClass(schemaClass->getClassKey());
+ }
+ }
+ }
+
+ sequenceManager.release(sequence);
+ broker->decOutstanding();
+}
+
+void SessionManager::handleContentInd(Broker* broker, Buffer& buffer, uint32_t sequence, bool prop, bool stat)
+{
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ SchemaClass* schemaClass;
+
+ buffer.getShortString(packageName);
+ buffer.getShortString(className);
+ buffer.getBin128(hash);
+
+ {
+ Mutex::ScopedLock l(lock);
+ map<string, Package*>::iterator pIter = packages.find(packageName);
+ if (pIter == packages.end())
+ return;
+ schemaClass = pIter->second->getClass(className, hash);
+ if (schemaClass == 0)
+ return;
+ }
+
+ Object object(broker, schemaClass, buffer, prop, stat);
+
+ if (prop && className == "agent" && packageName == "org.apache.qpid.broker")
+ broker->updateAgent(object);
+
+ {
+ Mutex::ScopedLock l(lock);
+ if (syncSequenceList.count(sequence) == 1) {
+ if (!object.isDeleted())
+ getResult.push_back(object);
+ return;
+ }
+ }
+
+ if (listener) {
+ if (prop)
+ listener->objectProps(*broker, object);
+ if (stat)
+ listener->objectStats(*broker, object);
+ }
+}
+
+void SessionManager::handleBrokerConnect(Broker* broker)
+{
+ if (listener != 0)
+ listener->brokerConnected(*broker);
+}
+
+void SessionManager::handleBrokerDisconnect(Broker* broker)
+{
+ if (listener != 0)
+ listener->brokerDisconnected(*broker);
+}
+
diff --git a/qpid/cpp/src/qpid/console/Value.cpp b/qpid/cpp/src/qpid/console/Value.cpp
new file mode 100644
index 0000000000..47c6a4ce57
--- /dev/null
+++ b/qpid/cpp/src/qpid/console/Value.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/Value.h"
+#include "qpid/framing/Buffer.h"
+
+#include <sstream>
+
+using namespace qpid;
+using namespace qpid::console;
+using namespace std;
+
+string NullValue::str() const
+{
+ return "<Null>";
+}
+
+RefValue::RefValue(framing::Buffer& buffer)
+{
+ uint64_t first = buffer.getLongLong();
+ uint64_t second = buffer.getLongLong();
+ value.setValue(first, second);
+}
+
+string RefValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+string UintValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+string IntValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+string Uint64Value::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+string Int64Value::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+StringValue::StringValue(framing::Buffer& buffer, int tc)
+{
+ if (tc == 6)
+ buffer.getShortString(value);
+ else
+ buffer.getMediumString(value);
+}
+
+string BoolValue::str() const
+{
+ return value ? "T" : "F";
+}
+
+string FloatValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+string DoubleValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+UuidValue::UuidValue(framing::Buffer& buffer)
+{
+ value.decode(buffer);
+}
+
+string MapValue::str() const
+{
+ stringstream s;
+ s << value;
+ return s.str();
+}
+
+MapValue::MapValue(framing::Buffer& buffer)
+{
+ value.decode(buffer);
+}
+
+
+Value::Ptr ValueFactory::newValue(int typeCode, framing::Buffer& buffer)
+{
+ switch (typeCode) {
+ case 1: return Value::Ptr(new UintValue(buffer.getOctet())); // U8
+ case 2: return Value::Ptr(new UintValue(buffer.getShort())); // U16
+ case 3: return Value::Ptr(new UintValue(buffer.getLong())); // U32
+ case 4: return Value::Ptr(new Uint64Value(buffer.getLongLong())); // U64
+ case 6: return Value::Ptr(new StringValue(buffer, 6)); // SSTR
+ case 7: return Value::Ptr(new StringValue(buffer, 7)); // LSTR
+ case 8: return Value::Ptr(new Int64Value(buffer.getLongLong())); // ABSTIME
+ case 9: return Value::Ptr(new Uint64Value(buffer.getLongLong())); // DELTATIME
+ case 10: return Value::Ptr(new RefValue(buffer)); // REF
+ case 11: return Value::Ptr(new BoolValue(buffer.getOctet())); // BOOL
+ case 12: return Value::Ptr(new FloatValue(buffer.getFloat())); // FLOAT
+ case 13: return Value::Ptr(new DoubleValue(buffer.getDouble())); // DOUBLE
+ case 14: return Value::Ptr(new UuidValue(buffer)); // UUID
+ case 15: return Value::Ptr(new MapValue(buffer)); // MAP
+ case 16: return Value::Ptr(new IntValue(buffer.getOctet())); // S8
+ case 17: return Value::Ptr(new IntValue(buffer.getShort())); // S16
+ case 18: return Value::Ptr(new IntValue(buffer.getLong())); // S32
+ case 19: return Value::Ptr(new Int64Value(buffer.getLongLong())); // S64
+ }
+
+ return Value::Ptr();
+}
+
+void ValueFactory::encodeValue(int typeCode, Value::Ptr value, framing::Buffer& buffer)
+{
+ switch (typeCode) {
+ case 1: buffer.putOctet(value->asUint()); return; // U8
+ case 2: buffer.putShort(value->asUint()); return; // U16
+ case 3: buffer.putLong(value->asUint()); return; // U32
+ case 4: buffer.putLongLong(value->asUint64()); return; // U64
+ case 6: buffer.putShortString(value->asString()); return; // SSTR
+ case 7: buffer.putMediumString(value->asString()); return; // LSTR
+ case 8: buffer.putLongLong(value->asInt64()); return; // ABSTIME
+ case 9: buffer.putLongLong(value->asUint64()); return; // DELTATIME
+ case 10: value->asObjectId().encode(buffer); return; // REF
+ case 11: buffer.putOctet(value->asBool() ? 1 : 0); return; // BOOL
+ case 12: buffer.putFloat(value->asFloat()); return; // FLOAT
+ case 13: buffer.putDouble(value->asDouble()); return; // DOUBLE
+ case 14: value->asUuid().encode(buffer); return; // UUID
+ case 15: value->asMap().encode(buffer); return; // MAP
+ case 16: buffer.putOctet(value->asInt()); return; // S8
+ case 17: buffer.putShort(value->asInt()); return; // S16
+ case 18: buffer.putLong(value->asInt()); return; // S32
+ case 19: buffer.putLongLong(value->asInt64()); return; // S64
+ }
+}
diff --git a/qpid/cpp/src/qpid/framing/AMQBody.cpp b/qpid/cpp/src/qpid/framing/AMQBody.cpp
new file mode 100644
index 0000000000..b3eeae0615
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQBody.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
+#include <iostream>
+
+namespace qpid {
+namespace framing {
+
+std::ostream& operator<<(std::ostream& out, const AMQBody& body)
+{
+ body.print(out);
+ return out;
+}
+
+AMQBody::~AMQBody() {}
+
+namespace {
+struct MatchBodies : public AMQBodyConstVisitor {
+ const AMQBody& body;
+ bool match;
+
+ MatchBodies(const AMQBody& b) : body(b), match(false) {}
+ virtual ~MatchBodies() {}
+
+ virtual void visit(const AMQHeaderBody&) { match=dynamic_cast<const AMQHeaderBody*>(&body); }
+ virtual void visit(const AMQContentBody&) { match=dynamic_cast<const AMQContentBody*>(&body); }
+ virtual void visit(const AMQHeartbeatBody&) { match=dynamic_cast<const AMQHeartbeatBody*>(&body); }
+ virtual void visit(const AMQMethodBody& x) {
+ const AMQMethodBody* y=dynamic_cast<const AMQMethodBody*>(&body);
+ match = (y && y->amqpMethodId() == x.amqpMethodId() && y->amqpClassId() == x.amqpClassId());
+ }
+};
+
+}
+bool AMQBody::match(const AMQBody& a, const AMQBody& b) {
+ MatchBodies matcher(a);
+ b.accept(matcher);
+ return matcher.match;
+}
+
+}} // namespace
diff --git a/qpid/cpp/src/qpid/framing/AMQBody.h b/qpid/cpp/src/qpid/framing/AMQBody.h
new file mode 100644
index 0000000000..56d1d250c1
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQBody.h
@@ -0,0 +1,86 @@
+#ifndef QPID_FRAMING_AMQBODY_H
+#define QPID_FRAMING_AMQBODY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/RefCounted.h"
+#include "qpid/framing/BodyFactory.h"
+#include <boost/intrusive_ptr.hpp>
+#include <ostream>
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+class Buffer;
+
+class AMQMethodBody;
+class AMQHeaderBody;
+class AMQContentBody;
+class AMQHeartbeatBody;
+
+struct AMQBodyConstVisitor {
+ virtual ~AMQBodyConstVisitor() {}
+ virtual void visit(const AMQHeaderBody&) = 0;
+ virtual void visit(const AMQContentBody&) = 0;
+ virtual void visit(const AMQHeartbeatBody&) = 0;
+ virtual void visit(const AMQMethodBody&) = 0;
+};
+
+class QPID_COMMON_CLASS_EXTERN AMQBody : public RefCounted {
+ public:
+ AMQBody() {}
+ QPID_COMMON_EXTERN virtual ~AMQBody();
+
+ // Make AMQBody copyable even though RefCounted.
+ AMQBody(const AMQBody&) : RefCounted() {}
+ AMQBody& operator=(const AMQBody&) { return *this; }
+
+ virtual uint8_t type() const = 0;
+
+ virtual void encode(Buffer& buffer) const = 0;
+ virtual void decode(Buffer& buffer, uint32_t=0) = 0;
+ virtual uint32_t encodedSize() const = 0;
+
+ virtual void print(std::ostream& out) const = 0;
+ virtual void accept(AMQBodyConstVisitor&) const = 0;
+
+ virtual AMQMethodBody* getMethod() { return 0; }
+ virtual const AMQMethodBody* getMethod() const { return 0; }
+
+ /** Match if same type and same class/method ID for methods */
+ static bool match(const AMQBody& , const AMQBody& );
+ virtual boost::intrusive_ptr<AMQBody> clone() const = 0;
+};
+
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& out, const AMQBody& body) ;
+
+enum BodyTypes {
+ METHOD_BODY = 1,
+ HEADER_BODY = 2,
+ CONTENT_BODY = 3,
+ HEARTBEAT_BODY = 8
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_AMQBODY_H*/
diff --git a/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h b/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h
new file mode 100644
index 0000000000..d12b70a168
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h
@@ -0,0 +1,70 @@
+#ifndef QPID_FRAMING_AMQCOMMANDCONTROLBODY_H
+#define QPID_FRAMING_AMQCOMMANDCONTROLBODY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/amqp_0_10/helpers.h"
+#include "qpid/framing/AMQBody.h"
+
+namespace qpid {
+namespace framing {
+
+/**
+ * AMQBody wrapper for Command and Control.
+ * Temporary measure to fit with old code.
+ */
+template <class T> class AMQCommandControlBody : public AMQBody, public T
+{
+ public:
+ virtual uint8_t type() const { return 100+T::SEGMENT_TYPE; }
+
+ virtual void encode(Buffer& buffer) const {
+ Codec::encode(buffer.getIterator(), static_cast<const T&>(*this));
+ }
+ virtual void decode(Buffer& buffer, uint32_t=0) {
+ Codec::decode(buffer.getIterator(), static_cast<T&>(*this));
+ }
+ virtual uint32_t encodedSize() const {
+ Codec::size(buffer.getIterator(), static_cast<const T&>(*this));
+ }
+
+ virtual void print(std::ostream& out) const {
+ out << static_cast<const T&>(*this) << endl;
+ }
+ virtual void AMQBody::accept(AMQBodyConstVisitor&) const { assert(0); }
+};
+
+class CommandBody : public AMQCommandControlBody<amqp_0_10::Command> {
+ using Command::accept; // Hide AMQBody::accept
+ virtual Command* getCommand() { return this; }
+ virtual const Command* getCommand() const { return this; }
+};
+
+class ControlBody : public AMQCommandControlBody<amqp_0_10::Control> {
+ using Control::accept; // Hide AMQBody::accept
+ virtual Control* getControl() { return this; }
+ virtual const Control* getControl() const { return this; }
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_AMQCOMMANDCONTROLBODY_H*/
diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.cpp b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp
new file mode 100644
index 0000000000..72f7d9978e
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AMQContentBody.h"
+#include <iostream>
+
+qpid::framing::AMQContentBody::AMQContentBody(){
+}
+
+qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){
+}
+
+uint32_t qpid::framing::AMQContentBody::encodedSize() const{
+ return data.size();
+}
+void qpid::framing::AMQContentBody::encode(Buffer& buffer) const{
+ buffer.putRawData(data);
+}
+void qpid::framing::AMQContentBody::decode(Buffer& buffer, uint32_t _size){
+ buffer.getRawData(data, _size);
+}
+
+void qpid::framing::AMQContentBody::print(std::ostream& out) const
+{
+ out << "content (" << encodedSize() << " bytes)";
+ const size_t max = 32;
+ out << " " << data.substr(0, max);
+ if (data.size() > max) out << "...";
+}
diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.h b/qpid/cpp/src/qpid/framing/AMQContentBody.h
new file mode 100644
index 0000000000..e25451e354
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQContentBody.h
@@ -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.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/CommonImportExport.h"
+
+#ifndef _AMQContentBody_
+#define _AMQContentBody_
+
+namespace qpid {
+namespace framing {
+
+class QPID_COMMON_CLASS_EXTERN AMQContentBody : public AMQBody
+{
+ string data;
+
+public:
+ QPID_COMMON_EXTERN AMQContentBody();
+ QPID_COMMON_EXTERN AMQContentBody(const string& data);
+ inline virtual ~AMQContentBody(){}
+ inline uint8_t type() const { return CONTENT_BODY; };
+ inline const string& getData() const { return data; }
+ inline string& getData() { return data; }
+ QPID_COMMON_EXTERN uint32_t encodedSize() const;
+ QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
+ QPID_COMMON_EXTERN void decode(Buffer& buffer, uint32_t size);
+ QPID_COMMON_EXTERN void print(std::ostream& out) const;
+ void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AMQDataBlock.h b/qpid/cpp/src/qpid/framing/AMQDataBlock.h
new file mode 100644
index 0000000000..7f0d0dc2b5
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQDataBlock.h
@@ -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.
+ *
+ */
+#include "qpid/framing/Buffer.h"
+
+#ifndef _AMQDataBlock_
+#define _AMQDataBlock_
+
+namespace qpid {
+namespace framing {
+
+class AMQDataBlock
+{
+public:
+ virtual ~AMQDataBlock() {}
+ virtual void encode(Buffer& buffer) const = 0;
+ virtual bool decode(Buffer& buffer) = 0;
+ virtual uint32_t encodedSize() const = 0;
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.cpp b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
new file mode 100644
index 0000000000..cd60cd971f
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AMQFrame.h"
+
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/BodyFactory.h"
+#include "qpid/framing/MethodBodyFactory.h"
+#include "qpid/Msg.h"
+
+#include <boost/format.hpp>
+#include <iostream>
+
+namespace qpid {
+namespace framing {
+
+void AMQFrame::init() { bof = eof = bos = eos = true; subchannel=0; channel=0; }
+
+AMQFrame::AMQFrame(const boost::intrusive_ptr<AMQBody>& b) : body(b) { init(); }
+
+AMQFrame::AMQFrame(const AMQBody& b) : body(b.clone()) { init(); }
+
+AMQFrame::~AMQFrame() {}
+
+void AMQFrame::setMethod(ClassId c, MethodId m) { body = MethodBodyFactory::create(c,m); }
+
+uint32_t AMQFrame::encodedSize() const {
+ uint32_t size = frameOverhead() + body->encodedSize();
+ if (body->getMethod())
+ size += sizeof(ClassId)+sizeof(MethodId);
+ return size;
+}
+
+uint32_t AMQFrame::frameOverhead() {
+ return 12 /*frame header*/;
+}
+
+uint16_t AMQFrame::DECODE_SIZE_MIN=4;
+
+uint16_t AMQFrame::decodeSize(char* data) {
+ Buffer buf(data+2, DECODE_SIZE_MIN);
+ return buf.getShort();
+}
+
+void AMQFrame::encode(Buffer& buffer) const
+{
+ //set track first (controls on track 0, everything else on 1):
+ uint8_t track = getBody()->type() ? 1 : 0;
+
+ uint8_t flags = (bof ? 0x08 : 0) | (eof ? 0x04 : 0) | (bos ? 0x02 : 0) | (eos ? 0x01 : 0);
+ buffer.putOctet(flags);
+ buffer.putOctet(getBody()->type());
+ buffer.putShort(encodedSize());
+ buffer.putOctet(0);
+ buffer.putOctet(0x0f & track);
+ buffer.putShort(channel);
+ buffer.putLong(0);
+ const AMQMethodBody* method=getMethod();
+ if (method) {
+ buffer.putOctet(method->amqpClassId());
+ buffer.putOctet(method->amqpMethodId());
+ }
+ body->encode(buffer);
+}
+
+bool AMQFrame::decode(Buffer& buffer)
+{
+ if(buffer.available() < frameOverhead())
+ return false;
+ buffer.record();
+
+ uint8_t flags = buffer.getOctet();
+ uint8_t framing_version = (flags & 0xc0) >> 6;
+ if (framing_version != 0)
+ throw FramingErrorException(QPID_MSG("Framing version unsupported"));
+ bof = flags & 0x08;
+ eof = flags & 0x04;
+ bos = flags & 0x02;
+ eos = flags & 0x01;
+ uint8_t type = buffer.getOctet();
+ uint16_t frame_size = buffer.getShort();
+ if (frame_size < frameOverhead())
+ throw FramingErrorException(QPID_MSG("Frame size too small " << frame_size));
+ uint8_t reserved1 = buffer.getOctet();
+ uint8_t field1 = buffer.getOctet();
+ subchannel = field1 & 0x0f;
+ channel = buffer.getShort();
+ (void) buffer.getLong(); // reserved2
+
+ // Verify that the protocol header meets current spec
+ // TODO: should we check reserved2 against zero as well? - the
+ // spec isn't clear
+ if ((flags & 0x30) != 0 || reserved1 != 0 || (field1 & 0xf0) != 0)
+ throw FramingErrorException(QPID_MSG("Reserved bits not zero"));
+
+ // TODO: should no longer care about body size and only pass up
+ // B,E,b,e flags
+ uint16_t body_size = frame_size - frameOverhead();
+ if (buffer.available() < body_size){
+ buffer.restore();
+ return false;
+ }
+
+ switch(type)
+ {
+ case 0://CONTROL
+ case METHOD_BODY: {
+ ClassId c = buffer.getOctet();
+ MethodId m = buffer.getOctet();
+ body = MethodBodyFactory::create(c, m);
+ break;
+ }
+ case HEADER_BODY: body = BodyFactory::create<AMQHeaderBody>(); break;
+ case CONTENT_BODY: body = BodyFactory::create<AMQContentBody>(); break;
+ case HEARTBEAT_BODY: body = BodyFactory::create<AMQHeartbeatBody>(); break;
+ default:
+ throw IllegalArgumentException(QPID_MSG("Invalid frame type " << type));
+ }
+ body->decode(buffer, body_size);
+
+ return true;
+}
+
+std::ostream& operator<<(std::ostream& out, const AMQFrame& f)
+{
+ return
+ out << "Frame["
+ << (f.getBof() ? "B" : "") << (f.getEof() ? "E" : "")
+ << (f.getBos() ? "b" : "") << (f.getEos() ? "e" : "") << "; "
+ << "channel=" << f.getChannel() << "; " << *f.getBody()
+ << "]";
+}
+
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.h b/qpid/cpp/src/qpid/framing/AMQFrame.h
new file mode 100644
index 0000000000..c669d12bc0
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQFrame.h
@@ -0,0 +1,112 @@
+#ifndef _AMQFrame_
+#define _AMQFrame_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/AMQDataBlock.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include <boost/intrusive_ptr.hpp>
+#include <boost/cast.hpp>
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+class QPID_COMMON_CLASS_EXTERN AMQFrame : public AMQDataBlock
+{
+ public:
+ QPID_COMMON_EXTERN AMQFrame(const boost::intrusive_ptr<AMQBody>& b=0);
+ QPID_COMMON_EXTERN AMQFrame(const AMQBody& b);
+ QPID_COMMON_EXTERN ~AMQFrame();
+
+ ChannelId getChannel() const { return channel; }
+ void setChannel(ChannelId c) { channel = c; }
+
+ AMQBody* getBody() { return body.get(); }
+ const AMQBody* getBody() const { return body.get(); }
+
+ AMQMethodBody* getMethod() { return getBody() ? getBody()->getMethod() : 0; }
+ const AMQMethodBody* getMethod() const { return getBody() ? getBody()->getMethod() : 0; }
+
+ void setMethod(ClassId c, MethodId m);
+
+ template <class T> T* castBody() {
+ return boost::polymorphic_downcast<T*>(getBody());
+ }
+
+ template <class T> const T* castBody() const {
+ return boost::polymorphic_downcast<const T*>(getBody());
+ }
+
+ QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
+ QPID_COMMON_EXTERN bool decode(Buffer& buffer);
+ QPID_COMMON_EXTERN uint32_t encodedSize() const;
+
+ // 0-10 terminology: first/last frame (in segment) first/last segment (in assembly)
+
+ bool isFirstSegment() const { return bof; }
+ bool isLastSegment() const { return eof; }
+ bool isFirstFrame() const { return bos; }
+ bool isLastFrame() const { return eos; }
+
+ void setFirstSegment(bool set=true) { bof = set; }
+ void setLastSegment(bool set=true) { eof = set; }
+ void setFirstFrame(bool set=true) { bos = set; }
+ void setLastFrame(bool set=true) { eos = set; }
+
+ // 0-9 terminology: beginning/end of frameset, beginning/end of segment.
+
+ bool getBof() const { return bof; }
+ void setBof(bool isBof) { bof = isBof; }
+ bool getEof() const { return eof; }
+ void setEof(bool isEof) { eof = isEof; }
+
+ bool getBos() const { return bos; }
+ void setBos(bool isBos) { bos = isBos; }
+ bool getEos() const { return eos; }
+ void setEos(bool isEos) { eos = isEos; }
+
+ static uint16_t DECODE_SIZE_MIN;
+ QPID_COMMON_EXTERN static uint32_t frameOverhead();
+ /** Must point to at least DECODE_SIZE_MIN bytes of data */
+ static uint16_t decodeSize(char* data);
+
+ private:
+ void init();
+
+ boost::intrusive_ptr<AMQBody> body;
+ uint16_t channel : 16;
+ uint8_t subchannel : 8;
+ bool bof : 1;
+ bool eof : 1;
+ bool bos : 1;
+ bool eos : 1;
+};
+
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const AMQFrame&);
+
+}} // namespace qpid::framing
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp b/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp
new file mode 100644
index 0000000000..14218f1b45
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+
+uint32_t qpid::framing::AMQHeaderBody::encodedSize() const {
+ return properties.encodedSize();
+}
+
+void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const {
+ properties.encode(buffer);
+}
+
+void qpid::framing::AMQHeaderBody::decode(Buffer& buffer, uint32_t size) {
+ uint32_t limit = buffer.available() - size;
+ while (buffer.available() > limit + 2) {
+ uint32_t len = buffer.getLong();
+ uint16_t type = buffer.getShort();
+ if (!properties.decode(buffer, len, type)) {
+ // TODO: should just skip & keep for later dispatch.
+ throw Exception(QPID_MSG("Unexpected property type: " << type));
+ }
+ }
+}
+
+uint64_t qpid::framing::AMQHeaderBody::getContentLength() const
+{
+ const MessageProperties* mProps = get<MessageProperties>();
+ if (mProps)
+ return mProps->getContentLength();
+ return 0;
+}
+
+void qpid::framing::AMQHeaderBody::print(std::ostream& out) const
+{
+ out << "header (" << encodedSize() << " bytes)";
+ out << "; properties={";
+ properties.print(out);
+ out << "}";
+}
+
+void qpid::framing::AMQHeaderBody::accept(AMQBodyConstVisitor& v) const {
+ v.visit(*this);
+}
diff --git a/qpid/cpp/src/qpid/framing/AMQHeaderBody.h b/qpid/cpp/src/qpid/framing/AMQHeaderBody.h
new file mode 100644
index 0000000000..a8c326969a
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQHeaderBody.h
@@ -0,0 +1,109 @@
+#ifndef QPID_FRAMING_AMQHEADERBODY_H
+#define QPID_FRAMING_AMQHEADERBODY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/CommonImportExport.h"
+#include <iostream>
+
+#include <boost/optional.hpp>
+
+
+namespace qpid {
+namespace framing {
+
+class QPID_COMMON_CLASS_EXTERN AMQHeaderBody : public AMQBody
+{
+ template <class T> struct OptProps { boost::optional<T> props; };
+ template <class Base, class T>
+ struct PropSet : public Base, public OptProps<T> {
+ uint32_t encodedSize() const {
+ const boost::optional<T>& p=this->OptProps<T>::props;
+ return (p ? p->encodedSize() : 0) + Base::encodedSize();
+ }
+ void encode(Buffer& buffer) const {
+ const boost::optional<T>& p=this->OptProps<T>::props;
+ if (p) p->encode(buffer);
+ Base::encode(buffer);
+ }
+ bool decode(Buffer& buffer, uint32_t size, uint16_t type) {
+ boost::optional<T>& p=this->OptProps<T>::props;
+ if (type == T::TYPE) {
+ p=T();
+ p->decodeStructBody(buffer, size);
+ return true;
+ }
+ else
+ return Base::decode(buffer, size, type);
+ }
+ void print(std::ostream& out) const {
+ const boost::optional<T>& p=this->OptProps<T>::props;
+ if (p) out << *p;
+ Base::print(out);
+ }
+ };
+
+ struct Empty {
+ uint32_t encodedSize() const { return 0; }
+ void encode(Buffer&) const {};
+ bool decode(Buffer&, uint32_t, uint16_t) const { return false; };
+ void print(std::ostream&) const {}
+ };
+
+ // Could use boost::mpl::fold to construct a larger set.
+ typedef PropSet<PropSet<Empty, DeliveryProperties>, MessageProperties> Properties;
+
+ Properties properties;
+
+public:
+
+ inline uint8_t type() const { return HEADER_BODY; }
+
+ QPID_COMMON_EXTERN uint32_t encodedSize() const;
+ QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
+ QPID_COMMON_EXTERN void decode(Buffer& buffer, uint32_t size);
+ QPID_COMMON_EXTERN uint64_t getContentLength() const;
+ QPID_COMMON_EXTERN void print(std::ostream& out) const;
+ QPID_COMMON_EXTERN void accept(AMQBodyConstVisitor&) const;
+
+ template <class T> T* get(bool create) {
+ boost::optional<T>& p=properties.OptProps<T>::props;
+ if (create && !p) p=T();
+ return p.get_ptr();
+ }
+
+ template <class T> const T* get() const {
+ return properties.OptProps<T>::props.get_ptr();
+ }
+
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
+};
+
+}}
+
+
+
+#endif /*!QPID_FRAMING_AMQHEADERBODY_H*/
diff --git a/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp
new file mode 100644
index 0000000000..477616221c
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/AMQHeartbeatBody.h"
+#include <iostream>
+
+qpid::framing::AMQHeartbeatBody::~AMQHeartbeatBody() {}
+
+void qpid::framing::AMQHeartbeatBody::print(std::ostream& out) const {
+ out << "heartbeat";
+}
diff --git a/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h
new file mode 100644
index 0000000000..19ac2be013
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h
@@ -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.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/CommonImportExport.h"
+
+#ifndef _AMQHeartbeatBody_
+#define _AMQHeartbeatBody_
+
+namespace qpid {
+namespace framing {
+
+class QPID_COMMON_CLASS_EXTERN AMQHeartbeatBody : public AMQBody
+{
+public:
+ QPID_COMMON_EXTERN virtual ~AMQHeartbeatBody();
+ inline uint32_t encodedSize() const { return 0; }
+ inline uint8_t type() const { return HEARTBEAT_BODY; }
+ inline void encode(Buffer& ) const {}
+ inline void decode(Buffer& , uint32_t /*size*/) {}
+ QPID_COMMON_EXTERN virtual void print(std::ostream& out) const;
+ void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
+};
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp b/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp
new file mode 100644
index 0000000000..594af4c6dc
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AMQMethodBody.h"
+
+namespace qpid {
+namespace framing {
+
+AMQMethodBody::~AMQMethodBody() {}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/AMQMethodBody.h b/qpid/cpp/src/qpid/framing/AMQMethodBody.h
new file mode 100644
index 0000000000..c634180712
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQMethodBody.h
@@ -0,0 +1,72 @@
+#ifndef _AMQMethodBody_
+#define _AMQMethodBody_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/CommonImportExport.h"
+
+#include <boost/shared_ptr.hpp>
+#include <ostream>
+#include <assert.h>
+
+namespace qpid {
+namespace framing {
+
+class Buffer;
+class AMQP_ServerOperations;
+class MethodBodyConstVisitor;
+
+class AMQMethodBody : public AMQBody {
+ public:
+ AMQMethodBody() {}
+ QPID_COMMON_EXTERN virtual ~AMQMethodBody();
+
+ virtual void accept(MethodBodyConstVisitor&) const = 0;
+
+ virtual MethodId amqpMethodId() const = 0;
+ virtual ClassId amqpClassId() const = 0;
+ virtual bool isContentBearing() const = 0;
+ virtual bool resultExpected() const = 0;
+ virtual bool responseExpected() const = 0;
+
+ template <class T> bool isA() const {
+ return amqpClassId()==T::CLASS_ID && amqpMethodId()==T::METHOD_ID;
+ }
+
+ virtual uint32_t encodedSize() const = 0;
+ virtual uint8_t type() const { return METHOD_BODY; }
+
+ virtual bool isSync() const { return false; /*only ModelMethods can have the sync flag set*/ }
+ virtual void setSync(bool) const { /*only ModelMethods can have the sync flag set*/ }
+
+ AMQMethodBody* getMethod() { return this; }
+ const AMQMethodBody* getMethod() const { return this; }
+ void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+};
+
+
+}} // namespace qpid::framing
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h b/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h
new file mode 100644
index 0000000000..42139c7937
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h
@@ -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.
+ *
+ */
+
+/*
+ * This file used to be auto-generated by Qpid Gentools v.0.1
+ * its here temporarily until we get a full solution to multi-version support
+ */
+#ifndef qpid_framing_highestProtocolVersion__
+#define qpid_framing_highestProtocolVersion__
+
+#include "qpid/framing/ProtocolVersion.h"
+
+
+namespace qpid {
+namespace framing {
+
+static ProtocolVersion highestProtocolVersion(0, 10);
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp b/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp
new file mode 100644
index 0000000000..2e6433a82f
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AccumulatedAck.h"
+
+#include <assert.h>
+#include <iostream>
+#include <boost/bind.hpp>
+
+using std::list;
+using std::max;
+using std::min;
+using namespace qpid::framing;
+
+AccumulatedAck::AccumulatedAck(SequenceNumber r) : mark(r) {}
+
+void AccumulatedAck::update(SequenceNumber first, SequenceNumber last){
+ assert(first <= last);
+ if (last < mark) return;
+
+
+ Range r(first, last);
+ bool handled = false;
+ bool markMerged = false;
+ list<Range>::iterator merged = ranges.end();
+ if (r.mergeable(mark)) {
+ mark = r.end;
+ markMerged = true;
+ handled = true;
+ } else {
+ for (list<Range>::iterator i = ranges.begin(); i != ranges.end() && !handled; i++) {
+ if (i->merge(r)) {
+ merged = i;
+ handled = true;
+ } else if (r.start < i->start) {
+ ranges.insert(i, r);
+ handled = true;
+ }
+ }
+ }
+ if (!handled) {
+ ranges.push_back(r);
+ } else {
+ while (!ranges.empty() && ranges.front().end <= mark) {
+ ranges.pop_front();
+ }
+ if (markMerged) {
+ //new range is incorporated, but may be possible to consolidate
+ merged = ranges.begin();
+ while (merged != ranges.end() && merged->mergeable(mark)) {
+ mark = merged->end;
+ merged = ranges.erase(merged);
+ }
+ }
+ if (merged != ranges.end()) {
+ //consolidate ranges
+ list<Range>::iterator i = merged;
+ list<Range>::iterator j = i++;
+ while (i != ranges.end() && j->merge(*i)) {
+ j = i++;
+ }
+ }
+ }
+}
+
+void AccumulatedAck::consolidate(){}
+
+void AccumulatedAck::clear(){
+ mark = SequenceNumber(0);//not sure that this is valid when wraparound is a possibility
+ ranges.clear();
+}
+
+bool AccumulatedAck::covers(SequenceNumber tag) const{
+ if (tag <= mark) return true;
+ for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) {
+ if (i->contains(tag)) return true;
+ }
+ return false;
+}
+
+void AccumulatedAck::collectRanges(SequenceNumberSet& set) const
+{
+ for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) {
+ set.push_back(i->start);
+ set.push_back(i->end);
+ }
+}
+
+void AccumulatedAck::update(const SequenceNumber cumulative, const SequenceNumberSet& range)
+{
+ update(mark, cumulative);
+ range.processRanges(*this);
+}
+
+
+bool Range::contains(SequenceNumber i) const
+{
+ return i >= start && i <= end;
+}
+
+bool Range::intersect(const Range& r) const
+{
+ return r.contains(start) || r.contains(end) || contains(r.start) || contains(r.end);
+}
+
+bool Range::merge(const Range& r)
+{
+ if (intersect(r) || mergeable(r.end) || r.mergeable(end)) {
+ start = min(start, r.start);
+ end = max(end, r.end);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Range::mergeable(const SequenceNumber& s) const
+{
+ if (contains(s) || start - s == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Range::Range(SequenceNumber s, SequenceNumber e) : start(s), end(e) {}
+
+
+namespace qpid{
+namespace framing{
+ std::ostream& operator<<(std::ostream& out, const Range& r)
+ {
+ out << "[" << r.start.getValue() << "-" << r.end.getValue() << "]";
+ return out;
+ }
+
+ std::ostream& operator<<(std::ostream& out, const AccumulatedAck& a)
+ {
+ out << "{mark: " << a.mark.getValue() << ", ranges: (";
+ for (list<Range>::const_iterator i = a.ranges.begin(); i != a.ranges.end(); i++) {
+ if (i != a.ranges.begin()) out << ", ";
+ out << *i;
+ }
+ out << ")]";
+ return out;
+ }
+}}
diff --git a/qpid/cpp/src/qpid/framing/AccumulatedAck.h b/qpid/cpp/src/qpid/framing/AccumulatedAck.h
new file mode 100644
index 0000000000..8e241b4ba1
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/AccumulatedAck.h
@@ -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.
+ *
+ */
+#ifndef _AccumulatedAck_
+#define _AccumulatedAck_
+
+#include <algorithm>
+#include <functional>
+#include <list>
+#include <ostream>
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceNumberSet.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+ namespace framing {
+
+ struct Range
+ {
+ SequenceNumber start;
+ SequenceNumber end;
+
+ Range(SequenceNumber s, SequenceNumber e);
+ bool contains(SequenceNumber i) const;
+ bool intersect(const Range& r) const;
+ bool merge(const Range& r);
+ bool mergeable(const SequenceNumber& r) const;
+ };
+ /**
+ * Keeps an accumulated record of acknowledged messages (by delivery
+ * tag).
+ */
+ class AccumulatedAck {
+ public:
+ /**
+ * Everything up to this value has been acknowledged.
+ */
+ SequenceNumber mark;
+ /**
+ * List of individually acknowledged messages greater than the
+ * 'mark'.
+ */
+ std::list<Range> ranges;
+
+ QPID_COMMON_EXTERN explicit AccumulatedAck(SequenceNumber r = SequenceNumber());
+ QPID_COMMON_EXTERN void update(SequenceNumber firstTag, SequenceNumber lastTag);
+ QPID_COMMON_EXTERN void consolidate();
+ QPID_COMMON_EXTERN void clear();
+ QPID_COMMON_EXTERN bool covers(SequenceNumber tag) const;
+ void collectRanges(SequenceNumberSet& set) const;
+ QPID_COMMON_EXTERN void update(const SequenceNumber cumulative, const SequenceNumberSet& range);
+ void operator()(SequenceNumber first, SequenceNumber last) { update(first, last); }
+ };
+ QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const Range&);
+ QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const AccumulatedAck&);
+ }
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/Array.cpp b/qpid/cpp/src/qpid/framing/Array.cpp
new file mode 100644
index 0000000000..454e8e298f
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Array.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+#include <assert.h>
+
+namespace qpid {
+namespace framing {
+
+Array::Array() : type(TYPE_CODE_VOID) {}
+
+Array::Array(TypeCode t) : type(t) {}
+
+Array::Array(uint8_t t) : type(typeCode(t)) {}
+
+Array::Array(const std::vector<std::string>& in)
+{
+ type = TYPE_CODE_STR16;
+ for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) {
+ ValuePtr value(new Str16Value(*i));
+ values.push_back(value);
+ }
+}
+
+uint32_t Array::encodedSize() const {
+ //note: size is only included when used as a 'top level' type
+ uint32_t len(4/*size*/ + 1/*type*/ + 4/*count*/);
+ for(ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) {
+ len += (*i)->getData().encodedSize();
+ }
+ return len;
+}
+
+int Array::count() const {
+ return values.size();
+}
+
+std::ostream& operator<<(std::ostream& out, const Array& a) {
+ out << typeName(a.getType()) << "{";
+ for(Array::ValueVector::const_iterator i = a.values.begin(); i != a.values.end(); ++i) {
+ if (i != a.values.begin()) out << ", ";
+ (*i)->print(out);
+ }
+ return out << "}";
+}
+
+void Array::encode(Buffer& buffer) const{
+ buffer.putLong(encodedSize() - 4);//size added only when array is a top-level type
+ buffer.putOctet(type);
+ buffer.putLong(count());
+ for (ValueVector::const_iterator i = values.begin(); i!=values.end(); ++i) {
+ (*i)->getData().encode(buffer);
+ }
+}
+
+void Array::decode(Buffer& buffer){
+ values.clear();
+ uint32_t size = buffer.getLong();//size added only when array is a top-level type
+ uint32_t available = buffer.available();
+ if (available < size) {
+ throw IllegalArgumentException(QPID_MSG("Not enough data for array, expected "
+ << size << " bytes but only " << available << " available"));
+ }
+ if (size) {
+ type = TypeCode(buffer.getOctet());
+ uint32_t count = buffer.getLong();
+
+ FieldValue dummy;
+ dummy.setType(type);
+ available = buffer.available();
+ if (available < count * dummy.getData().encodedSize()) {
+ throw IllegalArgumentException(QPID_MSG("Not enough data for array, expected "
+ << count << " items of " << dummy.getData().encodedSize()
+ << " bytes each but only " << available << " bytes available"));
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ ValuePtr value(new FieldValue);
+ value->setType(type);
+ value->getData().decode(buffer);
+ values.push_back(ValuePtr(value));
+ }
+ }
+}
+
+
+bool Array::operator==(const Array& x) const {
+ if (type != x.type) return false;
+ if (values.size() != x.values.size()) return false;
+
+ for (ValueVector::const_iterator i = values.begin(), j = x.values.begin(); i != values.end(); ++i, ++j) {
+ if (*(i->get()) != *(j->get())) return false;
+ }
+
+ return true;
+}
+
+void Array::insert(iterator i, ValuePtr value) {
+ if (type != value->getType()) {
+ // FIXME aconway 2008-10-31: put meaningful strings in this message.
+ throw Exception(QPID_MSG("Wrong type of value in Array, expected " << type
+ << " but found " << TypeCode(value->getType())));
+ }
+ values.insert(i, value);
+}
+
+
+}
+}
diff --git a/qpid/cpp/src/qpid/framing/Blob.cpp b/qpid/cpp/src/qpid/framing/Blob.cpp
new file mode 100644
index 0000000000..0c8316f3d2
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Blob.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/Blob.h"
+
+
+namespace qpid {
+namespace framing {
+
+void BlobHelper<void>::destroy(void*) {}
+
+void BlobHelper<void>::copy(void*, const void*) {}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/Blob.h b/qpid/cpp/src/qpid/framing/Blob.h
new file mode 100644
index 0000000000..9878d92fe4
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Blob.h
@@ -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.
+ *
+ */
+
diff --git a/qpid/cpp/src/qpid/framing/BodyFactory.h b/qpid/cpp/src/qpid/framing/BodyFactory.h
new file mode 100644
index 0000000000..6a8d9b1988
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/BodyFactory.h
@@ -0,0 +1,47 @@
+#ifndef QPID_FRAMING_BODYFACTORY_H
+#define QPID_FRAMING_BODYFACTORY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Indirect creation of body types to allow centralized changes to
+ * memory management strategy.
+ */
+class BodyFactory {
+ public:
+ template <class BodyType> static boost::intrusive_ptr<BodyType> create() {
+ return new BodyType;
+ }
+
+ template <class BodyType> static boost::intrusive_ptr<BodyType> copy(const BodyType& body) {
+ return new BodyType(body);
+ }
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_BODYFACTORY_H*/
diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.cpp b/qpid/cpp/src/qpid/framing/BodyHandler.cpp
new file mode 100644
index 0000000000..db302b1e4c
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/BodyHandler.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/BodyHandler.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
+#include <boost/cast.hpp>
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+
+using namespace qpid::framing;
+using namespace boost;
+
+BodyHandler::~BodyHandler() {}
+
+// TODO aconway 2007-08-13: Replace with visitor.
+void BodyHandler::handleBody(AMQBody* body) {
+ switch(body->type())
+ {
+ case METHOD_BODY:
+ handleMethod(polymorphic_downcast<AMQMethodBody*>(body));
+ break;
+ case HEADER_BODY:
+ handleHeader(polymorphic_downcast<AMQHeaderBody*>(body));
+ break;
+ case CONTENT_BODY:
+ handleContent(polymorphic_downcast<AMQContentBody*>(body));
+ break;
+ case HEARTBEAT_BODY:
+ handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body));
+ break;
+ default:
+ throw FramingErrorException(
+ QPID_MSG("Invalid frame type " << body->type()));
+ }
+}
+
diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.h b/qpid/cpp/src/qpid/framing/BodyHandler.h
new file mode 100644
index 0000000000..9ded737195
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/BodyHandler.h
@@ -0,0 +1,56 @@
+#ifndef _BodyHandler_
+#define _BodyHandler_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace framing {
+class AMQBody;
+class AMQMethodBody;
+class AMQHeaderBody;
+class AMQContentBody;
+class AMQHeartbeatBody;
+
+// TODO aconway 2007-08-10: rework using Visitor pattern?
+
+/**
+ * Interface to handle incoming frame bodies.
+ * Derived classes provide logic for each frame type.
+ */
+class BodyHandler {
+ public:
+ virtual ~BodyHandler();
+ virtual void handleBody(AMQBody* body);
+
+ protected:
+ virtual void handleMethod(AMQMethodBody*) = 0;
+ virtual void handleHeader(AMQHeaderBody*) = 0;
+ virtual void handleContent(AMQContentBody*) = 0;
+ virtual void handleHeartbeat(AMQHeartbeatBody*) = 0;
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/Buffer.cpp b/qpid/cpp/src/qpid/framing/Buffer.cpp
new file mode 100644
index 0000000000..5a5bc0325e
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Buffer.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/Msg.h"
+#include <string.h>
+#include <boost/format.hpp>
+namespace qpid {
+
+namespace framing {
+
+Buffer::Buffer(char* _data, uint32_t _size)
+ : size(_size), data(_data), position(0) {
+}
+
+void Buffer::record(){
+ r_position = position;
+}
+
+void Buffer::restore(bool reRecord){
+ uint32_t savedPosition = position;
+
+ position = r_position;
+
+ if (reRecord)
+ r_position = savedPosition;
+}
+
+void Buffer::reset(){
+ position = 0;
+}
+
+///////////////////////////////////////////////////
+
+void Buffer::putOctet(uint8_t i){
+ data[position++] = i;
+ assert(position <= size);
+}
+
+void Buffer::putShort(uint16_t i){
+ uint16_t b = i;
+ data[position++] = (uint8_t) (0xFF & (b >> 8));
+ data[position++] = (uint8_t) (0xFF & b);
+ assert(position <= size);
+}
+
+void Buffer::putLong(uint32_t i){
+ uint32_t b = i;
+ data[position++] = (uint8_t) (0xFF & (b >> 24));
+ data[position++] = (uint8_t) (0xFF & (b >> 16));
+ data[position++] = (uint8_t) (0xFF & (b >> 8));
+ data[position++] = (uint8_t) (0xFF & b);
+ assert(position <= size);
+}
+
+void Buffer::putLongLong(uint64_t i){
+ uint32_t hi = i >> 32;
+ uint32_t lo = i;
+ putLong(hi);
+ putLong(lo);
+}
+
+void Buffer::putInt8(int8_t i){
+ data[position++] = (uint8_t) i;
+ assert(position <= size);
+}
+
+void Buffer::putInt16(int16_t i){
+ putShort((uint16_t) i);
+}
+
+void Buffer::putInt32(int32_t i){
+ putLong((uint32_t) i);
+}
+
+void Buffer::putInt64(int64_t i){
+ putLongLong((uint64_t) i);
+}
+
+void Buffer::putFloat(float f){
+ union {
+ uint32_t i;
+ float f;
+ } val;
+
+ val.f = f;
+ putLong (val.i);
+}
+
+void Buffer::putDouble(double f){
+ union {
+ uint64_t i;
+ double f;
+ } val;
+
+ val.f = f;
+ putLongLong (val.i);
+}
+
+void Buffer::putBin128(const uint8_t* b){
+ memcpy (data + position, b, 16);
+ position += 16;
+}
+
+uint8_t Buffer::getOctet(){
+ uint8_t octet = static_cast<uint8_t>(data[position++]);
+ assert(position <= size);
+ return octet;
+}
+
+uint16_t Buffer::getShort(){
+ uint16_t hi = (unsigned char) data[position++];
+ hi = hi << 8;
+ hi |= (unsigned char) data[position++];
+ assert(position <= size);
+ return hi;
+}
+
+uint32_t Buffer::getLong(){
+ uint32_t a = (unsigned char) data[position++];
+ uint32_t b = (unsigned char) data[position++];
+ uint32_t c = (unsigned char) data[position++];
+ uint32_t d = (unsigned char) data[position++];
+ assert(position <= size);
+ a = a << 24;
+ a |= b << 16;
+ a |= c << 8;
+ a |= d;
+ return a;
+}
+
+uint64_t Buffer::getLongLong(){
+ uint64_t hi = getLong();
+ uint64_t lo = getLong();
+ hi = hi << 32;
+ return hi | lo;
+}
+
+int8_t Buffer::getInt8(){
+ int8_t i = static_cast<int8_t>(data[position++]);
+ assert(position <= size);
+ return i;
+}
+
+int16_t Buffer::getInt16(){
+ return (int16_t) getShort();
+}
+
+int32_t Buffer::getInt32(){
+ return (int32_t) getLong();
+}
+
+int64_t Buffer::getInt64(){
+ return (int64_t) getLongLong();
+}
+
+float Buffer::getFloat(){
+ union {
+ uint32_t i;
+ float f;
+ } val;
+ val.i = getLong();
+ return val.f;
+}
+
+double Buffer::getDouble(){
+ union {
+ uint64_t i;
+ double f;
+ } val;
+ val.i = getLongLong();
+ return val.f;
+}
+
+template <>
+uint64_t Buffer::getUInt<1>() {
+ return getOctet();
+}
+
+template <>
+uint64_t Buffer::getUInt<2>() {
+ return getShort();
+}
+
+template <>
+uint64_t Buffer::getUInt<4>() {
+ return getLong();
+}
+
+template <>
+uint64_t Buffer::getUInt<8>() {
+ return getLongLong();
+}
+
+template <>
+void Buffer::putUInt<1>(uint64_t i) {
+ if (std::numeric_limits<uint8_t>::min() <= i && i <= std::numeric_limits<uint8_t>::max()) {
+ putOctet(i);
+ return;
+ }
+ throw Exception(QPID_MSG("Could not encode (" << i << ") as uint8_t."));
+}
+
+template <>
+void Buffer::putUInt<2>(uint64_t i) {
+ if (std::numeric_limits<uint16_t>::min() <= i && i <= std::numeric_limits<uint16_t>::max()) {
+ putShort(i);
+ return;
+ }
+ throw Exception(QPID_MSG("Could not encode (" << i << ") as uint16_t."));
+}
+
+template <>
+void Buffer::putUInt<4>(uint64_t i) {
+ if (std::numeric_limits<uint32_t>::min() <= i && i <= std::numeric_limits<uint32_t>::max()) {
+ putLong(i);
+ return;
+ }
+ throw Exception(QPID_MSG("Could not encode (" << i << ") as uint32_t."));
+}
+
+template <>
+void Buffer::putUInt<8>(uint64_t i) {
+ putLongLong(i);
+}
+
+void Buffer::putShortString(const string& s){
+ size_t slen = s.length();
+ if (slen <= std::numeric_limits<uint8_t>::max()) {
+ uint8_t len = (uint8_t) slen;
+ checkAvailable(slen + 1);
+ putOctet(len);
+ s.copy(data + position, len);
+ position += len;
+ return;
+ }
+ throw Exception(QPID_MSG("Could not encode string of " << slen << " bytes as uint8_t string."));
+}
+
+void Buffer::putMediumString(const string& s){
+ size_t slen = s.length();
+ if (slen <= std::numeric_limits<uint16_t>::max()) {
+ uint16_t len = (uint16_t) slen;
+ checkAvailable(slen + 2);
+ putShort(len);
+ s.copy(data + position, len);
+ position += len;
+ return;
+ }
+ throw Exception(QPID_MSG("Could not encode string of " << slen << " bytes as uint16_t string."));
+}
+
+void Buffer::putLongString(const string& s){
+ uint32_t len = s.length();
+ checkAvailable(len + 4);
+ putLong(len);
+ s.copy(data + position, len);
+ position += len;
+}
+
+void Buffer::getShortString(string& s){
+ uint8_t len = getOctet();
+ checkAvailable(len);
+ s.assign(data + position, len);
+ position += len;
+}
+
+void Buffer::getMediumString(string& s){
+ uint16_t len = getShort();
+ checkAvailable(len);
+ s.assign(data + position, len);
+ position += len;
+}
+
+void Buffer::getLongString(string& s){
+ uint32_t len = getLong();
+ checkAvailable(len);
+ s.assign(data + position, len);
+ position += len;
+}
+
+void Buffer::getBin128(uint8_t* b){
+ memcpy (b, data + position, 16);
+ position += 16;
+}
+
+void Buffer::putRawData(const string& s){
+ uint32_t len = s.length();
+ checkAvailable(len);
+ s.copy(data + position, len);
+ position += len;
+}
+
+void Buffer::getRawData(string& s, uint32_t len){
+ checkAvailable(len);
+ s.assign(data + position, len);
+ position += len;
+}
+
+void Buffer::putRawData(const uint8_t* s, size_t len){
+ checkAvailable(len);
+ memcpy(data + position, s, len);
+ position += len;
+}
+
+void Buffer::getRawData(uint8_t* s, size_t len){
+ checkAvailable(len);
+ memcpy(s, data + position, len);
+ position += len;
+}
+
+void Buffer::dump(std::ostream& out) const {
+ for (uint32_t i = position; i < size; i++)
+ {
+ if (i != position)
+ out << " ";
+ out << boost::format("%02x") % ((unsigned) (uint8_t) data[i]);
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const Buffer& b){
+ out << "Buffer[";
+ b.dump(out);
+ return out << "]";
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/framing/ChannelHandler.h b/qpid/cpp/src/qpid/framing/ChannelHandler.h
new file mode 100644
index 0000000000..ddab204578
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ChannelHandler.h
@@ -0,0 +1,53 @@
+#ifndef QPID_FRAMING_CHANNELHANDLER_H
+#define QPID_FRAMING_CHANNELHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/AMQFrame.h"
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Sets the channel number on outgoing frames.
+ */
+class ChannelHandler : public FrameHandler
+{
+ public:
+ ChannelHandler(uint16_t channelId=0, FrameHandler* next=0)
+ : FrameHandler(next), channel(channelId) {}
+ void handle(AMQFrame& frame) {
+ frame.setChannel(channel);
+ next->handle(frame);
+ }
+ uint16_t get() const { return channel; }
+ ChannelHandler& set(uint16_t ch) { channel=ch; return *this; }
+ operator uint16_t() const { return get(); }
+ ChannelHandler& operator=(uint16_t ch) { return set(ch); }
+
+ private:
+ uint16_t channel;
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_CHANNELHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/framing/Endian.cpp b/qpid/cpp/src/qpid/framing/Endian.cpp
new file mode 100644
index 0000000000..5acc3c459f
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Endian.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/Endian.h"
+
+namespace qpid {
+namespace framing {
+
+Endian::Endian() : littleEndian(!testBigEndian()) {}
+
+bool Endian::testBigEndian()
+{
+ uint16_t a = 1;
+ uint16_t b;
+ uint8_t* p = (uint8_t*) &b;
+ p[0] = 0xFF & (a >> 8);
+ p[1] = 0xFF & (a);
+ return a == b;
+}
+
+uint8_t* Endian::convertIfRequired(uint8_t* const octets, int width)
+{
+ if (instance.littleEndian) {
+ for (int i = 0; i < (width/2); i++) {
+ uint8_t temp = octets[i];
+ octets[i] = octets[width - (1 + i)];
+ octets[width - (1 + i)] = temp;
+ }
+ }
+ return octets;
+}
+
+const Endian Endian::instance;
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/Endian.h b/qpid/cpp/src/qpid/framing/Endian.h
new file mode 100644
index 0000000000..077d5a3e9b
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Endian.h
@@ -0,0 +1,46 @@
+#ifndef QPID_FRAMING_ENDIAN_H
+#define QPID_FRAMING_ENDIAN_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Conversion utility for little-endian platforms that need to convert
+ * to and from network ordered octet sequences
+ */
+class Endian
+{
+ public:
+ static uint8_t* convertIfRequired(uint8_t* const octets, int width);
+ private:
+ const bool littleEndian;
+ Endian();
+ static const Endian instance;
+ static bool testBigEndian();
+};
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_ENDIAN_H*/
diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp
new file mode 100644
index 0000000000..21eaea0f4d
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Endian.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+#include <assert.h>
+
+namespace qpid {
+namespace framing {
+
+FieldTable::FieldTable(const FieldTable& ft)
+{
+ *this = ft;
+}
+
+FieldTable& FieldTable::operator=(const FieldTable& ft)
+{
+ clear();
+ values = ft.values;
+ return *this;
+}
+
+FieldTable::~FieldTable() {}
+
+uint32_t FieldTable::encodedSize() const {
+ uint32_t len(4/*size field*/ + 4/*count field*/);
+ for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) {
+ // shortstr_len_byte + key size + value size
+ len += 1 + (i->first).size() + (i->second)->encodedSize();
+ }
+ return len;
+}
+
+int FieldTable::count() const {
+ return values.size();
+}
+
+namespace
+{
+std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_type& i) {
+ return out << i.first << ":" << *i.second;
+}
+}
+
+std::ostream& operator<<(std::ostream& out, const FieldTable& t) {
+ out << "{";
+ FieldTable::ValueMap::const_iterator i = t.begin();
+ if (i != t.end()) out << *i++;
+ while (i != t.end())
+ {
+ out << "," << *i++;
+ }
+ return out << "}";
+}
+
+void FieldTable::set(const std::string& name, const ValuePtr& value){
+ values[name] = value;
+}
+
+void FieldTable::setString(const std::string& name, const std::string& value){
+ values[name] = ValuePtr(new Str16Value(value));
+}
+
+void FieldTable::setInt(const std::string& name, const int value){
+ values[name] = ValuePtr(new IntegerValue(value));
+}
+
+void FieldTable::setInt64(const std::string& name, const int64_t value){
+ values[name] = ValuePtr(new Integer64Value(value));
+}
+
+void FieldTable::setTimestamp(const std::string& name, const uint64_t value){
+ values[name] = ValuePtr(new TimeValue(value));
+}
+
+void FieldTable::setUInt64(const std::string& name, const uint64_t value){
+ values[name] = ValuePtr(new Unsigned64Value(value));
+}
+
+void FieldTable::setTable(const std::string& name, const FieldTable& value)
+{
+ values[name] = ValuePtr(new FieldTableValue(value));
+}
+void FieldTable::setArray(const std::string& name, const Array& value)
+{
+ values[name] = ValuePtr(new ArrayValue(value));
+}
+
+void FieldTable::setFloat(const std::string& name, const float value){
+ values[name] = ValuePtr(new FloatValue(value));
+}
+
+void FieldTable::setDouble(const std::string& name, double value){
+ values[name] = ValuePtr(new DoubleValue(value));
+}
+
+FieldTable::ValuePtr FieldTable::get(const std::string& name) const
+{
+ ValuePtr value;
+ ValueMap::const_iterator i = values.find(name);
+ if ( i!=values.end() )
+ value = i->second;
+ return value;
+}
+
+namespace {
+ template <class T> T default_value() { return T(); }
+ template <> int default_value<int>() { return 0; }
+ //template <> uint64_t default_value<uint64_t>() { return 0; }
+}
+
+template <class T>
+T getValue(const FieldTable::ValuePtr value)
+{
+ if (!value || !value->convertsTo<T>())
+ return default_value<T>();
+
+ return value->get<T>();
+}
+
+std::string FieldTable::getAsString(const std::string& name) const {
+ return getValue<std::string>(get(name));
+}
+
+int FieldTable::getAsInt(const std::string& name) const {
+ return getValue<int>(get(name));
+}
+
+uint64_t FieldTable::getAsUInt64(const std::string& name) const {
+ return static_cast<uint64_t>( getValue<int64_t>(get(name)));
+}
+
+int64_t FieldTable::getAsInt64(const std::string& name) const {
+ return getValue<int64_t>(get(name));
+}
+
+bool FieldTable::getTable(const std::string& name, FieldTable& value) const {
+ return getEncodedValue<FieldTable>(get(name), value);
+}
+
+bool FieldTable::getArray(const std::string& name, Array& value) const {
+ return getEncodedValue<Array>(get(name), value);
+}
+
+template <class T, int width, uint8_t typecode>
+bool getRawFixedWidthValue(FieldTable::ValuePtr vptr, T& value)
+{
+ if (vptr && vptr->getType() == typecode) {
+ value = vptr->get<T>();
+ return true;
+ }
+ return false;
+}
+
+bool FieldTable::getFloat(const std::string& name, float& value) const {
+ return getRawFixedWidthValue<float, 4, 0x23>(get(name), value);
+}
+
+bool FieldTable::getDouble(const std::string& name, double& value) const {
+ return getRawFixedWidthValue<double, 8, 0x33>(get(name), value);
+}
+
+//uint64_t FieldTable::getTimestamp(const std::string& name) const {
+// return getValue<uint64_t>(name);
+//}
+
+void FieldTable::encode(Buffer& buffer) const {
+ buffer.putLong(encodedSize() - 4);
+ buffer.putLong(values.size());
+ for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) {
+ buffer.putShortString(i->first);
+ i->second->encode(buffer);
+ }
+}
+
+void FieldTable::decode(Buffer& buffer){
+ clear();
+ uint32_t len = buffer.getLong();
+ if (len) {
+ uint32_t available = buffer.available();
+ if (available < len)
+ throw IllegalArgumentException(QPID_MSG("Not enough data for field table."));
+ uint32_t count = buffer.getLong();
+ uint32_t leftover = available - len;
+ while(buffer.available() > leftover && count--){
+ std::string name;
+ ValuePtr value(new FieldValue);
+
+ buffer.getShortString(name);
+ value->decode(buffer);
+ values[name] = ValuePtr(value);
+ }
+ }
+}
+
+bool FieldTable::operator==(const FieldTable& x) const {
+ if (values.size() != x.values.size()) return false;
+ for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) {
+ ValueMap::const_iterator j = x.values.find(i->first);
+ if (j == x.values.end()) return false;
+ if (*(i->second) != *(j->second)) return false;
+ }
+ return true;
+}
+
+void FieldTable::erase(const std::string& name)
+{
+ if (values.find(name) != values.end())
+ values.erase(name);
+}
+
+std::pair<FieldTable::ValueMap::iterator, bool> FieldTable::insert(const ValueMap::value_type& value)
+{
+ return values.insert(value);
+}
+
+FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value)
+{
+ return values.insert(position, value);
+}
+
+
+}
+}
diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp
new file mode 100644
index 0000000000..ce5a50117c
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Endian.h"
+#include "qpid/framing/List.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+
+namespace qpid {
+namespace framing {
+
+uint8_t FieldValue::getType() const
+{
+ return typeOctet;
+}
+
+void FieldValue::setType(uint8_t type)
+{
+ typeOctet = type;
+ if (typeOctet == 0xA8) {
+ data.reset(new EncodedValue<FieldTable>());
+ } else if (typeOctet == 0xA9) {
+ data.reset(new EncodedValue<List>());
+ } else if (typeOctet == 0xAA) {
+ data.reset(new EncodedValue<Array>());
+ } else {
+ uint8_t lenType = typeOctet >> 4;
+ switch(lenType){
+ case 0:
+ data.reset(new FixedWidthValue<1>());
+ break;
+ case 1:
+ data.reset(new FixedWidthValue<2>());
+ break;
+ case 2:
+ data.reset(new FixedWidthValue<4>());
+ break;
+ case 3:
+ data.reset(new FixedWidthValue<8>());
+ break;
+ case 4:
+ data.reset(new FixedWidthValue<16>());
+ break;
+ case 5:
+ data.reset(new FixedWidthValue<32>());
+ break;
+ case 6:
+ data.reset(new FixedWidthValue<64>());
+ break;
+ case 7:
+ data.reset(new FixedWidthValue<128>());
+ break;
+ case 8:
+ data.reset(new VariableWidthValue<1>());
+ break;
+ case 9:
+ data.reset(new VariableWidthValue<2>());
+ break;
+ case 0xA:
+ data.reset(new VariableWidthValue<4>());
+ break;
+ case 0xC:
+ data.reset(new FixedWidthValue<5>());
+ break;
+ case 0xD:
+ data.reset(new FixedWidthValue<9>());
+ break;
+ case 0xF:
+ data.reset(new FixedWidthValue<0>());
+ break;
+ default:
+ throw IllegalArgumentException(QPID_MSG("Unknown field table value type: " << (int)typeOctet));
+ }
+ }
+}
+
+void FieldValue::decode(Buffer& buffer)
+{
+ setType(buffer.getOctet());
+ data->decode(buffer);
+}
+
+void FieldValue::encode(Buffer& buffer)
+{
+ buffer.putOctet(typeOctet);
+ data->encode(buffer);
+}
+
+bool FieldValue::operator==(const FieldValue& v) const
+{
+ return
+ typeOctet == v.typeOctet &&
+ *data == *v.data;
+}
+
+Str8Value::Str8Value(const std::string& v) :
+ FieldValue(
+ TYPE_CODE_STR8,
+ new VariableWidthValue<1>(
+ reinterpret_cast<const uint8_t*>(v.data()),
+ reinterpret_cast<const uint8_t*>(v.data()+v.size())))
+{
+}
+
+Str16Value::Str16Value(const std::string& v) :
+ FieldValue(
+ 0x95,
+ new VariableWidthValue<2>(
+ reinterpret_cast<const uint8_t*>(v.data()),
+ reinterpret_cast<const uint8_t*>(v.data()+v.size())))
+{}
+
+Var16Value::Var16Value(const std::string& v, uint8_t code) :
+ FieldValue(
+ code,
+ new VariableWidthValue<2>(
+ reinterpret_cast<const uint8_t*>(v.data()),
+ reinterpret_cast<const uint8_t*>(v.data()+v.size())))
+{}
+Var32Value::Var32Value(const std::string& v, uint8_t code) :
+ FieldValue(
+ code,
+ new VariableWidthValue<4>(
+ reinterpret_cast<const uint8_t*>(v.data()),
+ reinterpret_cast<const uint8_t*>(v.data()+v.size())))
+{}
+
+Struct32Value::Struct32Value(const std::string& v) :
+ FieldValue(
+ 0xAB,
+ new VariableWidthValue<4>(
+ reinterpret_cast<const uint8_t*>(v.data()),
+ reinterpret_cast<const uint8_t*>(v.data()+v.size())))
+{}
+
+IntegerValue::IntegerValue(int v) :
+ FieldValue(0x21, new FixedWidthValue<4>(v))
+{}
+
+FloatValue::FloatValue(float v) :
+ FieldValue(0x23, new FixedWidthValue<4>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 4)))
+{}
+
+DoubleValue::DoubleValue(double v) :
+ FieldValue(0x33, new FixedWidthValue<8>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 8)))
+{}
+
+Integer64Value::Integer64Value(int64_t v) :
+ FieldValue(0x31, new FixedWidthValue<8>(v))
+{}
+
+Unsigned64Value::Unsigned64Value(uint64_t v) :
+ FieldValue(0x32, new FixedWidthValue<8>(v))
+{}
+
+
+TimeValue::TimeValue(uint64_t v) :
+ FieldValue(0x38, new FixedWidthValue<8>(v))
+{
+}
+
+FieldTableValue::FieldTableValue(const FieldTable& f) : FieldValue(0xa8, new EncodedValue<FieldTable>(f))
+{
+}
+
+ListValue::ListValue(const List& l) : FieldValue(0xa9, new EncodedValue<List>(l))
+{
+}
+
+ArrayValue::ArrayValue(const Array& a) : FieldValue(0xaa, new EncodedValue<Array>(a))
+{
+}
+
+VoidValue::VoidValue() : FieldValue(0xf0, new FixedWidthValue<0>()) {}
+
+BoolValue::BoolValue(bool b) :
+ FieldValue(0x08, new FixedWidthValue<1>(b))
+{}
+
+Unsigned8Value::Unsigned8Value(uint8_t v) :
+ FieldValue(0x02, new FixedWidthValue<1>(v))
+{}
+Unsigned16Value::Unsigned16Value(uint16_t v) :
+ FieldValue(0x12, new FixedWidthValue<2>(v))
+{}
+Unsigned32Value::Unsigned32Value(uint32_t v) :
+ FieldValue(0x22, new FixedWidthValue<4>(v))
+{}
+
+Integer8Value::Integer8Value(int8_t v) :
+ FieldValue(0x01, new FixedWidthValue<1>(v))
+{}
+Integer16Value::Integer16Value(int16_t v) :
+ FieldValue(0x11, new FixedWidthValue<2>(v))
+{}
+UuidValue::UuidValue(const unsigned char* v) :
+ FieldValue(0x48, new FixedWidthValue<16>(v))
+{}
+
+void FieldValue::print(std::ostream& out) const {
+ data->print(out);
+ out << TypeCode(typeOctet) << '(';
+ if (data->convertsToString()) out << data->getString();
+ else if (data->convertsToInt()) out << data->getInt();
+ else data->print(out);
+ out << ')';
+}
+
+uint8_t* FieldValue::convertIfRequired(uint8_t* const octets, int width)
+{
+ return Endian::convertIfRequired(octets, width);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/framing/FrameDecoder.cpp b/qpid/cpp/src/qpid/framing/FrameDecoder.cpp
new file mode 100644
index 0000000000..90cbbd84a1
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameDecoder.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/FrameDecoder.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/reply_exceptions.h"
+#include <algorithm>
+#include <string.h>
+
+namespace qpid {
+namespace framing {
+
+namespace {
+/** Append up to n bytes from start of buf to end of bytes. */
+void append(std::vector<char>& bytes, Buffer& buffer, size_t n) {
+ size_t oldSize = bytes.size();
+ if ((n = std::min(n, size_t(buffer.available()))) == 0)
+ return;
+ bytes.resize(oldSize+n);
+ char* p = &bytes[oldSize];
+ buffer.getRawData(reinterpret_cast<uint8_t*>(p), n);
+}
+}
+
+bool FrameDecoder::decode(Buffer& buffer) {
+ if (buffer.available() == 0) return false;
+ if (fragment.empty()) {
+ if (frame.decode(buffer)) // Decode from buffer
+ return true;
+ else // Store fragment
+ append(fragment, buffer, buffer.available());
+ }
+ else { // Already have a fragment
+ // Get enough data to decode the frame size.
+ if (fragment.size() < AMQFrame::DECODE_SIZE_MIN) {
+ append(fragment, buffer, AMQFrame::DECODE_SIZE_MIN - fragment.size());
+ }
+ if (fragment.size() >= AMQFrame::DECODE_SIZE_MIN) {
+ uint16_t size = AMQFrame::decodeSize(&fragment[0]);
+ if (size <= fragment.size())
+ throw FramingErrorException(QPID_MSG("Frame size " << size << " is too small."));
+ append(fragment, buffer, size-fragment.size());
+ Buffer b(&fragment[0], fragment.size());
+ if (frame.decode(b)) {
+ assert(b.available() == 0);
+ fragment.clear();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void FrameDecoder::setFragment(const char* data, size_t size) {
+ fragment.resize(size);
+ ::memcpy(&fragment[0], data, size);
+}
+
+std::pair<const char*, size_t> FrameDecoder::getFragment() const {
+ return std::pair<const char*, size_t>(&fragment[0], fragment.size());
+}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/FrameDecoder.h b/qpid/cpp/src/qpid/framing/FrameDecoder.h
new file mode 100644
index 0000000000..26bed6c447
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameDecoder.h
@@ -0,0 +1,52 @@
+#ifndef QPID_FRAMING_FRAMEDECODER_H
+#define QPID_FRAMING_FRAMEDECODER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Decode a frame from buffer. If buffer does not contain a complete
+ * frame, caches the fragment for the next call to decode.
+ */
+class FrameDecoder
+{
+ public:
+ QPID_COMMON_EXTERN bool decode(Buffer& buffer);
+ const AMQFrame& getFrame() const { return frame; }
+ AMQFrame& getFrame() { return frame; }
+
+ void setFragment(const char*, size_t);
+ std::pair<const char*, size_t> getFragment() const;
+
+ private:
+ std::vector<char> fragment;
+ AMQFrame frame;
+
+};
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_FRAMEDECODER_H*/
diff --git a/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h b/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h
new file mode 100644
index 0000000000..bd676960bf
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h
@@ -0,0 +1,60 @@
+#ifndef QPID_FRAMING_FRAMEVISITOR_H
+#define QPID_FRAMING_FRAMEVISITOR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/MethodBodyDefaultVisitor.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
+
+namespace qpid {
+namespace framing {
+/**
+ * Visitor for all concrete frame body types, which combines
+ * AMQBodyConstVisitor and MethodBodyDefaultVisitor.
+ *
+ * Derived classes can override visit methods to specify actions.
+ * Derived classes must override defaultVisit(), which is called
+ * for any non-overridden visit functions.
+ *
+ */
+struct FrameDefaultVisitor : public AMQBodyConstVisitor,
+ protected MethodBodyDefaultVisitor
+{
+ virtual void defaultVisit(const AMQBody&) = 0;
+ void defaultVisit(const AMQMethodBody& method) { defaultVisit(static_cast<const AMQBody&>(method)); }
+
+ void visit(const AMQHeaderBody& b) { defaultVisit(b); }
+ void visit(const AMQContentBody& b) { defaultVisit(b); }
+ void visit(const AMQHeartbeatBody& b) { defaultVisit(b); }
+ void visit(const AMQMethodBody& b) { b.accept(static_cast<MethodBodyDefaultVisitor&>(*this)); }
+
+ using AMQBodyConstVisitor::visit;
+ using MethodBodyDefaultVisitor::visit;
+};
+
+}} // namespace qpid::framing
+
+
+#endif /*!QPID_FRAMING_FRAMEVISITOR_H*/
diff --git a/qpid/cpp/src/qpid/framing/FrameHandler.h b/qpid/cpp/src/qpid/framing/FrameHandler.h
new file mode 100644
index 0000000000..fa1fb535ef
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameHandler.h
@@ -0,0 +1,33 @@
+#ifndef QPID_FRAMING_FRAMEHANDLER_H
+#define QPID_FRAMING_FRAMEHANDLER_H
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/Handler.h"
+
+namespace qpid {
+namespace framing {
+
+class AMQFrame;
+typedef Handler<AMQFrame&> FrameHandler;
+
+
+}}
+#endif /*!QPID_FRAMING_FRAMEHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/framing/FrameSet.cpp b/qpid/cpp/src/qpid/framing/FrameSet.cpp
new file mode 100644
index 0000000000..255aaf6e6b
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameSet.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/all_method_bodies.h"
+#include "qpid/framing/frame_functors.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/TypeFilter.h"
+
+using namespace qpid::framing;
+using namespace boost;
+
+FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalculateSize(true) { }
+FrameSet::FrameSet(const FrameSet& original) : id(original.id), contentSize(0), recalculateSize(true)
+{
+ for (Frames::const_iterator i = original.begin(); i != original.end(); ++i) {
+ parts.push_back(AMQFrame(*(i->getBody())));
+ parts.back().setFirstSegment(i->isFirstSegment());
+ parts.back().setLastSegment(i->isLastSegment());
+ parts.back().setFirstFrame(i->isFirstFrame());
+ parts.back().setLastFrame(i->isLastFrame());
+ }
+}
+
+void FrameSet::append(const AMQFrame& part)
+{
+ parts.push_back(part);
+ recalculateSize = true;
+}
+
+bool FrameSet::isComplete() const
+{
+ return !parts.empty() && parts.back().getEof() && parts.back().getEos();
+}
+
+bool FrameSet::isContentBearing() const
+{
+ const AMQMethodBody* method = getMethod();
+ return method && method->isContentBearing();
+}
+
+const AMQMethodBody* FrameSet::getMethod() const
+{
+ return parts.empty() ? 0 : parts[0].getMethod();
+}
+
+AMQMethodBody* FrameSet::getMethod()
+{
+ return parts.empty() ? 0 : parts[0].getMethod();
+}
+
+const AMQHeaderBody* FrameSet::getHeaders() const
+{
+ return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>();
+}
+
+AMQHeaderBody* FrameSet::getHeaders()
+{
+ return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>();
+}
+
+uint64_t FrameSet::getContentSize() const
+{
+ if (recalculateSize)
+ {
+ SumBodySize sum;
+ map_if(sum, TypeFilter<CONTENT_BODY>());
+ contentSize = sum.getSize();
+ recalculateSize = false;
+ }
+ return contentSize;
+}
+
+void FrameSet::getContent(std::string& out) const {
+ out.clear();
+ out.reserve(getContentSize());
+ for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) {
+ if (i->getBody()->type() == CONTENT_BODY)
+ out += i->castBody<AMQContentBody>()->getData();
+ }
+}
+
+std::string FrameSet::getContent() const {
+ std::string out;
+ getContent(out);
+ return out;
+}
diff --git a/qpid/cpp/src/qpid/framing/FrameSet.h b/qpid/cpp/src/qpid/framing/FrameSet.h
new file mode 100644
index 0000000000..cae75e5ec8
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/FrameSet.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/InlineVector.h"
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/CommonImportExport.h"
+
+#ifndef _FrameSet_
+#define _FrameSet_
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Collects the frames representing a message.
+ */
+class FrameSet
+{
+ typedef InlineVector<AMQFrame, 4> Frames;
+ const SequenceNumber id;
+ Frames parts;
+ mutable uint64_t contentSize;
+ mutable bool recalculateSize;
+
+public:
+ typedef boost::shared_ptr<FrameSet> shared_ptr;
+
+ QPID_COMMON_EXTERN FrameSet(const SequenceNumber& id);
+ QPID_COMMON_EXTERN FrameSet(const FrameSet&);
+ QPID_COMMON_EXTERN void append(const AMQFrame& part);
+ QPID_COMMON_EXTERN bool isComplete() const;
+
+ QPID_COMMON_EXTERN uint64_t getContentSize() const;
+
+ QPID_COMMON_EXTERN void getContent(std::string&) const;
+ QPID_COMMON_EXTERN std::string getContent() const;
+
+ bool isContentBearing() const;
+
+ QPID_COMMON_EXTERN const AMQMethodBody* getMethod() const;
+ QPID_COMMON_EXTERN AMQMethodBody* getMethod();
+ QPID_COMMON_EXTERN const AMQHeaderBody* getHeaders() const;
+ QPID_COMMON_EXTERN AMQHeaderBody* getHeaders();
+
+ template <class T> bool isA() const {
+ const AMQMethodBody* method = getMethod();
+ return method && method->isA<T>();
+ }
+
+ template <class T> const T* as() const {
+ const AMQMethodBody* method = getMethod();
+ return (method && method->isA<T>()) ? dynamic_cast<const T*>(method) : 0;
+ }
+
+ template <class T> T* as() {
+ AMQMethodBody* method = getMethod();
+ return (method && method->isA<T>()) ? dynamic_cast<T*>(method) : 0;
+ }
+
+ template <class T> const T* getHeaderProperties() const {
+ const AMQHeaderBody* header = getHeaders();
+ return header ? header->get<T>() : 0;
+ }
+
+ Frames::const_iterator begin() const { return parts.begin(); }
+ Frames::const_iterator end() const { return parts.end(); }
+
+ const SequenceNumber& getId() const { return id; }
+
+ template <class P> void remove(P predicate) {
+ parts.erase(std::remove_if(parts.begin(), parts.end(), predicate), parts.end());
+ }
+
+ template <class F> void map(F& functor) {
+ std::for_each(parts.begin(), parts.end(), functor);
+ }
+
+ template <class F> void map(F& functor) const {
+ std::for_each(parts.begin(), parts.end(), functor);
+ }
+
+ template <class F, class P> void map_if(F& functor, P predicate) {
+ for(Frames::iterator i = parts.begin(); i != parts.end(); i++) {
+ if (predicate(*i)) functor(*i);
+ }
+ }
+
+ template <class F, class P> void map_if(F& functor, P predicate) const {
+ for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) {
+ if (predicate(*i)) functor(*i);
+ }
+ }
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/Handler.h b/qpid/cpp/src/qpid/framing/Handler.h
new file mode 100644
index 0000000000..fa8db36f49
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Handler.h
@@ -0,0 +1,101 @@
+#ifndef QPID_FRAMING_HANDLER_H
+#define QPID_FRAMING_HANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/shared_ptr.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <assert.h>
+
+namespace qpid {
+namespace framing {
+
+template <class T>
+struct Handler {
+ typedef T HandledType;
+ typedef void handleFptr(T);
+ typedef void result_type; // Compatible with std/boost functors.
+
+ Handler(Handler<T>* next_=0) : next(next_) {}
+ virtual ~Handler() {}
+ virtual void handle(T) = 0;
+
+ /** Allow functor syntax for calling handle */
+ void operator()(T t) { handle(t); }
+
+
+ /** Pointer to next handler in a linked list. */
+ Handler<T>* next;
+
+ /** Adapt any void(T) functor as a Handler.
+ * Functor<F>(f) will copy f.
+ * Functor<F&>(f) will only take a reference to x.
+ */
+ template <class F> class Functor : public Handler<T> {
+ public:
+ Functor(F f, Handler<T>* next=0) : Handler<T>(next), functor(f) {}
+ void handle(T t) { functor(t); }
+ private:
+ F functor;
+ };
+
+ /** Adapt a member function of X as a Handler.
+ * Only holds a reference to its target, not a copy.
+ */
+ template <class X, void (X::*F)(T)>
+ class MemFunRef : public Handler<T> {
+ public:
+ MemFunRef(X& x, Handler<T>* next=0) : Handler(next), target(&x) {}
+ void handle(T t) { (target->*F)(t); }
+
+ /** Allow calling with -> syntax */
+ MemFunRef* operator->() { return this; }
+
+ private:
+ X* target;
+ };
+
+ /** Interface for a handler that implements a
+ * pair of in/out handle operations.
+ * @see InOutHandler
+ */
+ class InOutHandlerInterface {
+ public:
+ virtual ~InOutHandlerInterface() {}
+ virtual void handleIn(T) = 0;
+ virtual void handleOut(T) = 0;
+ };
+
+ /** Support for implementing an in-out handler pair as a single class.
+ * Overrides handleIn, handleOut functions in a single class.
+ */
+ struct InOutHandler : protected InOutHandlerInterface {
+ InOutHandler(Handler<T>* nextIn=0, Handler<T>* nextOut=0) : in(*this, nextIn), out(*this, nextOut) {}
+ MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleIn> in;
+ MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleOut> out;
+ };
+};
+
+
+
+}}
+#endif /*!QPID_FRAMING_HANDLER_H*/
+//
diff --git a/qpid/cpp/src/qpid/framing/HeaderProperties.h b/qpid/cpp/src/qpid/framing/HeaderProperties.h
new file mode 100644
index 0000000000..8b1828daec
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/HeaderProperties.h
@@ -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.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
+
+#ifndef _HeaderProperties_
+#define _HeaderProperties_
+
+namespace qpid {
+namespace framing {
+
+ class HeaderProperties
+ {
+
+ public:
+ inline virtual ~HeaderProperties(){}
+ virtual uint8_t classId() const = 0;
+ virtual uint32_t encodedSize() const = 0;
+ virtual void encode(Buffer& buffer) const = 0;
+ virtual void decode(Buffer& buffer, uint32_t size) = 0;
+ };
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/InitiationHandler.cpp b/qpid/cpp/src/qpid/framing/InitiationHandler.cpp
new file mode 100644
index 0000000000..7ded505a47
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/InitiationHandler.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/InitiationHandler.h"
+
+qpid::framing::InitiationHandler::~InitiationHandler() {}
diff --git a/qpid/cpp/src/qpid/framing/InitiationHandler.h b/qpid/cpp/src/qpid/framing/InitiationHandler.h
new file mode 100644
index 0000000000..5dfcc6b468
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/InitiationHandler.h
@@ -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.
+ *
+ */
+#include <string>
+
+#ifndef _InitiationHandler_
+#define _InitiationHandler_
+
+#include "qpid/framing/ProtocolInitiation.h"
+
+namespace qpid {
+namespace framing {
+
+ class InitiationHandler{
+ public:
+ virtual ~InitiationHandler();
+ virtual void initiated(const ProtocolInitiation&) = 0;
+ };
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/InputHandler.h b/qpid/cpp/src/qpid/framing/InputHandler.h
new file mode 100644
index 0000000000..3efb23632a
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/InputHandler.h
@@ -0,0 +1,41 @@
+#ifndef _InputHandler_
+#define _InputHandler_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/FrameHandler.h"
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+namespace framing {
+
+// TODO aconway 2007-08-29: Eliminate, replace with FrameHandler.
+class InputHandler : public FrameHandler {
+ public:
+ virtual ~InputHandler() {}
+ virtual void received(AMQFrame&) = 0;
+ void handle(AMQFrame& f) { received(f); }
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/Invoker.h b/qpid/cpp/src/qpid/framing/Invoker.h
new file mode 100644
index 0000000000..4f1cf7c331
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Invoker.h
@@ -0,0 +1,86 @@
+#ifndef QPID_FRAMING_INVOKER_H
+#define QPID_FRAMING_INVOKER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/MethodBodyDefaultVisitor.h"
+#include "qpid/framing/StructHelper.h"
+
+#include <boost/optional.hpp>
+
+namespace qpid {
+namespace framing {
+
+class AMQMethodBody;
+
+/**
+ * Base class for invoker visitors.
+ */
+class Invoker: public MethodBodyDefaultVisitor, protected StructHelper
+{
+ public:
+ struct Result {
+ public:
+ Result() : handled(false) {}
+ const std::string& getResult() const { return result; }
+ bool hasResult() const { return !result.empty(); }
+ bool wasHandled() const { return handled; }
+ operator bool() const { return handled; }
+
+ std::string result;
+ bool handled;
+ };
+
+ void defaultVisit(const AMQMethodBody&) {}
+ Result getResult() const { return result; }
+
+ protected:
+ Result result;
+};
+
+/**
+ * Invoke an invocable object.
+ * Invocable classes must provide a nested type Invoker.
+ */
+template <class Invocable>
+Invoker::Result invoke(Invocable& target, const AMQMethodBody& body) {
+ typename Invocable::Invoker invoker(target);
+ body.accept(invoker);
+ return invoker.getResult();
+}
+
+/**
+ * Invoke an invocable object.
+ * Invocable classes must provide a nested type Invoker.
+ */
+template <class Invocable>
+Invoker::Result invoke(Invocable& target, const AMQBody& body) {
+ typename Invocable::Invoker invoker(target);
+ const AMQMethodBody* method = body.getMethod();
+ if (method)
+ method->accept(invoker);
+ return invoker.getResult();
+}
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_INVOKER_H*/
diff --git a/qpid/cpp/src/qpid/framing/IsInSequenceSet.h b/qpid/cpp/src/qpid/framing/IsInSequenceSet.h
new file mode 100644
index 0000000000..fe10c1b9fa
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/IsInSequenceSet.h
@@ -0,0 +1,51 @@
+#ifndef QPID_FRAMING_ISINSEQUENCESET_H
+#define QPID_FRAMING_ISINSEQUENCESET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/SequenceSet.h"
+
+namespace qpid {
+namespace framing {
+/**
+ * Functor to test whether values are in a sequence set. This is a
+ * stateful functor that requires the values to be supplied in order
+ * and takes advantage of that ordering to avoid multiple scans.
+ */
+class IsInSequenceSet
+{
+ public:
+ IsInSequenceSet(const SequenceSet& s) : set(s), i(set.rangesBegin()) {}
+
+ bool operator()(const SequenceNumber& n) {
+ while (i != set.rangesEnd() && i->end() <= n) ++i;
+ return i != set.rangesEnd() && i->begin() <= n;
+ }
+
+ private:
+ const SequenceSet& set;
+ SequenceSet::RangeIterator i;
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_ISINSEQUENCESET_H*/
diff --git a/qpid/cpp/src/qpid/framing/List.cpp b/qpid/cpp/src/qpid/framing/List.cpp
new file mode 100644
index 0000000000..963ebc206b
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/List.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/List.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+
+namespace qpid {
+namespace framing {
+
+uint32_t List::encodedSize() const
+{
+ uint32_t len(4/*size*/ + 4/*count*/);
+ for(Values::const_iterator i = values.begin(); i != values.end(); ++i) {
+ len += (*i)->encodedSize();
+ }
+ return len;
+}
+
+void List::encode(Buffer& buffer) const
+{
+ buffer.putLong(encodedSize() - 4);
+ buffer.putLong(size());
+ for (Values::const_iterator i = values.begin(); i!=values.end(); ++i) {
+ (*i)->encode(buffer);
+ }
+}
+
+void List::decode(Buffer& buffer)
+{
+ values.clear();
+ uint32_t size = buffer.getLong();
+ uint32_t available = buffer.available();
+ if (available < size) {
+ throw IllegalArgumentException(QPID_MSG("Not enough data for list, expected "
+ << size << " bytes but only " << available << " available"));
+ }
+ if (size) {
+ uint32_t count = buffer.getLong();
+ for (uint32_t i = 0; i < count; i++) {
+ ValuePtr value(new FieldValue);
+ value->decode(buffer);
+ values.push_back(value);
+ }
+ }
+}
+
+
+bool List::operator==(const List& other) const {
+ return values.size() == other.values.size() &&
+ std::equal(values.begin(), values.end(), other.values.begin());
+}
+
+std::ostream& operator<<(std::ostream& out, const List& l)
+{
+ out << "{";
+ for(List::Values::const_iterator i = l.values.begin(); i != l.values.end(); ++i) {
+ if (i != l.values.begin()) out << ", ";
+ (*i)->print(out);
+ }
+ return out << "}";
+}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/MethodBodyFactory.h b/qpid/cpp/src/qpid/framing/MethodBodyFactory.h
new file mode 100644
index 0000000000..88bc444795
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/MethodBodyFactory.h
@@ -0,0 +1,45 @@
+#ifndef QPID_FRAMING_METHODBODYFACTORY_H
+#define QPID_FRAMING_METHODBODYFACTORY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace framing {
+
+class AMQMethodBody;
+
+/**
+ * Functions to create instances of AMQMethodBody sub-classes.
+ * Note: MethodBodyFactory.cpp file is generated by rubygen.
+ */
+class MethodBodyFactory
+{
+ public:
+ static boost::intrusive_ptr<AMQMethodBody> create(ClassId c, MethodId m);
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_METHODBODYFACTORY_H*/
diff --git a/qpid/cpp/src/qpid/framing/MethodContent.h b/qpid/cpp/src/qpid/framing/MethodContent.h
new file mode 100644
index 0000000000..b290a0c140
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/MethodContent.h
@@ -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.
+ *
+ */
+#ifndef _MethodContent_
+#define _MethodContent_
+
+#include <string>
+#include "qpid/framing/AMQHeaderBody.h"
+
+namespace qpid {
+namespace framing {
+
+class MethodContent
+{
+public:
+ virtual ~MethodContent() {}
+ //TODO: rethink this interface
+ virtual AMQHeaderBody getHeader() const = 0;
+ virtual const std::string& getData() const = 0;
+};
+
+}}
+#endif
diff --git a/qpid/cpp/src/qpid/framing/ModelMethod.h b/qpid/cpp/src/qpid/framing/ModelMethod.h
new file mode 100644
index 0000000000..d99bd06cfa
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ModelMethod.h
@@ -0,0 +1,49 @@
+#ifndef _ModelMethod_
+#define _ModelMethod_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/Header.h"
+
+namespace qpid {
+namespace framing {
+
+
+class ModelMethod : public AMQMethodBody
+{
+ mutable Header header;
+public:
+ virtual ~ModelMethod() {}
+ virtual void encodeHeader(Buffer& buffer) const { header.encode(buffer); }
+ virtual void decodeHeader(Buffer& buffer, uint32_t size=0) { header.decode(buffer, size); }
+ virtual uint32_t headerSize() const { return header.encodedSize(); }
+ virtual bool isSync() const { return header.getSync(); }
+ virtual void setSync(bool on) const { header.setSync(on); }
+ Header& getHeader() { return header; }
+ const Header& getHeader() const { return header; }
+};
+
+
+}} // namespace qpid::framing
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/OutputHandler.h b/qpid/cpp/src/qpid/framing/OutputHandler.h
new file mode 100644
index 0000000000..88c95589da
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/OutputHandler.h
@@ -0,0 +1,42 @@
+#ifndef _OutputHandler_
+#define _OutputHandler_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/noncopyable.hpp>
+#include "qpid/framing/FrameHandler.h"
+
+namespace qpid {
+namespace framing {
+
+// TODO aconway 2007-08-29: Replace with FrameHandler.
+class OutputHandler : public FrameHandler {
+ public:
+ virtual ~OutputHandler() {}
+ virtual void send(AMQFrame&) = 0;
+ void handle(AMQFrame& f) { send(f); }
+};
+
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp
new file mode 100644
index 0000000000..e617015d64
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/ProtocolInitiation.h"
+
+namespace qpid {
+namespace framing {
+
+ProtocolInitiation::ProtocolInitiation(){}
+
+ProtocolInitiation::ProtocolInitiation(uint8_t _major, uint8_t _minor) : version(_major, _minor) {}
+
+ProtocolInitiation::ProtocolInitiation(ProtocolVersion p) : version(p) {}
+
+ProtocolInitiation::~ProtocolInitiation(){}
+
+void ProtocolInitiation::encode(Buffer& buffer) const {
+ buffer.putOctet('A');
+ buffer.putOctet('M');
+ buffer.putOctet('Q');
+ buffer.putOctet('P');
+ buffer.putOctet(1);//class
+ buffer.putOctet(1);//instance
+ buffer.putOctet(version.getMajor());
+ buffer.putOctet(version.getMinor());
+}
+
+bool ProtocolInitiation::decode(Buffer& buffer){
+ if(buffer.available() >= 8){
+ buffer.getOctet();//A
+ buffer.getOctet();//M
+ buffer.getOctet();//Q
+ buffer.getOctet();//P
+ buffer.getOctet();//class
+ buffer.getOctet();//instance
+ version.setMajor(buffer.getOctet());
+ version.setMinor(buffer.getOctet());
+ return true;
+ }else{
+ return false;
+ }
+}
+
+
+std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi) {
+ return o << int(pi.getMajor()) << "-" << int(pi.getMinor());
+}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/ProtocolInitiation.h b/qpid/cpp/src/qpid/framing/ProtocolInitiation.h
new file mode 100644
index 0000000000..c519bc2442
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.h
@@ -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.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/AMQDataBlock.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/CommonImportExport.h"
+
+#ifndef _ProtocolInitiation_
+#define _ProtocolInitiation_
+
+namespace qpid {
+namespace framing {
+
+class ProtocolInitiation : public AMQDataBlock
+{
+private:
+ ProtocolVersion version;
+
+public:
+ QPID_COMMON_EXTERN ProtocolInitiation();
+ QPID_COMMON_EXTERN ProtocolInitiation(uint8_t major, uint8_t minor);
+ QPID_COMMON_EXTERN ProtocolInitiation(ProtocolVersion p);
+ QPID_COMMON_EXTERN virtual ~ProtocolInitiation();
+ QPID_COMMON_EXTERN virtual void encode(Buffer& buffer) const;
+ QPID_COMMON_EXTERN virtual bool decode(Buffer& buffer);
+ inline virtual uint32_t encodedSize() const { return 8; }
+ inline uint8_t getMajor() const { return version.getMajor(); }
+ inline uint8_t getMinor() const { return version.getMinor(); }
+ inline ProtocolVersion getVersion() const { return version; }
+ bool operator==(ProtocolVersion v) const { return v == getVersion(); }
+};
+
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi);
+
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp b/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp
new file mode 100644
index 0000000000..c63cddb4cc
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/ProtocolVersion.h"
+#include <sstream>
+
+using namespace qpid::framing;
+
+const std::string ProtocolVersion::toString() const
+{
+ std::stringstream ss;
+ ss << major_ << "-" << minor_;
+ return ss.str();
+}
+
+ProtocolVersion& ProtocolVersion::operator=(ProtocolVersion p)
+{
+ major_ = p.major_;
+ minor_ = p.minor_;
+ return *this;
+}
+
+bool ProtocolVersion::operator==(ProtocolVersion p) const
+{
+ return major_ == p.major_ && minor_ == p.minor_;
+}
+
diff --git a/qpid/cpp/src/qpid/framing/Proxy.cpp b/qpid/cpp/src/qpid/framing/Proxy.cpp
new file mode 100644
index 0000000000..452fb13b01
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Proxy.cpp
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/framing/Proxy.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace framing {
+
+Proxy::Proxy(FrameHandler& h) : out(&h), sync(false) {}
+
+Proxy::~Proxy() {}
+
+void Proxy::send(const AMQBody& b) {
+ if (sync) {
+ const AMQMethodBody* m = dynamic_cast<const AMQMethodBody*>(&b);
+ if (m) m->setSync(sync);
+ }
+ AMQFrame f(b);
+ out->handle(f);
+}
+
+ProtocolVersion Proxy::getVersion() const {
+ return ProtocolVersion();
+}
+
+FrameHandler& Proxy::getHandler() { return *out; }
+
+void Proxy::setHandler(FrameHandler& f) { out=&f; }
+
+Proxy::ScopedSync::ScopedSync(Proxy& p) : proxy(p) { proxy.sync = true; }
+Proxy::ScopedSync::~ScopedSync() { proxy.sync = false; }
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/Proxy.h b/qpid/cpp/src/qpid/framing/Proxy.h
new file mode 100644
index 0000000000..0884e9cbd2
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Proxy.h
@@ -0,0 +1,64 @@
+#ifndef _framing_Proxy_h
+#define _framing_Proxy_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/ProtocolVersion.h"
+
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+class AMQBody;
+
+/**
+ * Base class for proxies.
+ */
+class Proxy
+{
+ public:
+ class ScopedSync
+ {
+ Proxy& proxy;
+ public:
+ QPID_COMMON_EXTERN ScopedSync(Proxy& p);
+ QPID_COMMON_EXTERN ~ScopedSync();
+ };
+
+ QPID_COMMON_EXTERN Proxy(FrameHandler& h);
+ QPID_COMMON_EXTERN virtual ~Proxy();
+
+ QPID_COMMON_EXTERN void send(const AMQBody&);
+
+ QPID_COMMON_EXTERN ProtocolVersion getVersion() const;
+
+ QPID_COMMON_EXTERN FrameHandler& getHandler();
+ QPID_COMMON_EXTERN void setHandler(FrameHandler&);
+ private:
+ FrameHandler* out;
+ bool sync;
+};
+
+}} // namespace qpid::framing
+
+
+
+#endif /*!_framing_Proxy_h*/
diff --git a/qpid/cpp/src/qpid/framing/ResizableBuffer.h b/qpid/cpp/src/qpid/framing/ResizableBuffer.h
new file mode 100644
index 0000000000..0abc5ba7f4
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/ResizableBuffer.h
@@ -0,0 +1,60 @@
+#ifndef QPID_FRAMING_RESIZABLEBUFFER_H
+#define QPID_FRAMING_RESIZABLEBUFFER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/Buffer.h"
+#include <vector>
+
+namespace qpid {
+namespace framing {
+
+/**
+ * A buffer that maintains its own storage and can be resized,
+ * keeping any data already written to the buffer.
+ */
+class ResizableBuffer : public Buffer
+{
+ public:
+ ResizableBuffer(size_t initialSize) : store(initialSize) {
+ static_cast<Buffer&>(*this) = Buffer(&store[0], store.size());
+ }
+
+ void resize(size_t newSize) {
+ size_t oldPos = getPosition();
+ store.resize(newSize);
+ static_cast<Buffer&>(*this) = Buffer(&store[0], store.size());
+ setPosition(oldPos);
+ }
+
+ /** Make sure at least n bytes are available */
+ void makeAvailable(size_t n) {
+ if (n > available())
+ resize(getSize() + n - available());
+ }
+
+ private:
+ std::vector<char> store;
+};
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_RESIZABLEBUFFER_H*/
diff --git a/qpid/cpp/src/qpid/framing/SendContent.cpp b/qpid/cpp/src/qpid/framing/SendContent.cpp
new file mode 100644
index 0000000000..04b60396da
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SendContent.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/SendContent.h"
+
+qpid::framing::SendContent::SendContent(FrameHandler& h, uint16_t mfs, uint efc) : handler(h),
+ maxFrameSize(mfs),
+ expectedFrameCount(efc), frameCount(0) {}
+
+void qpid::framing::SendContent::operator()(const AMQFrame& f)
+{
+ bool first = frameCount == 0;
+ bool last = ++frameCount == expectedFrameCount;
+
+ uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
+ const AMQContentBody* body(f.castBody<AMQContentBody>());
+ if (body->encodedSize() > maxContentSize) {
+ uint32_t offset = 0;
+ for (int chunk = body->encodedSize() / maxContentSize; chunk > 0; chunk--) {
+ sendFragment(*body, offset, maxContentSize, first && offset == 0, last && offset + maxContentSize == body->encodedSize());
+ offset += maxContentSize;
+ }
+ uint32_t remainder = body->encodedSize() % maxContentSize;
+ if (remainder) {
+ sendFragment(*body, offset, remainder, first && offset == 0, last);
+ }
+ } else {
+ AMQFrame copy(f);
+ setFlags(copy, first, last);
+ handler.handle(copy);
+ }
+}
+
+void qpid::framing::SendContent::sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const
+{
+ AMQFrame fragment((AMQContentBody(body.getData().substr(offset, size))));
+ setFlags(fragment, first, last);
+ handler.handle(fragment);
+}
+
+void qpid::framing::SendContent::setFlags(AMQFrame& f, bool first, bool last) const
+{
+ f.setBof(false);
+ f.setBos(first);
+ f.setEof(true);//content is always the last segment
+ f.setEos(last);
+}
+
diff --git a/qpid/cpp/src/qpid/framing/SendContent.h b/qpid/cpp/src/qpid/framing/SendContent.h
new file mode 100644
index 0000000000..1c464b9c8b
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SendContent.h
@@ -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.
+ *
+ */
+#include <string>
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/CommonImportExport.h"
+
+#ifndef _SendContent_
+#define _SendContent_
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Functor that sends frame to handler, refragmenting if
+ * necessary. Currently only works on content frames but this could be
+ * changed once we support multi-frame segments in general.
+ */
+class SendContent
+{
+ FrameHandler& handler;
+ const uint16_t maxFrameSize;
+ uint expectedFrameCount;
+ uint frameCount;
+
+ void sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const;
+ void setFlags(AMQFrame& f, bool first, bool last) const;
+public:
+ QPID_COMMON_EXTERN SendContent(FrameHandler& _handler, uint16_t _maxFrameSize, uint frameCount);
+ QPID_COMMON_EXTERN void operator()(const AMQFrame& f);
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/SequenceNumber.cpp b/qpid/cpp/src/qpid/framing/SequenceNumber.cpp
new file mode 100644
index 0000000000..41cb236629
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SequenceNumber.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/Buffer.h"
+#include <ostream>
+
+using qpid::framing::SequenceNumber;
+using qpid::framing::Buffer;
+
+void SequenceNumber::encode(Buffer& buffer) const
+{
+ buffer.putLong(value);
+}
+
+void SequenceNumber::decode(Buffer& buffer)
+{
+ value = buffer.getLong();
+}
+
+uint32_t SequenceNumber::encodedSize() const {
+ return 4;
+}
+
+namespace qpid {
+namespace framing {
+
+std::ostream& operator<<(std::ostream& o, const SequenceNumber& n) {
+ return o << n.getValue();
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp b/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp
new file mode 100644
index 0000000000..e9d78f3c17
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/SequenceNumberSet.h"
+
+using namespace qpid::framing;
+
+void SequenceNumberSet::encode(Buffer& buffer) const
+{
+ buffer.putShort(size() * 4);
+ for (const_iterator i = begin(); i != end(); i++) {
+ buffer.putLong(i->getValue());
+ }
+}
+
+void SequenceNumberSet::decode(Buffer& buffer)
+{
+ clear();
+ uint16_t count = (buffer.getShort() / 4);
+ for (uint16_t i = 0; i < count; i++) {
+ push_back(SequenceNumber(buffer.getLong()));
+ }
+}
+
+uint32_t SequenceNumberSet::encodedSize() const
+{
+ return 2 /*count*/ + (size() * 4);
+}
+
+SequenceNumberSet SequenceNumberSet::condense() const
+{
+ SequenceNumberSet result;
+ const_iterator last = end();
+ const_iterator start = end();
+ for (const_iterator i = begin(); i != end(); i++) {
+ if (start == end()) {
+ start = i;
+ } else if (*i - *last > 1) {
+ result.push_back(*start);
+ result.push_back(*last);
+ start = i;
+ }
+ last = i;
+ }
+ if (start != end()) {
+ result.push_back(*start);
+ result.push_back(*last);
+ }
+ return result;
+}
+
+void SequenceNumberSet::addRange(const SequenceNumber& start, const SequenceNumber& end)
+{
+ push_back(start);
+ push_back(end);
+}
+
+namespace qpid{
+namespace framing{
+
+std::ostream& operator<<(std::ostream& out, const SequenceNumberSet& set) {
+ out << "{";
+ for (SequenceNumberSet::const_iterator i = set.begin(); i != set.end(); i++) {
+ if (i != set.begin()) out << ", ";
+ out << (i->getValue());
+ }
+ out << "}";
+ return out;
+}
+
+}
+}
diff --git a/qpid/cpp/src/qpid/framing/SequenceNumberSet.h b/qpid/cpp/src/qpid/framing/SequenceNumberSet.h
new file mode 100644
index 0000000000..c8356c8163
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SequenceNumberSet.h
@@ -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.
+ *
+ */
+#ifndef _framing_SequenceNumberSet_h
+#define _framing_SequenceNumberSet_h
+
+#include <ostream>
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/InlineVector.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+class SequenceNumberSet : public InlineVector<SequenceNumber, 2>
+{
+ typedef InlineVector<SequenceNumber, 2> Base;
+public:
+ typedef Base::const_iterator const_iterator;
+ typedef Base::iterator iterator;
+
+ void encode(Buffer& buffer) const;
+ void decode(Buffer& buffer);
+ uint32_t encodedSize() const;
+ QPID_COMMON_EXTERN SequenceNumberSet condense() const;
+ QPID_COMMON_EXTERN void addRange(const SequenceNumber& start, const SequenceNumber& end);
+
+ template <class T>
+ void processRanges(T& t) const
+ {
+ if (size() % 2) { //must be even number
+ throw InvalidArgumentException("SequenceNumberSet contains odd number of elements");
+ }
+
+ for (SequenceNumberSet::const_iterator i = begin(); i != end(); i++) {
+ SequenceNumber first = *(i);
+ SequenceNumber last = *(++i);
+ t(first, last);
+ }
+ }
+
+ friend QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const SequenceNumberSet&);
+};
+
+
+}} // namespace qpid::framing
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/SequenceSet.cpp b/qpid/cpp/src/qpid/framing/SequenceSet.cpp
new file mode 100644
index 0000000000..72fcd8a9e2
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/SequenceSet.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+
+using namespace qpid::framing;
+using std::max;
+using std::min;
+
+namespace qpid {
+namespace framing {
+
+namespace {
+//each range contains 2 numbers, 4 bytes each
+uint16_t RANGE_SIZE = 2 * 4;
+}
+
+void SequenceSet::encode(Buffer& buffer) const
+{
+ buffer.putShort(rangesSize() * RANGE_SIZE);
+ for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++) {
+ buffer.putLong(i->first().getValue());
+ buffer.putLong(i->last().getValue());
+ }
+}
+
+void SequenceSet::decode(Buffer& buffer)
+{
+ clear();
+ uint16_t size = buffer.getShort();
+ uint16_t count = size / RANGE_SIZE;//number of ranges
+ if (size % RANGE_SIZE)
+ throw IllegalArgumentException(QPID_MSG("Invalid size for sequence set: " << size));
+
+ for (uint16_t i = 0; i < count; i++) {
+ add(SequenceNumber(buffer.getLong()), SequenceNumber(buffer.getLong()));
+ }
+}
+
+uint32_t SequenceSet::encodedSize() const {
+ return 2 /*size field*/ + (rangesSize() * RANGE_SIZE);
+}
+
+bool SequenceSet::contains(const SequenceNumber& s) const {
+ return RangeSet<SequenceNumber>::contains(s);
+}
+
+void SequenceSet::add(const SequenceNumber& s) { *this += s; }
+
+void SequenceSet::add(const SequenceNumber& start, const SequenceNumber& finish) {
+ *this += Range<SequenceNumber>::makeClosed(std::min(start,finish), std::max(start, finish));
+}
+
+void SequenceSet::add(const SequenceSet& set) { *this += set; }
+
+void SequenceSet::remove(const SequenceSet& set) { *this -= set; }
+
+void SequenceSet::remove(const SequenceNumber& start, const SequenceNumber& finish) {
+ *this -= Range<SequenceNumber>::makeClosed(std::min(start,finish), std::max(start, finish));
+}
+
+void SequenceSet::remove(const SequenceNumber& s) { *this -= s; }
+
+
+struct RangePrinter {
+ std::ostream& out;
+ RangePrinter(std::ostream& o) : out(o) {}
+ void operator()(SequenceNumber i, SequenceNumber j) const {
+ out << "[" << i.getValue() << "," << j.getValue() << "] ";
+ }
+};
+
+std::ostream& operator<<(std::ostream& o, const SequenceSet& s) {
+ RangePrinter print(o);
+ o << "{ ";
+ s.for_each(print);
+ return o << "}";
+}
+
+}} // namespace qpid::framing
+
diff --git a/qpid/cpp/src/qpid/framing/TemplateVisitor.h b/qpid/cpp/src/qpid/framing/TemplateVisitor.h
new file mode 100644
index 0000000000..d6d59603f7
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/TemplateVisitor.h
@@ -0,0 +1,89 @@
+#ifndef QPID_FRAMING_TEMPLATEVISITOR_H
+#define QPID_FRAMING_TEMPLATEVISITOR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/mpl/fold.hpp>
+#include <boost/utility/value_init.hpp>
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Metafunction to generate a visitor class derived from Base, with a
+ * visit for each type in TypeList calling functor F. TypeList may be
+ * any boost::mpl type collection e.g. mpl::list.
+ *
+ * Generated class is: TemplateVisitor<Base, F, TypeList>::type
+ *
+ * @see make_visitor
+ */
+template <class VisitTemplate, class TypeList, class F>
+class TemplateVisitor
+{
+ struct Base : public VisitorBase {
+ F action;
+ Base(F f) : action(f) {}
+ using VisitorBase::visit;
+ };
+
+ template <class B, class T> struct Visit : public B {
+ Visit(F action) : B(action) {}
+ using B::visit;
+ void visit(const T& body) { action(body); }
+ };
+
+ typedef typename boost::mpl::fold<
+ TypeList, Base, Visit<boost::mpl::placeholders::_1,
+ boost::mpl::placeholders::_2>
+ >::type type;
+};
+
+/**
+ * Construct a TemplateVisitor to perform the given action,
+ * for example:
+ * @code
+ */
+template <class VisitorBase, class TypeList, class F>
+TemplateVisitor<VisitorBase,TypeList,F>::type make_visitor(F action) {
+ return TemplateVisitor<VisitorBase,TypeList,F>::type(action);
+};
+
+/**
+ * For method body classes in TypeList, invoke the corresponding function
+ * on Target and return true. For other body types return false.
+ */
+template <class TypeList, class Target>
+bool invoke(const AMQBody& body, Target& target) {
+ typename InvokeVisitor<TypeList, Target>::type v(target);
+ body.accept(v);
+ return v.target;
+}
+
+}} // namespace qpid::framing
+
+
+#endif /*!QPID_FRAMING_INVOKEVISITOR_H*/
+
+}} // namespace qpid::framing
+
+
+
+#endif /*!QPID_FRAMING_TEMPLATEVISITOR_H*/
diff --git a/qpid/cpp/src/qpid/framing/TransferContent.cpp b/qpid/cpp/src/qpid/framing/TransferContent.cpp
new file mode 100644
index 0000000000..837d7d346a
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/TransferContent.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/framing/TransferContent.h"
+
+namespace qpid {
+namespace framing {
+
+TransferContent::TransferContent(const std::string& data, const std::string& key) {
+ setData(data);
+ if (!key.empty()) getDeliveryProperties().setRoutingKey(key);
+}
+
+
+AMQHeaderBody TransferContent::getHeader() const
+{
+ return header;
+}
+
+const std::string& TransferContent::getData() const {
+ return data;
+}
+
+std::string& TransferContent::getData() {
+ return data;
+}
+
+void TransferContent::setData(const std::string& _data)
+{
+ data = _data;
+ header.get<MessageProperties>(true)->setContentLength(data.size());
+}
+
+void TransferContent::appendData(const std::string& _data)
+{
+ data += _data;
+ header.get<MessageProperties>(true)->setContentLength(data.size());
+}
+
+MessageProperties& TransferContent::getMessageProperties()
+{
+ return *header.get<MessageProperties>(true);
+}
+
+DeliveryProperties& TransferContent::getDeliveryProperties()
+{
+ return *header.get<DeliveryProperties>(true);
+}
+
+void TransferContent::populate(const FrameSet& frameset)
+{
+ const AMQHeaderBody* h = frameset.getHeaders();
+ if (h) {
+ header = *h;
+ }
+ frameset.getContent(data);
+}
+
+const MessageProperties& TransferContent::getMessageProperties() const
+{
+ const MessageProperties* props = header.get<MessageProperties>();
+ if (!props) throw Exception("No message properties.");
+ return *props;
+}
+
+const DeliveryProperties& TransferContent::getDeliveryProperties() const
+{
+ const DeliveryProperties* props = header.get<DeliveryProperties>();
+ if (!props) throw Exception("No message properties.");
+ return *props;
+}
+
+bool TransferContent::hasMessageProperties() const
+{
+ return header.get<MessageProperties>();
+}
+
+bool TransferContent::hasDeliveryProperties() const
+{
+ return header.get<DeliveryProperties>();
+}
+
+
+}}
diff --git a/qpid/cpp/src/qpid/framing/TransferContent.h b/qpid/cpp/src/qpid/framing/TransferContent.h
new file mode 100644
index 0000000000..9a698a1823
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/TransferContent.h
@@ -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.
+ *
+ */
+#ifndef _TransferContent_
+#define _TransferContent_
+
+#include "qpid/framing/FrameSet.h"
+#include "qpid/framing/MethodContent.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+/** Message content */
+class QPID_COMMON_CLASS_EXTERN TransferContent : public MethodContent
+{
+ AMQHeaderBody header;
+ std::string data;
+public:
+ QPID_COMMON_EXTERN TransferContent(const std::string& data = std::string(), const std::string& key=std::string());
+
+ ///@internal
+ QPID_COMMON_EXTERN AMQHeaderBody getHeader() const;
+
+ QPID_COMMON_EXTERN void setData(const std::string&);
+ QPID_COMMON_EXTERN const std::string& getData() const;
+ QPID_COMMON_EXTERN std::string& getData();
+
+ QPID_COMMON_EXTERN void appendData(const std::string&);
+
+ QPID_COMMON_EXTERN bool hasMessageProperties() const;
+ QPID_COMMON_EXTERN MessageProperties& getMessageProperties();
+ QPID_COMMON_EXTERN const MessageProperties& getMessageProperties() const;
+
+ QPID_COMMON_EXTERN bool hasDeliveryProperties() const;
+ QPID_COMMON_EXTERN DeliveryProperties& getDeliveryProperties();
+ QPID_COMMON_EXTERN const DeliveryProperties& getDeliveryProperties() const;
+
+ ///@internal
+ QPID_COMMON_EXTERN void populate(const FrameSet& frameset);
+};
+
+}}
+#endif
diff --git a/qpid/cpp/src/qpid/framing/TypeFilter.h b/qpid/cpp/src/qpid/framing/TypeFilter.h
new file mode 100644
index 0000000000..d1c42de583
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/TypeFilter.h
@@ -0,0 +1,51 @@
+#ifndef QPID_FRAMING_TYPEFILTER_H
+#define QPID_FRAMING_TYPEFILTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameHandler.h"
+
+namespace qpid {
+namespace framing {
+
+/**
+ * Predicate that selects frames by type
+ */
+template <uint8_t Type>
+struct TypeFilter {
+ bool operator()(const AMQFrame& f) const {
+ return f.getBody()->type() == Type;
+ }
+};
+
+template <uint8_t T1, uint8_t T2>
+struct TypeFilter2 {
+ bool operator()(const AMQFrame& f) const {
+ return f.getBody()->type() == T1 || f.getBody()->type() == T2;
+ }
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_TYPEFILTER_H*/
diff --git a/qpid/cpp/src/qpid/framing/Uuid.cpp b/qpid/cpp/src/qpid/framing/Uuid.cpp
new file mode 100644
index 0000000000..945c0a4d24
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Uuid.cpp
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/framing/Uuid.h"
+
+#include "qpid/sys/uuid.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/Msg.h"
+
+namespace qpid {
+namespace framing {
+
+using namespace std;
+
+static const size_t UNPARSED_SIZE=36;
+
+Uuid::Uuid(bool unique) {
+ if (unique) {
+ generate();
+ } else {
+ clear();
+ }
+}
+
+Uuid::Uuid(const uint8_t* data) {
+ assign(data);
+}
+
+void Uuid::assign(const uint8_t* data) {
+ // This const cast is for Solaris which has a
+ // uuid_copy that takes a non const 2nd argument
+ uuid_copy(c_array(), const_cast<uint8_t*>(data));
+}
+
+void Uuid::generate() {
+ uuid_generate(c_array());
+}
+
+void Uuid::clear() {
+ uuid_clear(c_array());
+}
+
+// Force int 0/!0 to false/true; avoids compile warnings.
+bool Uuid::isNull() const {
+ return !!uuid_is_null(data());
+}
+
+void Uuid::encode(Buffer& buf) const {
+ buf.putRawData(data(), size());
+}
+
+void Uuid::decode(Buffer& buf) {
+ if (buf.available() < size())
+ throw IllegalArgumentException(QPID_MSG("Not enough data for UUID."));
+ buf.getRawData(c_array(), size());
+}
+
+ostream& operator<<(ostream& out, Uuid uuid) {
+ char unparsed[UNPARSED_SIZE + 1];
+ uuid_unparse(uuid.data(), unparsed);
+ return out << unparsed;
+}
+
+istream& operator>>(istream& in, Uuid& uuid) {
+ char unparsed[UNPARSED_SIZE + 1] = {0};
+ in.get(unparsed, sizeof(unparsed));
+ if (!in.fail()) {
+ if (uuid_parse(unparsed, uuid.c_array()) != 0)
+ in.setstate(ios::failbit);
+ }
+ return in;
+}
+
+std::string Uuid::str() const {
+ std::ostringstream os;
+ os << *this;
+ return os.str();
+}
+
+}} // namespace qpid::framing
diff --git a/qpid/cpp/src/qpid/framing/Visitor.h b/qpid/cpp/src/qpid/framing/Visitor.h
new file mode 100644
index 0000000000..759ee65914
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/Visitor.h
@@ -0,0 +1,92 @@
+#ifndef QPID_FRAMING_VISITOR_H
+#define QPID_FRAMING_VISITOR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/mpl/vector.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <boost/preprocessor/seq/for_each.hpp>
+
+namespace qpid {
+namespace framing {
+
+/** @file Generic visitor pattern. */
+
+/** visit() interface for type T (optional return type R, default is void.)
+ * To create a visitor for a set of types T1, T2 ... do this:
+ * struct MyVisitor : public Visit<T1>, public Visit<T2> ... {};
+ *@param T Type to visit. This must be forward declared, and need not be defined.
+ */
+template <class T, class R=void> struct Visit {
+ typedef R ReturnType;
+ typedef T VisitType;
+
+ virtual ~Visit() {}
+ virtual R visit(T&) = 0;
+};
+
+
+#define QPID_VISITOR_DECL(_1,_2,T) class T;
+
+#define QPID_VISITOR_BASE(_1,_2,T) , public ::qpid::framing::Visit<T>
+
+/** Convenience macro to generate a visitor interface.
+ * QPID_VISITOR(MyVisitor,(A)(B)(C)); is equivalent to:
+ * @code
+ * class A; class B; class C;
+ * class MyVisitor : public Visit<A> , public Visit<B> , public Visit<C> {};
+ * @endcode
+ * @param visitor name of the generated visitor class.
+ * @param bases a sequence of visitable types in the form (T1)(T2)...
+ * Any parenthesized notations are due to quirks of the preprocesser.
+ */
+#define QPID_VISITOR(visitor,types) \
+ BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_DECL, _, types) \
+ class visitor : public ::qpid::framing::Visit<BOOST_PP_SEQ_HEAD(types)> \
+ BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_BASE, _, BOOST_PP_SEQ_TAIL(types)) \
+ {}
+
+/** The root class for the hierarchy of objects visitable by Visitor V.
+ * Defines virtual accept().
+ */
+template <class V, class R=void>
+struct VisitableRoot {
+ typedef V VisitorType;
+ typedef R ReturnType;
+ virtual ~VisitableRoot() {}
+ virtual R accept(V& v) = 0;
+};
+
+/** The base class for concrete visitable classes.
+ * Implements accept().
+ * @param T type of visitable class (CRTP).
+ * @param Base base class to inherit from.
+ */
+template <class T, class Base>
+struct Visitable : public Base {
+ void accept(typename Base::VisitorType& v) {
+ static_cast<Visit<T>& >(v).visit(static_cast<T&>(*this));
+ }
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_VISITOR_H*/
diff --git a/qpid/cpp/src/qpid/framing/amqp_framing.h b/qpid/cpp/src/qpid/framing/amqp_framing.h
new file mode 100644
index 0000000000..3a8b39afb5
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/amqp_framing.h
@@ -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.
+ *
+ */
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/BodyHandler.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
+#include "qpid/framing/InputHandler.h"
+#include "qpid/framing/OutputHandler.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/ProtocolVersion.h"
diff --git a/qpid/cpp/src/qpid/framing/frame_functors.h b/qpid/cpp/src/qpid/framing/frame_functors.h
new file mode 100644
index 0000000000..d2064d6a57
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/frame_functors.h
@@ -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.
+ *
+ */
+#include <string>
+#include <ostream>
+#include <iostream>
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/Buffer.h"
+
+#ifndef _frame_functors_
+#define _frame_functors_
+
+namespace qpid {
+namespace framing {
+
+class SumFrameSize
+{
+ uint64_t size;
+public:
+ SumFrameSize() : size(0) {}
+ void operator()(const AMQFrame& f) { size += f.encodedSize(); }
+ uint64_t getSize() { return size; }
+};
+
+class SumBodySize
+{
+ uint64_t size;
+public:
+ SumBodySize() : size(0) {}
+ void operator()(const AMQFrame& f) { size += f.getBody()->encodedSize(); }
+ uint64_t getSize() { return size; }
+};
+
+class Count
+{
+ uint count;
+public:
+ Count() : count(0) {}
+ void operator()(const AMQFrame&) { count++; }
+ uint getCount() { return count; }
+};
+
+class EncodeFrame
+{
+ Buffer& buffer;
+public:
+ EncodeFrame(Buffer& b) : buffer(b) {}
+ void operator()(const AMQFrame& f) { f.encode(buffer); }
+};
+
+class EncodeBody
+{
+ Buffer& buffer;
+public:
+ EncodeBody(Buffer& b) : buffer(b) {}
+ void operator()(const AMQFrame& f) { f.getBody()->encode(buffer); }
+};
+
+/**
+ * Sends to the specified handler a copy of the frame it is applied to.
+ */
+class Relay
+{
+ FrameHandler& handler;
+
+public:
+ Relay(FrameHandler& h) : handler(h) {}
+
+ void operator()(const AMQFrame& f)
+ {
+ AMQFrame copy(f);
+ handler.handle(copy);
+ }
+};
+
+class Print
+{
+ std::ostream& out;
+public:
+ Print(std::ostream& o) : out(o) {}
+
+ void operator()(const AMQFrame& f)
+ {
+ out << f << std::endl;
+ }
+};
+
+class MarkLastSegment
+{
+public:
+ void operator()(AMQFrame& f) const { f.setEof(true); }
+};
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/framing/variant.h b/qpid/cpp/src/qpid/framing/variant.h
new file mode 100644
index 0000000000..8e13063385
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/variant.h
@@ -0,0 +1,91 @@
+#ifndef QPID_FRAMING_VARIANT_H
+#define QPID_FRAMING_VARIANT_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**@file Tools for using boost::variant. */
+
+
+#include <boost/variant.hpp>
+
+namespace qpid {
+namespace framing {
+class Buffer;
+
+/** boost::static_visitor that throws an exception if variant contains a blank.
+ * Subclasses need to have a using() declaration, which can be generated
+ * with QPID_USING_NOBLANK(R)
+ */
+template <class R=void>
+struct NoBlankVisitor : public boost::static_visitor<R> {
+ R foundBlank() const {
+ assert(0);
+ throw Exception(QPID_MSG("Invalid variant value."));
+ }
+ R operator()(const boost::blank&) const { return foundBlank(); }
+ R operator()(boost::blank&) const { return foundBlank(); }
+};
+
+
+}} // qpid::framing
+
+
+/** Generate a using statement, needed in visitors inheriting NoBlankVisitor
+ * @param R return type.
+ */
+#define QPID_USING_NOBLANK(R) using ::qpid::framing::NoBlankVisitor<R>::operator()
+
+namespace qpid {
+namespace framing {
+
+/** Convert the variant value to type R. */
+template <class R> struct ConvertVisitor : public NoBlankVisitor<R> {
+ QPID_USING_NOBLANK(R);
+ template <class T> R operator()(T& t) const { return t; }
+};
+
+/** Convert the address of variant value to type R. */
+template <class R> struct AddressVisitor : public NoBlankVisitor<R> {
+ QPID_USING_NOBLANK(R);
+ template <class T> R operator()(T& t) const { return &t; }
+};
+
+/** Apply a visitor to the nested variant.*/
+template<class V>
+struct ApplyVisitor : public NoBlankVisitor<typename V::result_type> {
+ QPID_USING_NOBLANK(typename V::result_type);
+ const V& visitor;
+ ApplyVisitor(const V& v) : visitor(v) {}
+ template <class T> typename V::result_type operator()(T& t) const {
+ return boost::apply_visitor(visitor, t);
+ }
+};
+
+/** Convenience function to construct and apply an ApplyVisitor */
+template <class Visitor, class Visitable>
+typename Visitor::result_type applyApplyVisitor(const Visitor& visitor, Visitable& visitable) {
+ return boost::apply_visitor(ApplyVisitor<Visitor>(visitor), visitable);
+}
+
+}} // namespace qpid::framing
+
+
+#endif /*!QPID_FRAMING_VARIANT_H*/
diff --git a/qpid/cpp/src/qpid/log/Helpers.h b/qpid/cpp/src/qpid/log/Helpers.h
new file mode 100644
index 0000000000..82ef8244be
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/Helpers.h
@@ -0,0 +1,79 @@
+#ifndef QPID_LOG_HELPERS_H
+#define QPID_LOG_HELPERS_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/range.hpp>
+
+#include <ostream>
+
+namespace qpid {
+namespace log {
+
+/** @file Helper classes for logging complex types */
+
+/// @internal
+template <class Range>
+struct ListFormatter {
+ typedef typename boost::range_const_iterator<Range>::type Iterator;
+ boost::iterator_range<Iterator> range;
+ const char* separator;
+
+ ListFormatter(const Range& r, const char* s=", ") : range(r), separator(s) {}
+};
+
+/// @internal
+template <class Range>
+std::ostream& operator<<(std::ostream& out, const ListFormatter<Range>& sl) {
+ typename ListFormatter<Range>::Iterator i = sl.range.begin();
+ if (i != sl.range.end()) out << *(i++);
+ while (i != sl.range.end()) out << sl.separator << *(i++);
+ return out;
+}
+
+/** Return a formatting object with operator <<
+ * to stream range as a separated list.
+ *@param range: a range - all standard containers are ranges,
+ * as is a pair of iterators.
+ *@param separator: printed between elements, default ", "
+ */
+template <class Range>
+ListFormatter<Range> formatList(const Range& range, const char* separator=", ") {
+ return ListFormatter<Range>(range, separator);
+}
+
+/** Return a formatting object with operator <<
+ * to stream the range defined by iterators [begin, end)
+ * as a separated list.
+ *@param begin, end: Beginning and end of range.
+ *@param separator: printed between elements, default ", "
+ */
+template <class U, class V>
+ListFormatter<std::pair<U,V> > formatList(U begin, V end, const char* separator=", ") {
+ return formatList(std::make_pair(begin,end), separator);
+}
+
+
+}} // namespace qpid::log
+
+
+
+#endif /*!QPID_LOG_HELPERS_H*/
diff --git a/qpid/cpp/src/qpid/log/Logger.cpp b/qpid/cpp/src/qpid/log/Logger.cpp
new file mode 100644
index 0000000000..1600822142
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/Logger.cpp
@@ -0,0 +1,167 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/log/SinkOptions.h"
+#include "qpid/memory.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Time.h"
+#include "qpid/DisableExceptionLogging.h"
+#include <boost/pool/detail/singleton.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+#include <stdexcept>
+#include <time.h>
+
+
+namespace qpid {
+namespace log {
+
+using namespace std;
+
+typedef sys::Mutex::ScopedLock ScopedLock;
+
+inline void Logger::enable_unlocked(Statement* s) {
+ s->enabled=selector.isEnabled(s->level, s->function);
+}
+
+Logger& Logger::instance() {
+ return boost::details::pool::singleton_default<Logger>::instance();
+}
+
+Logger::Logger() : flags(0) {
+ // Disable automatic logging in Exception constructors to avoid
+ // re-entrant use of logger singleton if there is an error in
+ // option parsing.
+ DisableExceptionLogging del;
+
+ // Initialize myself from env variables so all programs
+ // (e.g. tests) can use logging even if they don't parse
+ // command line args.
+ Options opts("");
+ opts.parse(0, 0);
+ configure(opts);
+}
+
+Logger::~Logger() {}
+
+void Logger::select(const Selector& s) {
+ ScopedLock l(lock);
+ selector=s;
+ std::for_each(statements.begin(), statements.end(),
+ boost::bind(&Logger::enable_unlocked, this, _1));
+}
+
+Logger::Output::Output() {}
+Logger::Output::~Output() {}
+
+void Logger::log(const Statement& s, const std::string& msg) {
+ // Format the message outside the lock.
+ std::ostringstream os;
+ if (!prefix.empty())
+ os << prefix << ": ";
+ if (flags&TIME) {
+ if (flags&HIRES)
+ qpid::sys::outputHiresNow(os);
+ else
+ qpid::sys::outputFormattedNow(os);
+ }
+ if (flags&LEVEL)
+ os << LevelTraits::name(s.level) << " ";
+ if (flags&THREAD)
+ os << "[0x" << hex << qpid::sys::Thread::logId() << "] ";
+ if (flags&FILE)
+ os << s.file << ":";
+ if (flags&LINE)
+ os << dec << s.line << ":";
+ if (flags&FUNCTION)
+ os << s.function << ":";
+ if (flags & (FILE|LINE|FUNCTION))
+ os << " ";
+ os << msg << endl;
+ std::string formatted=os.str();
+ {
+ ScopedLock l(lock);
+ std::for_each(outputs.begin(), outputs.end(),
+ boost::bind(&Output::log, _1, s, formatted));
+ }
+}
+
+void Logger::output(std::auto_ptr<Output> out) {
+ ScopedLock l(lock);
+ outputs.push_back(out.release());
+}
+
+void Logger::clear() {
+ select(Selector()); // locked
+ format(0); // locked
+ ScopedLock l(lock);
+ outputs.clear();
+}
+
+void Logger::format(int formatFlags) {
+ ScopedLock l(lock);
+ flags=formatFlags;
+}
+
+static int bitIf(bool test, int bit) {
+ return test ? bit : 0;
+}
+
+int Logger::format(const Options& opts) {
+ int flags=
+ bitIf(opts.level, LEVEL) |
+ bitIf(opts.time, TIME) |
+ bitIf(opts.source, (FILE|LINE)) |
+ bitIf(opts.function, FUNCTION) |
+ bitIf(opts.thread, THREAD) |
+ bitIf(opts.hiresTs, HIRES);
+ format(flags);
+ return flags;
+}
+
+void Logger::add(Statement& s) {
+ ScopedLock l(lock);
+ enable_unlocked(&s);
+ statements.insert(&s);
+}
+
+void Logger::configure(const Options& opts) {
+ options = opts;
+ clear();
+ Options o(opts);
+ if (o.trace)
+ o.selectors.push_back("trace+");
+ format(o);
+ select(Selector(o));
+ setPrefix(opts.prefix);
+ options.sinkOptions->setup(this);
+}
+
+void Logger::reconfigure(const std::vector<std::string>& selectors) {
+ options.selectors = selectors;
+ select(Selector(options));
+}
+
+void Logger::setPrefix(const std::string& p) { prefix = p; }
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/Options.cpp b/qpid/cpp/src/qpid/log/Options.cpp
new file mode 100644
index 0000000000..0001d00bdf
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/Options.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/Options.h"
+#include "qpid/log/SinkOptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Options.h"
+#include <map>
+#include <string>
+#include <algorithm>
+
+namespace qpid {
+namespace log {
+
+using namespace std;
+
+Options::Options(const std::string& argv0_, const std::string& name_) :
+ qpid::Options(name_),
+ argv0(argv0_),
+ name(name_),
+ time(true),
+ level(true),
+ thread(false),
+ source(false),
+ function(false),
+ hiresTs(false),
+ trace(false),
+ sinkOptions (SinkOptions::create(argv0_))
+{
+ selectors.push_back("notice+");
+
+ ostringstream levels;
+ levels << LevelTraits::name(Level(0));
+ for (int i = 1; i < LevelTraits::COUNT; ++i)
+ levels << " " << LevelTraits::name(Level(i));
+
+ addOptions()
+ ("trace,t", optValue(trace), "Enables all logging" )
+ ("log-enable", optValue(selectors, "RULE"),
+ ("Enables logging for selected levels and components. "
+ "RULE is in the form 'LEVEL[+][:PATTERN]' "
+ "Levels are one of: \n\t "+levels.str()+"\n"
+ "For example:\n"
+ "\t'--log-enable warning+' "
+ "logs all warning, error and critical messages.\n"
+ "\t'--log-enable debug:framing' "
+ "logs debug messages from the framing namespace. "
+ "This option can be used multiple times").c_str())
+ ("log-time", optValue(time, "yes|no"), "Include time in log messages")
+ ("log-level", optValue(level,"yes|no"), "Include severity level in log messages")
+ ("log-source", optValue(source,"yes|no"), "Include source file:line in log messages")
+ ("log-thread", optValue(thread,"yes|no"), "Include thread ID in log messages")
+ ("log-function", optValue(function,"yes|no"), "Include function signature in log messages")
+ ("log-hires-timestamp", optValue(hiresTs,"yes|no"), "Use unformatted hi-res timestamp in log messages")
+ ("log-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages")
+ ;
+ add(*sinkOptions);
+}
+
+Options::Options(const Options &o) :
+ qpid::Options(o.name),
+ argv0(o.argv0),
+ name(o.name),
+ selectors(o.selectors),
+ time(o.time),
+ level(o.level),
+ thread(o.thread),
+ source(o.source),
+ function(o.function),
+ hiresTs(o.hiresTs),
+ trace(o.trace),
+ prefix(o.prefix),
+ sinkOptions (SinkOptions::create(o.argv0))
+{
+ *sinkOptions = *o.sinkOptions;
+}
+
+Options& Options::operator=(const Options& x) {
+ if (this != &x) {
+ argv0 = x.argv0;
+ name = x.name;
+ selectors = x.selectors;
+ time = x.time;
+ level= x.level;
+ thread = x.thread;
+ source = x.source;
+ function = x.function;
+ hiresTs = x.hiresTs;
+ trace = x.trace;
+ prefix = x.prefix;
+ *sinkOptions = *x.sinkOptions;
+ }
+ return *this;
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/OstreamOutput.cpp b/qpid/cpp/src/qpid/log/OstreamOutput.cpp
new file mode 100644
index 0000000000..9b6ec1f8aa
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/OstreamOutput.cpp
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/OstreamOutput.h"
+#include <stdexcept>
+
+using namespace std;
+
+namespace qpid {
+namespace log {
+
+OstreamOutput::OstreamOutput(std::ostream& o) : out(&o) {}
+
+OstreamOutput::OstreamOutput(const std::string& file)
+ : out(new ofstream(file.c_str(), ios_base::out | ios_base::app)),
+ mine(out)
+{
+ if (!out->good())
+ throw std::runtime_error("Can't open log file: "+file);
+}
+
+void OstreamOutput::log(const Statement&, const std::string& m) {
+ *out << m << flush;
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/OstreamOutput.h b/qpid/cpp/src/qpid/log/OstreamOutput.h
new file mode 100644
index 0000000000..12fd4ce425
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/OstreamOutput.h
@@ -0,0 +1,41 @@
+#ifndef QPID_LOG_OSTREAMOUTPUT_H
+#define QPID_LOG_OSTREAMOUTPUT_H
+
+/*
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "qpid/log/Logger.h"
+#include <boost/scoped_ptr.hpp>
+#include <fstream>
+#include <ostream>
+
+namespace qpid {
+namespace log {
+
+/**
+ * OstreamOutput is a reusable logging sink that directs logging to a C++
+ * ostream.
+ */
+class OstreamOutput : public qpid::log::Logger::Output {
+public:
+ QPID_COMMON_EXTERN OstreamOutput(std::ostream& o);
+ QPID_COMMON_EXTERN OstreamOutput(const std::string& file);
+
+ virtual void log(const Statement&, const std::string& m);
+
+private:
+ std::ostream* out;
+ boost::scoped_ptr<std::ostream> mine;
+};
+
+}} // namespace qpid::log
+
+#endif /*!QPID_LOG_OSTREAMOUTPUT_H*/
diff --git a/qpid/cpp/src/qpid/log/Selector.cpp b/qpid/cpp/src/qpid/log/Selector.cpp
new file mode 100644
index 0000000000..a4bc580470
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/Selector.cpp
@@ -0,0 +1,68 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/Selector.h"
+#include "qpid/log/Options.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <string.h>
+
+namespace qpid {
+namespace log {
+
+using namespace std;
+
+void Selector::enable(const string& enableStr) {
+ string level, pattern;
+ size_t c=enableStr.find(':');
+ if (c==string::npos) {
+ level=enableStr;
+ }
+ else {
+ level=enableStr.substr(0,c);
+ pattern=enableStr.substr(c+1);
+ }
+ if (!level.empty() && level[level.size()-1]=='+') {
+ for (int i = LevelTraits::level(level.substr(0,level.size()-1));
+ i < LevelTraits::COUNT;
+ ++i)
+ enable(Level(i), pattern);
+ }
+ else {
+ enable(LevelTraits::level(level), pattern);
+ }
+}
+
+Selector::Selector(const Options& opt){
+ for_each(opt.selectors.begin(), opt.selectors.end(),
+ boost::bind(&Selector::enable, this, _1));
+}
+
+bool Selector::isEnabled(Level level, const char* function) {
+ const char* functionEnd = function+::strlen(function);
+ for (std::vector<std::string>::iterator i=substrings[level].begin();
+ i != substrings[level].end();
+ ++i)
+ {
+ if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd)
+ return true;
+ }
+ return false;
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/Statement.cpp b/qpid/cpp/src/qpid/log/Statement.cpp
new file mode 100644
index 0000000000..6a32b50096
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/Statement.cpp
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/Statement.h"
+#include "qpid/log/Logger.h"
+#include <boost/bind.hpp>
+#include <stdexcept>
+#include <algorithm>
+#include <ctype.h>
+
+namespace qpid {
+namespace log {
+
+namespace {
+using namespace std;
+
+struct NonPrint { bool operator()(unsigned char c) { return !isprint(c) && !isspace(c); } };
+
+const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+std::string quote(const std::string& str) {
+ NonPrint nonPrint;
+ size_t n = std::count_if(str.begin(), str.end(), nonPrint);
+ if (n==0) return str;
+ std::string ret;
+ ret.reserve(str.size()+2*n); // Avoid extra allocations.
+ for (string::const_iterator i = str.begin(); i != str.end(); ++i) {
+ if (nonPrint(*i)) {
+ ret.push_back('\\');
+ ret.push_back('x');
+ ret.push_back(hex[((*i) >> 4)&0xf]);
+ ret.push_back(hex[(*i) & 0xf]);
+ }
+ else ret.push_back(*i);
+ }
+ return ret;
+}
+
+}
+
+void Statement::log(const std::string& message) {
+ Logger::instance().log(*this, quote(message));
+}
+
+Statement::Initializer::Initializer(Statement& s) : statement(s) {
+ Logger::instance().add(s);
+}
+
+namespace {
+const char* names[LevelTraits::COUNT] = {
+ "trace", "debug", "info", "notice", "warning", "error", "critical"
+};
+
+} // namespace
+
+Level LevelTraits::level(const char* name) {
+ for (int i =0; i < LevelTraits::COUNT; ++i) {
+ if (strcmp(names[i], name)==0)
+ return Level(i);
+ }
+ throw std::runtime_error(std::string("Invalid log level name: ")+name);
+}
+
+const char* LevelTraits::name(Level l) {
+ return names[l];
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp
new file mode 100644
index 0000000000..292e9147f6
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/posix/SinkOptions.h"
+#include "qpid/log/SinkOptions.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/OstreamOutput.h"
+#include "qpid/memory.h"
+#include "qpid/Exception.h"
+#include <iostream>
+#include <map>
+#include <string>
+#include <syslog.h>
+
+using std::string;
+using qpid::Exception;
+
+namespace {
+
+// SyslogFacilities maps from syslog values to the text equivalents.
+class SyslogFacilities {
+public:
+ typedef std::map<string, int> ByName;
+ typedef std::map<int, string> ByValue;
+
+ SyslogFacilities() {
+ struct NameValue { const char* name; int value; };
+ NameValue nameValue[] = {
+ { "AUTH", LOG_AUTH },
+#ifdef HAVE_LOG_AUTHPRIV
+ { "AUTHPRIV", LOG_AUTHPRIV },
+#endif
+ { "CRON", LOG_CRON },
+ { "DAEMON", LOG_DAEMON },
+#ifdef HAVE_LOG_FTP
+ { "FTP", LOG_FTP },
+#endif
+ { "KERN", LOG_KERN },
+ { "LOCAL0", LOG_LOCAL0 },
+ { "LOCAL1", LOG_LOCAL1 },
+ { "LOCAL2", LOG_LOCAL2 },
+ { "LOCAL3", LOG_LOCAL3 },
+ { "LOCAL4", LOG_LOCAL4 },
+ { "LOCAL5", LOG_LOCAL5 },
+ { "LOCAL6", LOG_LOCAL6 },
+ { "LOCAL7", LOG_LOCAL7 },
+ { "LPR", LOG_LPR },
+ { "MAIL", LOG_MAIL },
+ { "NEWS", LOG_NEWS },
+ { "SYSLOG", LOG_SYSLOG },
+ { "USER", LOG_USER },
+ { "UUCP", LOG_UUCP }
+ };
+ for (size_t i = 0; i < sizeof(nameValue)/sizeof(nameValue[0]); ++i) {
+ byName.insert(ByName::value_type(nameValue[i].name, nameValue[i].value));
+ // Recognise with and without LOG_ prefix e.g.: AUTH and LOG_AUTH
+ byName.insert(ByName::value_type(string("LOG_")+nameValue[i].name, nameValue[i].value));
+ byValue.insert(ByValue::value_type(nameValue[i].value, string("LOG_")+nameValue[i].name));
+ }
+ }
+
+ int value(const string& name) const {
+ string key(name);
+ std::transform(key.begin(), key.end(), key.begin(), ::toupper);
+ ByName::const_iterator i = byName.find(key);
+ if (i == byName.end())
+ throw Exception("Not a valid syslog facility: " + name);
+ return i->second;
+ }
+
+ string name(int value) const {
+ ByValue::const_iterator i = byValue.find(value);
+ if (i == byValue.end())
+ throw Exception("Not a valid syslog value: " + value);
+ return i->second;
+ }
+
+ private:
+ ByName byName;
+ ByValue byValue;
+};
+
+// 'priorities' maps qpid log levels to syslog priorities. They are in
+// order of qpid log levels and must map to:
+// "trace", "debug", "info", "notice", "warning", "error", "critical"
+static int priorities[qpid::log::LevelTraits::COUNT] = {
+ LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE,
+ LOG_WARNING, LOG_ERR, LOG_CRIT
+};
+
+std::string basename(const std::string path) {
+ size_t i = path.find_last_of('/');
+ return path.substr((i == std::string::npos) ? 0 : i+1);
+}
+
+} // namespace
+
+namespace qpid {
+namespace log {
+namespace posix {
+
+std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) {
+ return o << SyslogFacilities().name(f.value);
+}
+
+std::istream& operator>>(std::istream& i, SyslogFacility& f) {
+ std::string name;
+ i >> name;
+ f.value = SyslogFacilities().value(name);
+ return i;
+}
+
+class SyslogOutput : public qpid::log::Logger::Output {
+public:
+ SyslogOutput(const std::string& logName, const SyslogFacility& logFacility)
+ : name(logName), facility(logFacility.value)
+ {
+ ::openlog(name.c_str(), LOG_PID, facility);
+ }
+
+ virtual ~SyslogOutput() {
+ ::closelog();
+ }
+
+ virtual void log(const Statement& s, const std::string& m)
+ {
+ syslog(priorities[s.level], "%s", m.c_str());
+ }
+
+private:
+ std::string name;
+ int facility;
+};
+
+SinkOptions::SinkOptions(const std::string& argv0)
+ : qpid::log::SinkOptions(),
+ logToStderr(true),
+ logToStdout(false),
+ logToSyslog(false),
+ syslogName(basename(argv0)),
+ syslogFacility(LOG_DAEMON) {
+
+ addOptions()
+ ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr")
+ ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout")
+ ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.")
+ ("log-to-syslog", optValue(logToSyslog, "yes|no"), "Send logging output to syslog;\n\tcustomize using --syslog-name and --syslog-facility")
+ ("syslog-name", optValue(syslogName, "NAME"), "Name to use in syslog messages")
+ ("syslog-facility", optValue(syslogFacility,"LOG_XXX"), "Facility to use in syslog messages")
+ ;
+
+}
+
+qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) {
+ const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs);
+ if (this != prhs) {
+ logToStderr = prhs->logToStderr;
+ logToStdout = prhs->logToStdout;
+ logToSyslog = prhs->logToSyslog;
+ logFile = prhs->logFile;
+ syslogName = prhs->syslogName;
+ syslogFacility.value = prhs->syslogFacility.value;
+ }
+ return *this;
+}
+
+void SinkOptions::detached(void) {
+ if (logToStderr && !logToStdout && !logToSyslog) {
+ logToStderr = false;
+ logToSyslog = true;
+ }
+}
+
+// The Logger acting on these options calls setup() to request any
+// Sinks be set up and fed back to the logger.
+void SinkOptions::setup(qpid::log::Logger *logger) {
+ if (logToStderr)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(std::clog)));
+ if (logToStdout)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(std::cout)));
+
+ if (logFile.length() > 0)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(logFile)));
+
+ if (logToSyslog)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new SyslogOutput(syslogName, syslogFacility)));
+
+}
+
+} // namespace qpid::log::posix
+
+SinkOptions* SinkOptions::create(const std::string& argv0) {
+ return new qpid::log::posix::SinkOptions (argv0);
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/posix/SinkOptions.h b/qpid/cpp/src/qpid/log/posix/SinkOptions.h
new file mode 100644
index 0000000000..d929c29025
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/posix/SinkOptions.h
@@ -0,0 +1,64 @@
+#ifndef QPID_LOG_POSIX_SINKOPTIONS_H
+#define QPID_LOG_POSIX_SINKOPTIONS_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/SinkOptions.h"
+#include <string>
+
+namespace qpid {
+namespace log {
+namespace posix {
+
+/**
+ * Provides a type that can be passed to << and >> operators to convert
+ * syslog facility values to/from strings.
+ */
+struct SyslogFacility {
+ int value;
+ SyslogFacility(int i=0) : value(i) {}
+};
+
+struct SinkOptions : public qpid::log::SinkOptions {
+ SinkOptions(const std::string& argv0);
+ virtual ~SinkOptions() {}
+
+ virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs);
+
+ // This allows the caller to indicate that there's no normal outputs
+ // available. For example, when running as a daemon. In these cases, the
+ // platform's "syslog"-type output should replace the default stderr
+ // unless some other sink has been selected.
+ virtual void detached(void);
+
+ // The Logger acting on these options calls setup() to request any
+ // Sinks be set up and fed back to the logger.
+ virtual void setup(qpid::log::Logger *logger);
+
+ bool logToStderr;
+ bool logToStdout;
+ bool logToSyslog;
+ std::string logFile;
+ std::string syslogName;
+ SyslogFacility syslogFacility;
+};
+
+}}} // namespace qpid::log::posix
+
+#endif /*!QPID_LOG_POSIX_SINKOPTIONS_H*/
diff --git a/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp b/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp
new file mode 100644
index 0000000000..0c74bea64e
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp
@@ -0,0 +1,148 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/windows/SinkOptions.h"
+#include "qpid/log/SinkOptions.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/OstreamOutput.h"
+#include "qpid/memory.h"
+#include "qpid/Exception.h"
+#include <iostream>
+#include <map>
+#include <string>
+
+#include <windows.h>
+
+using qpid::Exception;
+
+namespace qpid {
+namespace log {
+namespace windows {
+
+namespace {
+
+// 'eventTypes' maps qpid log levels to Windows event types. They are in
+// order of qpid log levels and must map to:
+// "trace", "debug", "info", "notice", "warning", "error", "critical"
+static int eventTypes[qpid::log::LevelTraits::COUNT] = {
+ EVENTLOG_INFORMATION_TYPE, /* trace */
+ EVENTLOG_INFORMATION_TYPE, /* debug */
+ EVENTLOG_INFORMATION_TYPE, /* info */
+ EVENTLOG_INFORMATION_TYPE, /* notice */
+ EVENTLOG_WARNING_TYPE, /* warning */
+ EVENTLOG_ERROR_TYPE, /* error */
+ EVENTLOG_ERROR_TYPE /* critical */
+};
+
+} // namespace
+
+class EventLogOutput : public qpid::log::Logger::Output {
+public:
+ EventLogOutput(const std::string& /*sourceName*/) : logHandle(0)
+ {
+ logHandle = OpenEventLog(0, "Application");
+ }
+
+ virtual ~EventLogOutput() {
+ if (logHandle)
+ CloseEventLog(logHandle);
+ }
+
+ virtual void log(const Statement& s, const std::string& m)
+ {
+ if (logHandle) {
+ const char *msg = m.c_str();
+ ReportEvent(logHandle,
+ eventTypes[s.level],
+ 0, /* category unused */
+ 0, /* event id */
+ 0, /* user security id */
+ 1, /* number of strings */
+ 0, /* no event-specific data */
+ &msg,
+ 0);
+ }
+ }
+
+private:
+ HANDLE logHandle;
+};
+
+SinkOptions::SinkOptions(const std::string& /*argv0*/)
+ : qpid::log::SinkOptions(),
+ logToStderr(true),
+ logToStdout(false),
+ logToEventLog(false),
+ eventSource("Application")
+{
+ addOptions()
+ ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr")
+ ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout")
+ ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.")
+ ("log-to-eventlog", optValue(logToEventLog, "yes|no"), "Send logging output to event log;\n\tcustomize using --syslog-name and --syslog-facility")
+ ("eventlog-source-name", optValue(eventSource, "Application"), "Event source to log to")
+ ;
+
+}
+
+qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) {
+ const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs);
+ if (this != prhs) {
+ logToStderr = prhs->logToStderr;
+ logToStdout = prhs->logToStdout;
+ logToEventLog = prhs->logToEventLog;
+ eventSource = prhs->eventSource;
+ logFile = prhs->logFile;
+ }
+ return *this;
+}
+
+void SinkOptions::detached(void) {
+ if (logToStderr && !logToStdout && !logToEventLog) {
+ logToStderr = false;
+ logToEventLog = true;
+ }
+}
+
+// The Logger acting on these options calls setup() to request any
+// Sinks be set up and fed back to the logger.
+void SinkOptions::setup(qpid::log::Logger *logger) {
+ if (logToStderr)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(std::clog)));
+ if (logToStdout)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(std::cout)));
+
+ if (logFile.length() > 0)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new qpid::log::OstreamOutput(logFile)));
+
+ if (logToEventLog)
+ logger->output(make_auto_ptr<qpid::log::Logger::Output>
+ (new EventLogOutput(eventSource)));
+
+}
+
+} // namespace windows
+
+SinkOptions* SinkOptions::create(const std::string& argv0) {
+ return new qpid::log::windows::SinkOptions (argv0);
+}
+
+}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/windows/SinkOptions.h b/qpid/cpp/src/qpid/log/windows/SinkOptions.h
new file mode 100644
index 0000000000..f270c504a2
--- /dev/null
+++ b/qpid/cpp/src/qpid/log/windows/SinkOptions.h
@@ -0,0 +1,54 @@
+#ifndef QPID_LOG_WINDOWS_SINKOPTIONS_H
+#define QPID_LOG_WINDOWS_SINKOPTIONS_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/SinkOptions.h"
+#include <string>
+
+namespace qpid {
+namespace log {
+namespace windows {
+
+struct QPID_COMMON_CLASS_EXTERN SinkOptions : public qpid::log::SinkOptions {
+ QPID_COMMON_EXTERN SinkOptions(const std::string& argv0);
+ virtual ~SinkOptions() {}
+
+ QPID_COMMON_EXTERN virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs);
+
+ // This allows the caller to indicate that there's no normal outputs
+ // available. For example, when running as a service. In these cases, the
+ // platform's "syslog"-type output should replace the default stderr
+ // unless some other sink has been selected.
+ QPID_COMMON_EXTERN virtual void detached(void);
+
+ // The Logger acting on these options calls setup() to request any
+ // Sinks be set up and fed back to the logger.
+ QPID_COMMON_EXTERN virtual void setup(qpid::log::Logger *logger);
+
+ bool logToStderr;
+ bool logToStdout;
+ bool logToEventLog;
+ std::string eventSource;
+ std::string logFile;
+};
+
+}}} // namespace qpid::log::windows
+
+#endif /*!QPID_LOG_WINDOWS_SINKOPTIONS_H*/
diff --git a/qpid/cpp/src/qpid/management/Buffer.cpp b/qpid/cpp/src/qpid/management/Buffer.cpp
new file mode 100644
index 0000000000..7556b2a243
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/Buffer.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/management/Buffer.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/amqp_0_10/Codecs.h"
+
+using namespace std;
+
+namespace qpid {
+namespace management {
+
+Buffer::Buffer(char* data, uint32_t size) : impl(new framing::Buffer(data, size)) {}
+Buffer::~Buffer() { delete impl; }
+void Buffer::record() { impl->record(); }
+void Buffer::restore(bool reRecord) { impl->restore(reRecord); }
+void Buffer::reset() { impl->reset(); }
+uint32_t Buffer::available() { return impl->available(); }
+uint32_t Buffer::getSize() { return impl->getSize(); }
+uint32_t Buffer::getPosition() { return impl->getPosition(); }
+char* Buffer::getPointer() { return impl->getPointer(); }
+void Buffer::putOctet(uint8_t i) { impl->putOctet(i); }
+void Buffer::putShort(uint16_t i) { impl->putShort(i); }
+void Buffer::putLong(uint32_t i) { impl->putLong(i); }
+void Buffer::putLongLong(uint64_t i) { impl->putLongLong(i); }
+void Buffer::putInt8(int8_t i) { impl->putInt8(i); }
+void Buffer::putInt16(int16_t i) { impl->putInt16(i); }
+void Buffer::putInt32(int32_t i) { impl->putInt32(i); }
+void Buffer::putInt64(int64_t i) { impl->putInt64(i); }
+void Buffer::putFloat(float i) { impl->putFloat(i); }
+void Buffer::putDouble(double i) { impl->putDouble(i); }
+void Buffer::putBin128(const uint8_t* i) { impl->putBin128(i); }
+uint8_t Buffer::getOctet() { return impl->getOctet(); }
+uint16_t Buffer::getShort() { return impl->getShort(); }
+uint32_t Buffer::getLong() { return impl->getLong(); }
+uint64_t Buffer::getLongLong() { return impl->getLongLong(); }
+int8_t Buffer:: getInt8() { return impl-> getInt8(); }
+int16_t Buffer::getInt16() { return impl->getInt16(); }
+int32_t Buffer::getInt32() { return impl->getInt32(); }
+int64_t Buffer::getInt64() { return impl->getInt64(); }
+float Buffer::getFloat() { return impl->getFloat(); }
+double Buffer::getDouble() { return impl->getDouble(); }
+void Buffer::putShortString(const string& i) { impl->putShortString(i); }
+void Buffer::putMediumString(const string& i) { impl->putMediumString(i); }
+void Buffer::putLongString(const string& i) { impl->putLongString(i); }
+void Buffer::getShortString(string& i) { impl->getShortString(i); }
+void Buffer::getMediumString(string& i) { impl->getMediumString(i); }
+void Buffer::getLongString(string& i) { impl->getLongString(i); }
+void Buffer::getBin128(uint8_t* i) { impl->getBin128(i); }
+void Buffer::putRawData(const string& i) { impl->putRawData(i); }
+void Buffer::getRawData(string& s, uint32_t size) { impl->getRawData(s, size); }
+void Buffer::putRawData(const uint8_t* data, size_t size) { impl->putRawData(data, size); }
+void Buffer::getRawData(uint8_t* data, size_t size) { impl->getRawData(data, size); }
+
+void Buffer::putMap(const types::Variant::Map& i)
+{
+ string encoded;
+ amqp_0_10::MapCodec::encode(i, encoded);
+ impl->putRawData(encoded);
+}
+
+void Buffer::putList(const types::Variant::List& i)
+{
+ string encoded;
+ amqp_0_10::ListCodec::encode(i, encoded);
+ impl->putRawData(encoded);
+}
+
+void Buffer::getMap(types::Variant::Map& map)
+{
+ string encoded;
+ uint32_t saved = impl->getPosition();
+ uint32_t length = impl->getLong();
+ impl->setPosition(saved);
+ impl->getRawData(encoded, length + sizeof(uint32_t));
+ amqp_0_10::MapCodec::decode(encoded, map);
+}
+
+void Buffer::getList(types::Variant::List& list)
+{
+ string encoded;
+ uint32_t saved = impl->getPosition();
+ uint32_t length = impl->getLong();
+ impl->setPosition(saved);
+ impl->getRawData(encoded, length + sizeof(uint32_t));
+ amqp_0_10::ListCodec::decode(encoded, list);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/management/ConnectionSettings.cpp b/qpid/cpp/src/qpid/management/ConnectionSettings.cpp
new file mode 100644
index 0000000000..1421a26867
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ConnectionSettings.cpp
@@ -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.
+ *
+ */
+#include "qpid/management/ConnectionSettings.h"
+#include "qpid/Version.h"
+
+qpid::management::ConnectionSettings::ConnectionSettings() :
+ protocol("tcp"),
+ host("localhost"),
+ port(5672),
+ locale("en_US"),
+ heartbeat(0),
+ maxChannels(32767),
+ maxFrameSize(65535),
+ bounds(2),
+ tcpNoDelay(false),
+ service(qpid::saslName),
+ minSsf(0),
+ maxSsf(256)
+{}
+
+qpid::management::ConnectionSettings::~ConnectionSettings() {}
+
diff --git a/qpid/cpp/src/qpid/management/Manageable.cpp b/qpid/cpp/src/qpid/management/Manageable.cpp
new file mode 100644
index 0000000000..651215ffb5
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/Manageable.cpp
@@ -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.
+//
+
+#include "qpid/management/Manageable.h"
+
+using namespace qpid::management;
+using std::string;
+
+string Manageable::StatusText (status_t status, string text)
+{
+ if ((status & STATUS_USER) == STATUS_USER)
+ return text;
+
+ switch (status)
+ {
+ case STATUS_OK : return "OK";
+ case STATUS_UNKNOWN_OBJECT : return "UnknownObject";
+ case STATUS_UNKNOWN_METHOD : return "UnknownMethod";
+ case STATUS_NOT_IMPLEMENTED : return "NotImplemented";
+ case STATUS_PARAMETER_INVALID : return "InvalidParameter";
+ case STATUS_FEATURE_NOT_IMPLEMENTED : return "FeatureNotImplemented";
+ case STATUS_FORBIDDEN : return "Forbidden";
+ }
+
+ return "??";
+}
+
+Manageable::status_t Manageable::ManagementMethod (uint32_t, Args&, std::string&)
+{
+ return STATUS_UNKNOWN_METHOD;
+}
+
+bool Manageable::AuthorizeMethod(uint32_t, Args&, const std::string&)
+{
+ return true;
+}
+
diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp
new file mode 100644
index 0000000000..8a12a57fa6
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp
@@ -0,0 +1,3121 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// NOTE on use of log levels: The criteria for using trace vs. debug
+// is to use trace for log messages that are generated for each
+// unbatched stats/props notification and debug for everything else.
+
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/management/ManagementObject.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/log/Statement.h"
+#include <qpid/broker/Message.h>
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/types/Variant.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/framing/List.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include <list>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <typeinfo>
+
+using boost::intrusive_ptr;
+using qpid::framing::Uuid;
+using qpid::types::Variant;
+using qpid::amqp_0_10::MapCodec;
+using qpid::amqp_0_10::ListCodec;
+using qpid::sys::Mutex;
+using namespace qpid::framing;
+using namespace qpid::management;
+using namespace qpid::broker;
+using namespace qpid;
+using namespace std;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+
+namespace {
+ const string defaultVendorName("vendor");
+ const string defaultProductName("product");
+
+ // Create a valid binding key substring by
+ // replacing all '.' chars with '_'
+ const string keyifyNameStr(const string& name)
+ {
+ string n2 = name;
+
+ size_t pos = n2.find('.');
+ while (pos != n2.npos) {
+ n2.replace(pos, 1, "_");
+ pos = n2.find('.', pos);
+ }
+ return n2;
+ }
+
+struct ScopedManagementContext
+{
+ ScopedManagementContext(const qpid::broker::ConnectionState* context)
+ {
+ setManagementExecutionContext(context);
+ }
+ ~ScopedManagementContext()
+ {
+ setManagementExecutionContext(0);
+ }
+};
+}
+
+
+static Variant::Map mapEncodeSchemaId(const string& pname,
+ const string& cname,
+ const string& type,
+ const uint8_t *md5Sum)
+{
+ Variant::Map map_;
+
+ map_["_package_name"] = pname;
+ map_["_class_name"] = cname;
+ map_["_type"] = type;
+ map_["_hash"] = qpid::types::Uuid(md5Sum);
+ return map_;
+}
+
+
+ManagementAgent::RemoteAgent::~RemoteAgent ()
+{
+ QPID_LOG(debug, "Remote Agent removed bank=[" << brokerBank << "." << agentBank << "]");
+ if (mgmtObject != 0) {
+ mgmtObject->resourceDestroy();
+ agent.deleteObjectNowLH(mgmtObject->getObjectId());
+ }
+}
+
+ManagementAgent::ManagementAgent (const bool qmfV1, const bool qmfV2) :
+ threadPoolSize(1), interval(10), broker(0), timer(0),
+ startTime(sys::now()),
+ suppressed(false), disallowAllV1Methods(false),
+ vendorNameKey(defaultVendorName), productNameKey(defaultProductName),
+ qmf1Support(qmfV1), qmf2Support(qmfV2), maxReplyObjs(100),
+ msgBuffer(MA_BUFFER_SIZE)
+{
+ nextObjectId = 1;
+ brokerBank = 1;
+ bootSequence = 1;
+ nextRemoteBank = 10;
+ nextRequestSequence = 1;
+ clientWasAdded = false;
+ attrMap["_vendor"] = defaultVendorName;
+ attrMap["_product"] = defaultProductName;
+}
+
+ManagementAgent::~ManagementAgent ()
+{
+ {
+ sys::Mutex::ScopedLock lock (userLock);
+
+ // Reset the shared pointers to exchanges. If this is not done now, the exchanges
+ // will stick around until dExchange and mExchange are implicitly destroyed (long
+ // after this destructor completes). Those exchanges hold references to management
+ // objects that will be invalid.
+ dExchange.reset();
+ mExchange.reset();
+ v2Topic.reset();
+ v2Direct.reset();
+
+ moveNewObjectsLH();
+ for (ManagementObjectMap::iterator iter = managementObjects.begin ();
+ iter != managementObjects.end ();
+ iter++) {
+ ManagementObject* object = iter->second;
+ delete object;
+ }
+ managementObjects.clear();
+ }
+}
+
+void ManagementAgent::configure(const string& _dataDir, uint16_t _interval,
+ qpid::broker::Broker* _broker, int _threads)
+{
+ dataDir = _dataDir;
+ interval = _interval;
+ broker = _broker;
+ threadPoolSize = _threads;
+ ManagementObject::maxThreads = threadPoolSize;
+
+ // Get from file or generate and save to file.
+ if (dataDir.empty())
+ {
+ uuid.generate();
+ QPID_LOG (info, "ManagementAgent has no data directory, generated new broker ID: "
+ << uuid);
+ }
+ else
+ {
+ string filename(dataDir + "/.mbrokerdata");
+ ifstream inFile(filename.c_str ());
+
+ if (inFile.good())
+ {
+ inFile >> uuid;
+ inFile >> bootSequence;
+ inFile >> nextRemoteBank;
+ inFile.close();
+ if (uuid.isNull()) {
+ uuid.generate();
+ QPID_LOG (info, "No stored broker ID found - ManagementAgent generated broker ID: " << uuid);
+ } else
+ QPID_LOG (info, "ManagementAgent restored broker ID: " << uuid);
+
+ // if sequence goes beyond a 12-bit field, skip zero and wrap to 1.
+ bootSequence++;
+ if (bootSequence & 0xF000)
+ bootSequence = 1;
+ writeData();
+ }
+ else
+ {
+ uuid.generate();
+ QPID_LOG (info, "ManagementAgent generated broker ID: " << uuid);
+ writeData();
+ }
+
+ QPID_LOG (debug, "ManagementAgent boot sequence: " << bootSequence);
+ }
+}
+
+void ManagementAgent::pluginsInitialized() {
+ // Do this here so cluster plugin has the chance to set up the timer.
+ timer = &broker->getClusterTimer();
+ timer->add(new Periodic(*this, interval));
+}
+
+
+void ManagementAgent::setName(const string& vendor, const string& product, const string& instance)
+{
+ if (vendor.find(':') != vendor.npos) {
+ throw Exception("vendor string cannot contain a ':' character.");
+ }
+ if (product.find(':') != product.npos) {
+ throw Exception("product string cannot contain a ':' character.");
+ }
+ attrMap["_vendor"] = vendor;
+ attrMap["_product"] = product;
+ string inst;
+ if (instance.empty()) {
+ if (uuid.isNull())
+ {
+ throw Exception("ManagementAgent::configure() must be called if default name is used.");
+ }
+ inst = uuid.str();
+ } else
+ inst = instance;
+
+ name_address = vendor + ":" + product + ":" + inst;
+ attrMap["_instance"] = inst;
+ attrMap["_name"] = name_address;
+
+ vendorNameKey = keyifyNameStr(vendor);
+ productNameKey = keyifyNameStr(product);
+ instanceNameKey = keyifyNameStr(inst);
+}
+
+
+void ManagementAgent::getName(string& vendor, string& product, string& instance)
+{
+ vendor = std::string(attrMap["_vendor"]);
+ product = std::string(attrMap["_product"]);
+ instance = std::string(attrMap["_instance"]);
+}
+
+
+const std::string& ManagementAgent::getAddress()
+{
+ return name_address;
+}
+
+
+void ManagementAgent::writeData ()
+{
+ string filename (dataDir + "/.mbrokerdata");
+ ofstream outFile (filename.c_str ());
+
+ if (outFile.good())
+ {
+ outFile << uuid << " " << bootSequence << " " << nextRemoteBank << endl;
+ outFile.close();
+ }
+}
+
+void ManagementAgent::setExchange(qpid::broker::Exchange::shared_ptr _mexchange,
+ qpid::broker::Exchange::shared_ptr _dexchange)
+{
+ mExchange = _mexchange;
+ dExchange = _dexchange;
+}
+
+void ManagementAgent::setExchangeV2(qpid::broker::Exchange::shared_ptr _texchange,
+ qpid::broker::Exchange::shared_ptr _dexchange)
+{
+ v2Topic = _texchange;
+ v2Direct = _dexchange;
+}
+
+void ManagementAgent::registerClass (const string& packageName,
+ const string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ sys::Mutex::ScopedLock lock(userLock);
+ PackageMap::iterator pIter = findOrAddPackageLH(packageName);
+ addClassLH(ManagementItem::CLASS_KIND_TABLE, pIter, className, md5Sum, schemaCall);
+}
+
+void ManagementAgent::registerEvent (const string& packageName,
+ const string& eventName,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ sys::Mutex::ScopedLock lock(userLock);
+ PackageMap::iterator pIter = findOrAddPackageLH(packageName);
+ addClassLH(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall);
+}
+
+// Deprecated: V1 objects
+ObjectId ManagementAgent::addObject(ManagementObject* object, uint64_t persistId, bool persistent)
+{
+ uint16_t sequence;
+ uint64_t objectNum;
+
+ sequence = persistent ? 0 : bootSequence;
+ objectNum = persistId ? persistId : nextObjectId++;
+
+ ObjectId objId(0 /*flags*/, sequence, brokerBank, objectNum);
+ objId.setV2Key(*object); // let object generate the v2 key
+
+ object->setObjectId(objId);
+
+ {
+ sys::Mutex::ScopedLock lock(addLock);
+ newManagementObjects.push_back(object);
+ }
+ QPID_LOG(debug, "Management object (V1) added: " << objId.getV2Key());
+ return objId;
+}
+
+
+
+ObjectId ManagementAgent::addObject(ManagementObject* object,
+ const string& key,
+ bool persistent)
+{
+ uint16_t sequence;
+
+ sequence = persistent ? 0 : bootSequence;
+
+ ObjectId objId(0 /*flags*/, sequence, brokerBank);
+ if (key.empty()) {
+ objId.setV2Key(*object); // let object generate the key
+ } else {
+ objId.setV2Key(key);
+ }
+
+ object->setObjectId(objId);
+ {
+ sys::Mutex::ScopedLock lock(addLock);
+ newManagementObjects.push_back(object);
+ }
+ QPID_LOG(debug, "Management object added: " << objId.getV2Key());
+ return objId;
+}
+
+void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severity)
+{
+ static const std::string severityStr[] = {
+ "emerg", "alert", "crit", "error", "warn",
+ "note", "info", "debug"
+ };
+ sys::Mutex::ScopedLock lock (userLock);
+ uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+
+ if (qmf1Support) {
+ Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'e');
+ outBuffer.putShortString(event.getPackageName());
+ outBuffer.putShortString(event.getEventName());
+ outBuffer.putBin128(event.getMd5Sum());
+ outBuffer.putLongLong(uint64_t(sys::Duration(sys::EPOCH, sys::now())));
+ outBuffer.putOctet(sev);
+ string sBuf;
+ event.encode(sBuf);
+ outBuffer.putRawData(sBuf);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, mExchange,
+ "console.event.1.0." + event.getPackageName() + "." + event.getEventName());
+ QPID_LOG(debug, "SEND raiseEvent (v1) class=" << event.getPackageName() << "." << event.getEventName());
+ }
+
+ if (qmf2Support) {
+ Variant::Map map_;
+ Variant::Map schemaId;
+ Variant::Map values;
+ Variant::Map headers;
+
+ map_["_schema_id"] = mapEncodeSchemaId(event.getPackageName(),
+ event.getEventName(),
+ "_event",
+ event.getMd5Sum());
+ event.mapEncode(values);
+ map_["_values"] = values;
+ map_["_timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
+ map_["_severity"] = sev;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_event";
+ headers["qmf.agent"] = name_address;
+
+ stringstream key;
+ key << "agent.ind.event." << keyifyNameStr(event.getPackageName())
+ << "." << keyifyNameStr(event.getEventName())
+ << "." << severityStr[sev]
+ << "." << vendorNameKey
+ << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ key << "." << instanceNameKey;
+
+
+ string content;
+ Variant::List list_;
+ list_.push_back(map_);
+ ListCodec::encode(list_, content);
+ sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str());
+ QPID_LOG(debug, "SEND raiseEvent (v2) class=" << event.getPackageName() << "." << event.getEventName());
+ }
+}
+
+ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds)
+ : TimerTask (sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC),
+ "ManagementAgent::periodicProcessing"),
+ agent(_agent) {}
+
+ManagementAgent::Periodic::~Periodic () {}
+
+void ManagementAgent::Periodic::fire ()
+{
+ agent.timer->add (new Periodic (agent, agent.interval));
+ agent.periodicProcessing ();
+}
+
+void ManagementAgent::clientAdded (const string& routingKey)
+{
+ sys::Mutex::ScopedLock lock(userLock);
+
+ //
+ // If this routing key is not relevant to object updates, exit.
+ //
+ if ((routingKey.compare(0, 1, "#") != 0) &&
+ (routingKey.compare(0, 9, "console.#") != 0) &&
+ (routingKey.compare(0, 12, "console.obj.") != 0))
+ return;
+
+ //
+ // Mark local objects for full-update.
+ //
+ clientWasAdded = true;
+
+ //
+ // If the routing key is relevant for local objects only, don't involve
+ // any of the remote agents.
+ //
+ if (routingKey.compare(0, 39, "console.obj.*.*.org.apache.qpid.broker.") == 0)
+ return;
+
+ std::list<std::string> rkeys;
+
+ for (RemoteAgentMap::iterator aIter = remoteAgents.begin();
+ aIter != remoteAgents.end();
+ aIter++) {
+ rkeys.push_back(aIter->second->routingKey);
+ }
+
+ while (rkeys.size()) {
+ char localBuffer[16];
+ Buffer outBuffer(localBuffer, 16);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'x');
+ outLen = outBuffer.getPosition();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, rkeys.front());
+ QPID_LOG(debug, "SEND ConsoleAddedIndication to=" << rkeys.front());
+ rkeys.pop_front();
+ }
+}
+
+void ManagementAgent::clusterUpdate() {
+ // Called on all cluster memebers when a new member joins a cluster.
+ // Set clientWasAdded so that on the next periodicProcessing we will do
+ // a full update on all cluster members.
+ sys::Mutex::ScopedLock l(userLock);
+ moveNewObjectsLH(); // keep lists consistent with updater/updatee.
+ moveDeletedObjectsLH();
+ clientWasAdded = true;
+ debugSnapshot("Cluster member joined");
+}
+
+void ManagementAgent::encodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq)
+{
+ buf.putOctet ('A');
+ buf.putOctet ('M');
+ buf.putOctet ('2');
+ buf.putOctet (opcode);
+ buf.putLong (seq);
+}
+
+bool ManagementAgent::checkHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq)
+{
+ uint8_t h1 = buf.getOctet();
+ uint8_t h2 = buf.getOctet();
+ uint8_t h3 = buf.getOctet();
+
+ *opcode = buf.getOctet();
+ *seq = buf.getLong();
+
+ return h1 == 'A' && h2 == 'M' && h3 == '2';
+}
+
+// NOTE WELL: assumes userLock is held by caller (LH)
+// NOTE EVEN WELLER: drops this lock when delivering the message!!!
+void ManagementAgent::sendBufferLH(Buffer& buf,
+ uint32_t length,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const string& routingKey)
+{
+ if (suppressed) {
+ QPID_LOG(debug, "Suppressing management message to " << routingKey);
+ return;
+ }
+ if (exchange.get() == 0) return;
+
+ intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange->getName (), 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content((AMQContentBody()));
+
+ content.castBody<AMQContentBody>()->decode(buf, length);
+
+ method.setEof(false);
+ header.setBof(false);
+ header.setEof(false);
+ content.setBof(false);
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+
+ MessageProperties* props =
+ msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(length);
+
+ DeliveryProperties* dp =
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true);
+ dp->setRoutingKey(routingKey);
+
+ msg->getFrames().append(content);
+ msg->setIsManagementMessage(true);
+
+ {
+ sys::Mutex::ScopedUnlock u(userLock);
+
+ DeliverableMessage deliverable (msg);
+ try {
+ exchange->route(deliverable, routingKey, 0);
+ } catch(exception&) {}
+ }
+ buf.reset();
+}
+
+
+void ManagementAgent::sendBufferLH(Buffer& buf,
+ uint32_t length,
+ const string& exchange,
+ const string& routingKey)
+{
+ qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
+ if (ex.get() != 0)
+ sendBufferLH(buf, length, ex, routingKey);
+}
+
+
+// NOTE WELL: assumes userLock is held by caller (LH)
+// NOTE EVEN WELLER: drops this lock when delivering the message!!!
+void ManagementAgent::sendBufferLH(const string& data,
+ const string& cid,
+ const Variant::Map& headers,
+ const string& content_type,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const string& routingKey,
+ uint64_t ttl_msec)
+{
+ Variant::Map::const_iterator i;
+
+ if (suppressed) {
+ QPID_LOG(debug, "Suppressing management message to " << routingKey);
+ return;
+ }
+ if (exchange.get() == 0) return;
+
+ intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange->getName (), 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content((AMQContentBody(data)));
+
+ method.setEof(false);
+ header.setBof(false);
+ header.setEof(false);
+ content.setBof(false);
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+
+ MessageProperties* props =
+ msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(data.length());
+ if (!cid.empty()) {
+ props->setCorrelationId(cid);
+ }
+ props->setContentType(content_type);
+ props->setAppId("qmf2");
+
+ for (i = headers.begin(); i != headers.end(); ++i) {
+ msg->getOrInsertHeaders().setString(i->first, i->second.asString());
+ }
+
+ DeliveryProperties* dp =
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true);
+ dp->setRoutingKey(routingKey);
+ if (ttl_msec) {
+ dp->setTtl(ttl_msec);
+ msg->setTimestamp(broker->getExpiryPolicy());
+ }
+ msg->getFrames().append(content);
+ msg->setIsManagementMessage(true);
+
+ {
+ sys::Mutex::ScopedUnlock u(userLock);
+
+ DeliverableMessage deliverable (msg);
+ try {
+ exchange->route(deliverable, routingKey, 0);
+ } catch(exception&) {}
+ }
+}
+
+
+void ManagementAgent::sendBufferLH(const string& data,
+ const string& cid,
+ const Variant::Map& headers,
+ const string& content_type,
+ const string& exchange,
+ const string& routingKey,
+ uint64_t ttl_msec)
+{
+ qpid::broker::Exchange::shared_ptr ex(broker->getExchanges().get(exchange));
+ if (ex.get() != 0)
+ sendBufferLH(data, cid, headers, content_type, ex, routingKey, ttl_msec);
+}
+
+
+/** Objects that have been added since the last periodic poll are temporarily
+ * saved in the newManagementObjects list. This allows objects to be
+ * added without needing to block on the userLock (addLock is used instead).
+ * These new objects need to be integrated into the object database
+ * (managementObjects) *before* they can be properly managed. This routine
+ * performs the integration.
+ *
+ * Note well: objects on the newManagementObjects list may have been
+ * marked as "deleted", and, possibly re-added. This would result in
+ * duplicate object ids. To avoid clashes, don't put deleted objects
+ * into the active object database.
+ */
+void ManagementAgent::moveNewObjectsLH()
+{
+ sys::Mutex::ScopedLock lock (addLock);
+ while (!newManagementObjects.empty()) {
+ ManagementObject *object = newManagementObjects.back();
+ newManagementObjects.pop_back();
+
+ if (object->isDeleted()) {
+ DeletedObject::shared_ptr dptr(new DeletedObject(object, qmf1Support, qmf2Support));
+ pendingDeletedObjs[dptr->getKey()].push_back(dptr);
+ delete object;
+ } else { // add to active object list, check for duplicates.
+ ObjectId oid = object->getObjectId();
+ ManagementObjectMap::iterator destIter = managementObjects.find(oid);
+ if (destIter != managementObjects.end()) {
+ // duplicate found. It is OK if the old object has been marked
+ // deleted...
+ ManagementObject *oldObj = destIter->second;
+ if (oldObj->isDeleted()) {
+ DeletedObject::shared_ptr dptr(new DeletedObject(oldObj, qmf1Support, qmf2Support));
+ pendingDeletedObjs[dptr->getKey()].push_back(dptr);
+ delete oldObj;
+ } else {
+ // Duplicate non-deleted objects? This is a user error - oids must be unique.
+ // for now, leak the old object (safer than deleting - may still be referenced)
+ // and complain loudly...
+ QPID_LOG(error, "Detected two management objects with the same identifier: " << oid);
+ }
+ }
+ managementObjects[oid] = object;
+ }
+ }
+}
+
+void ManagementAgent::periodicProcessing (void)
+{
+#define BUFSIZE 65536
+#define HEADROOM 4096
+ debugSnapshot("Management agent periodic processing");
+ sys::Mutex::ScopedLock lock (userLock);
+ uint32_t contentSize;
+ string routingKey;
+ string sBuf;
+
+ uint64_t uptime = sys::Duration(startTime, sys::now());
+ static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime);
+
+ moveNewObjectsLH();
+
+ //
+ // Clear the been-here flag on all objects in the map.
+ //
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ object->setFlags(0);
+ if (clientWasAdded) {
+ object->setForcePublish(true);
+ }
+ }
+
+ clientWasAdded = false;
+
+ // first send the pending deletes before sending updates. This prevents a
+ // "false delete" scenario: if an object was deleted then re-added during
+ // the last poll cycle, it will have a delete entry and an active entry.
+ // if we sent the active update first, _then_ the delete update, clients
+ // would incorrectly think the object was deleted. See QPID-2997
+ //
+ bool objectsDeleted = moveDeletedObjectsLH();
+ if (!pendingDeletedObjs.empty()) {
+ // use a temporary copy of the pending deletes so dropping the lock when
+ // the buffer is sent is safe.
+ PendingDeletedObjsMap tmp(pendingDeletedObjs);
+ pendingDeletedObjs.clear();
+
+ for (PendingDeletedObjsMap::iterator mIter = tmp.begin(); mIter != tmp.end(); mIter++) {
+ std::string packageName;
+ std::string className;
+ msgBuffer.reset();
+ uint32_t v1Objs = 0;
+ uint32_t v2Objs = 0;
+ Variant::List list_;
+
+ size_t pos = mIter->first.find(":");
+ packageName = mIter->first.substr(0, pos);
+ className = mIter->first.substr(pos+1);
+
+ for (DeletedObjectList::iterator lIter = mIter->second.begin();
+ lIter != mIter->second.end(); lIter++) {
+ msgBuffer.makeAvailable(HEADROOM); // Make sure there's buffer space.
+ std::string oid = (*lIter)->objectId;
+ if (!(*lIter)->encodedV1Config.empty()) {
+ encodeHeader(msgBuffer, 'c');
+ msgBuffer.putRawData((*lIter)->encodedV1Config);
+ QPID_LOG(trace, "Deleting V1 properties " << oid
+ << " len=" << (*lIter)->encodedV1Config.size());
+ v1Objs++;
+ }
+ if (!(*lIter)->encodedV1Inst.empty()) {
+ encodeHeader(msgBuffer, 'i');
+ msgBuffer.putRawData((*lIter)->encodedV1Inst);
+ QPID_LOG(trace, "Deleting V1 statistics " << oid
+ << " len=" << (*lIter)->encodedV1Inst.size());
+ v1Objs++;
+ }
+ if (v1Objs >= maxReplyObjs) {
+ v1Objs = 0;
+ contentSize = msgBuffer.getSize();
+ stringstream key;
+ key << "console.obj.1.0." << packageName << "." << className;
+ msgBuffer.reset();
+ sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND V1 Multicast ContentInd V1 (delete) to="
+ << key.str() << " len=" << contentSize);
+ }
+
+ if (!(*lIter)->encodedV2.empty()) {
+ QPID_LOG(trace, "Deleting V2 " << "map=" << (*lIter)->encodedV2);
+ list_.push_back((*lIter)->encodedV2);
+ if (++v2Objs >= maxReplyObjs) {
+ v2Objs = 0;
+
+ string content;
+ ListCodec::encode(list_, content);
+ list_.clear();
+ if (content.length()) {
+ stringstream key;
+ Variant::Map headers;
+ key << "agent.ind.data." << keyifyNameStr(packageName)
+ << "." << keyifyNameStr(className)
+ << "." << vendorNameKey
+ << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ key << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND Multicast ContentInd V2 (delete) to=" << key.str() << " len=" << content.length());
+ }
+ }
+ }
+ } // end current list
+
+ // send any remaining objects...
+
+ if (v1Objs) {
+ contentSize = BUFSIZE - msgBuffer.available();
+ stringstream key;
+ key << "console.obj.1.0." << packageName << "." << className;
+ msgBuffer.reset();
+ sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND V1 Multicast ContentInd V1 (delete) to=" << key.str() << " len=" << contentSize);
+ }
+
+ if (!list_.empty()) {
+ string content;
+ ListCodec::encode(list_, content);
+ list_.clear();
+ if (content.length()) {
+ stringstream key;
+ Variant::Map headers;
+ key << "agent.ind.data." << keyifyNameStr(packageName)
+ << "." << keyifyNameStr(className)
+ << "." << vendorNameKey
+ << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ key << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND Multicast ContentInd V2 (delete) to=" << key.str() << " len=" << content.length());
+ }
+ }
+ } // end map
+ }
+
+ //
+ // Process the entire object map. Remember: we drop the userLock each time we call
+ // sendBuffer(). This allows the managementObjects map to be altered during the
+ // sendBuffer() call, so always restart the search after a sendBuffer() call
+ //
+ while (1) {
+ msgBuffer.reset();
+ Variant::List list_;
+ uint32_t pcount;
+ uint32_t scount;
+ uint32_t v1Objs, v2Objs;
+ ManagementObjectMap::iterator baseIter;
+ std::string packageName;
+ std::string className;
+
+ for (baseIter = managementObjects.begin();
+ baseIter != managementObjects.end();
+ baseIter++) {
+ ManagementObject* baseObject = baseIter->second;
+ //
+ // Skip until we find a base object requiring processing...
+ //
+ if (baseObject->getFlags() == 0) {
+ packageName = baseObject->getPackageName();
+ className = baseObject->getClassName();
+ break;
+ }
+ }
+
+ if (baseIter == managementObjects.end())
+ break; // done - all objects processed
+
+ pcount = scount = 0;
+ v1Objs = 0;
+ v2Objs = 0;
+ list_.clear();
+ msgBuffer.reset();
+
+ for (ManagementObjectMap::iterator iter = baseIter;
+ iter != managementObjects.end();
+ iter++) {
+ msgBuffer.makeAvailable(HEADROOM); // Make sure there's buffer space
+ ManagementObject* baseObject = baseIter->second;
+ ManagementObject* object = iter->second;
+ bool send_stats, send_props;
+ if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
+ object->setFlags(1);
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ // skip any objects marked deleted since our first pass. Deal with them
+ // on the next periodic cycle...
+ if (object->isDeleted()) {
+ continue;
+ }
+
+ send_props = (object->getConfigChanged() || object->getForcePublish());
+ send_stats = (object->hasInst() && (object->getInstChanged() || object->getForcePublish()));
+
+ if (send_props && qmf1Support) {
+ size_t pos = msgBuffer.getPosition();
+ encodeHeader(msgBuffer, 'c');
+ sBuf.clear();
+ object->writeProperties(sBuf);
+ msgBuffer.putRawData(sBuf);
+ QPID_LOG(trace, "Changed V1 properties "
+ << object->getObjectId().getV2Key()
+ << " len=" << msgBuffer.getPosition()-pos);
+ ++v1Objs;
+ }
+
+ if (send_stats && qmf1Support) {
+ size_t pos = msgBuffer.getPosition();
+ encodeHeader(msgBuffer, 'i');
+ sBuf.clear();
+ object->writeStatistics(sBuf);
+ msgBuffer.putRawData(sBuf);
+ QPID_LOG(trace, "Changed V1 statistics "
+ << object->getObjectId().getV2Key()
+ << " len=" << msgBuffer.getPosition()-pos);
+ ++v1Objs;
+ }
+
+ if ((send_stats || send_props) && qmf2Support) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oid;
+
+ object->getObjectId().mapEncode(oid);
+ map_["_object_id"] = oid;
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ "_data",
+ object->getMd5Sum());
+ object->writeTimestamps(map_);
+ object->mapEncodeValues(values, send_props, send_stats);
+ map_["_values"] = values;
+ list_.push_back(map_);
+ v2Objs++;
+ QPID_LOG(trace, "Changed V2"
+ << (send_stats? " statistics":"")
+ << (send_props? " properties":"")
+ << " map=" << map_);
+ }
+
+ if (send_props) pcount++;
+ if (send_stats) scount++;
+
+ object->setForcePublish(false);
+
+ if ((qmf1Support && (v1Objs >= maxReplyObjs)) ||
+ (qmf2Support && (v2Objs >= maxReplyObjs)))
+ break; // have enough objects, send an indication...
+ }
+ }
+
+ if (pcount || scount) {
+ if (qmf1Support) {
+ contentSize = BUFSIZE - msgBuffer.available();
+ if (contentSize > 0) {
+ stringstream key;
+ key << "console.obj.1.0." << packageName << "." << className;
+ msgBuffer.reset();
+ sendBufferLH(msgBuffer, contentSize, mExchange, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND V1 Multicast ContentInd to=" << key.str()
+ << " props=" << pcount
+ << " stats=" << scount
+ << " len=" << contentSize);
+ }
+ }
+
+ if (qmf2Support) {
+ string content;
+ ListCodec::encode(list_, content);
+ if (content.length()) {
+ stringstream key;
+ Variant::Map headers;
+ key << "agent.ind.data." << keyifyNameStr(packageName)
+ << "." << keyifyNameStr(className)
+ << "." << vendorNameKey
+ << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ key << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ sendBufferLH(content, "", headers, "amqp/list", v2Topic, key.str()); // UNLOCKS USERLOCK
+ QPID_LOG(debug, "SEND Multicast ContentInd to=" << key.str()
+ << " props=" << pcount
+ << " stats=" << scount
+ << " len=" << content.length());
+ }
+ }
+ }
+ } // end processing updates for all objects
+
+ if (objectsDeleted) deleteOrphanedAgentsLH();
+
+ // heartbeat generation
+
+ if (qmf1Support) {
+#define BUFSIZE 65536
+ uint32_t contentSize;
+ char msgChars[BUFSIZE];
+ Buffer msgBuffer(msgChars, BUFSIZE);
+ encodeHeader(msgBuffer, 'h');
+ msgBuffer.putLongLong(uint64_t(sys::Duration(sys::EPOCH, sys::now())));
+
+ contentSize = BUFSIZE - msgBuffer.available ();
+ msgBuffer.reset ();
+ routingKey = "console.heartbeat.1.0";
+ sendBufferLH(msgBuffer, contentSize, mExchange, routingKey);
+ QPID_LOG(debug, "SEND HeartbeatInd to=" << routingKey);
+ }
+
+ if (qmf2Support) {
+ std::stringstream addr_key;
+
+ addr_key << "agent.ind.heartbeat." << vendorNameKey << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ addr_key << "." << instanceNameKey;
+
+ Variant::Map map;
+ Variant::Map headers;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_agent_heartbeat_indication";
+ headers["qmf.agent"] = name_address;
+
+ map["_values"] = attrMap;
+ map["_values"].asMap()["_timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
+ map["_values"].asMap()["_heartbeat_interval"] = interval;
+ map["_values"].asMap()["_epoch"] = bootSequence;
+
+ string content;
+ MapCodec::encode(map, content);
+
+ // Set TTL (in msecs) on outgoing heartbeat indications based on the interval
+ // time to prevent stale heartbeats from getting to the consoles.
+ sendBufferLH(content, "", headers, "amqp/map", v2Topic, addr_key.str(), interval * 2 * 1000);
+
+ QPID_LOG(debug, "SENT AgentHeartbeat name=" << name_address);
+ }
+}
+
+void ManagementAgent::deleteObjectNowLH(const ObjectId& oid)
+{
+ ManagementObjectMap::iterator iter = managementObjects.find(oid);
+ if (iter == managementObjects.end())
+ return;
+ ManagementObject* object = iter->second;
+ if (!object->isDeleted())
+ return;
+
+ // since sendBufferLH drops the userLock, don't call it until we
+ // are done manipulating the object.
+#define DNOW_BUFSIZE 2048
+ char msgChars[DNOW_BUFSIZE];
+ Buffer msgBuffer(msgChars, DNOW_BUFSIZE);
+ Variant::List list_;
+ stringstream v1key, v2key;
+
+ if (qmf1Support) {
+ string sBuf;
+
+ v1key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName();
+ encodeHeader(msgBuffer, 'c');
+ object->writeProperties(sBuf);
+ msgBuffer.putRawData(sBuf);
+ }
+
+ if (qmf2Support) {
+ Variant::Map map_;
+ Variant::Map values;
+
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ "_data",
+ object->getMd5Sum());
+ object->writeTimestamps(map_);
+ object->mapEncodeValues(values, true, false);
+ map_["_values"] = values;
+ list_.push_back(map_);
+ v2key << "agent.ind.data." << keyifyNameStr(object->getPackageName())
+ << "." << keyifyNameStr(object->getClassName())
+ << "." << vendorNameKey
+ << "." << productNameKey;
+ if (!instanceNameKey.empty())
+ v2key << "." << instanceNameKey;
+ }
+
+ object = 0;
+ managementObjects.erase(oid);
+
+ // object deleted, ok to drop lock now.
+
+ if (qmf1Support) {
+ uint32_t contentSize = msgBuffer.getPosition();
+ msgBuffer.reset();
+ sendBufferLH(msgBuffer, contentSize, mExchange, v1key.str());
+ QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v1key.str());
+ }
+
+ if (qmf2Support) {
+ Variant::Map headers;
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ string content;
+ ListCodec::encode(list_, content);
+ sendBufferLH(content, "", headers, "amqp/list", v2Topic, v2key.str());
+ QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v2key.str());
+ }
+}
+
+void ManagementAgent::sendCommandCompleteLH(const string& replyToKey, uint32_t sequence,
+ uint32_t code, const string& text)
+{
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader (outBuffer, 'z', sequence);
+ outBuffer.putLong (code);
+ outBuffer.putShortString (text);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND CommandCompleteInd code=" << code << " text=" << text << " to=" <<
+ replyToKey << " seq=" << sequence);
+}
+
+void ManagementAgent::sendExceptionLH(const string& rte, const string& rtk, const string& cid,
+ const string& text, uint32_t code, bool viaLocal)
+{
+ static const string addr_exchange("qmf.default.direct");
+
+ Variant::Map map;
+ Variant::Map headers;
+ Variant::Map values;
+ string content;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_exception";
+ headers["qmf.agent"] = viaLocal ? "broker" : name_address;
+
+ values["error_code"] = code;
+ values["error_text"] = text;
+ map["_values"] = values;
+
+ MapCodec::encode(map, content);
+ sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+
+ QPID_LOG(debug, "SENT Exception code=" << code <<" text=" << text);
+}
+
+bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
+ const string& routingKey,
+ const FieldTable* /*args*/,
+ const bool topic,
+ int qmfVersion)
+{
+ sys::Mutex::ScopedLock lock (userLock);
+ Message& msg = ((DeliverableMessage&) deliverable).getMessage ();
+
+ if (topic && qmfVersion == 1) {
+
+ // qmf1 is bound only to the topic management exchange.
+ // Parse the routing key. This management broker should act as though it
+ // is bound to the exchange to match the following keys:
+ //
+ // agent.1.0.#
+ // broker
+ // schema.#
+
+ if (routingKey == "broker") {
+ dispatchAgentCommandLH(msg);
+ return false;
+ }
+
+ if (routingKey.length() > 6) {
+
+ if (routingKey.compare(0, 9, "agent.1.0") == 0) {
+ dispatchAgentCommandLH(msg);
+ return false;
+ }
+
+ if (routingKey.compare(0, 8, "agent.1.") == 0) {
+ return authorizeAgentMessageLH(msg);
+ }
+
+ if (routingKey.compare(0, 7, "schema.") == 0) {
+ dispatchAgentCommandLH(msg);
+ return true;
+ }
+ }
+ }
+
+ if (qmfVersion == 2) {
+
+ if (topic) {
+ // Intercept messages bound to:
+ // "console.ind.locate.# - process these messages, and also allow them to be forwarded.
+ if (routingKey == "console.request.agent_locate") {
+ dispatchAgentCommandLH(msg);
+ return true;
+ }
+
+ } else { // direct exchange
+
+ // Intercept messages bound to:
+ // "broker" - generic alias for the local broker
+ // "<name_address>" - the broker agent's proper name
+ // and do not forward them futher
+ if (routingKey == "broker" || routingKey == name_address) {
+ dispatchAgentCommandLH(msg, routingKey == "broker");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
+{
+ moveNewObjectsLH();
+
+ string methodName;
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ AclModule* acl = broker->getAcl();
+ string inArgs;
+
+ string sBuf;
+ inBuffer.getRawData(sBuf, 16);
+ ObjectId objId;
+ objId.decode(sBuf);
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+ inBuffer.getShortString(methodName);
+ inBuffer.getRawData(inArgs, inBuffer.available());
+
+ QPID_LOG(debug, "RECV MethodRequest (v1) class=" << packageName << ":" << className << "(" << Uuid(hash) << ") method=" <<
+ methodName << " replyTo=" << replyToKey);
+
+ encodeHeader(outBuffer, 'm', sequence);
+
+ if (disallowAllV1Methods) {
+ outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
+ outBuffer.putMediumString("QMFv1 methods forbidden on this broker, use QMFv2");
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN reason='All QMFv1 Methods Forbidden' seq=" << sequence);
+ return;
+ }
+
+ DisallowedMethods::const_iterator i = disallowed.find(make_pair(className, methodName));
+ if (i != disallowed.end()) {
+ outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
+ outBuffer.putMediumString(i->second);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN text=" << i->second << " seq=" << sequence);
+ return;
+ }
+
+ string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
+ if (acl != 0) {
+ map<acl::Property, string> params;
+ params[acl::PROP_SCHEMAPACKAGE] = packageName;
+ params[acl::PROP_SCHEMACLASS] = className;
+
+ if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params)) {
+ outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
+ outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN));
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence);
+ return;
+ }
+ }
+
+ ManagementObjectMap::iterator iter = numericFind(objId);
+ if (iter == managementObjects.end() || iter->second->isDeleted()) {
+ outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT);
+ outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_UNKNOWN_OBJECT));
+ } else {
+ if ((iter->second->getPackageName() != packageName) ||
+ (iter->second->getClassName() != className)) {
+ outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID);
+ outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID));
+ }
+ else
+ try {
+ outBuffer.record();
+ sys::Mutex::ScopedUnlock u(userLock);
+ string outBuf;
+ iter->second->doMethod(methodName, inArgs, outBuf, userId);
+ outBuffer.putRawData(outBuf);
+ } catch(exception& e) {
+ outBuffer.restore();
+ outBuffer.putLong(Manageable::STATUS_EXCEPTION);
+ outBuffer.putMediumString(e.what());
+ }
+ }
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND MethodResponse (v1) to=" << replyToKey << " seq=" << sequence);
+}
+
+
+void ManagementAgent::handleMethodRequestLH (const string& body, const string& rte, const string& rtk,
+ const string& cid, const ConnectionToken* connToken, bool viaLocal)
+{
+ moveNewObjectsLH();
+
+ string methodName;
+ Variant::Map inMap;
+ MapCodec::decode(body, inMap);
+ Variant::Map::const_iterator oid, mid;
+ string content;
+ string error;
+ uint32_t errorCode(0);
+
+ Variant::Map outMap;
+ Variant::Map headers;
+
+ headers["method"] = "response";
+ headers["qmf.opcode"] = "_method_response";
+ headers["qmf.agent"] = viaLocal ? "broker" : name_address;
+
+ if ((oid = inMap.find("_object_id")) == inMap.end() ||
+ (mid = inMap.find("_method_name")) == inMap.end()) {
+ sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_PARAMETER_INVALID),
+ Manageable::STATUS_PARAMETER_INVALID, viaLocal);
+ return;
+ }
+
+ ObjectId objId;
+ Variant::Map inArgs;
+ Variant::Map callMap;
+
+ try {
+ // coversions will throw if input is invalid.
+ objId = ObjectId(oid->second.asMap());
+ methodName = mid->second.getString();
+
+ mid = inMap.find("_arguments");
+ if (mid != inMap.end()) {
+ inArgs = (mid->second).asMap();
+ }
+ } catch(exception& e) {
+ sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+ return;
+ }
+
+ ManagementObjectMap::iterator iter = managementObjects.find(objId);
+
+ if (iter == managementObjects.end() || iter->second->isDeleted()) {
+ stringstream estr;
+ estr << "No object found with ID=" << objId;
+ sendExceptionLH(rte, rtk, cid, estr.str(), 1, viaLocal);
+ return;
+ }
+
+ // validate
+ AclModule* acl = broker->getAcl();
+ DisallowedMethods::const_iterator i;
+
+ i = disallowed.find(make_pair(iter->second->getClassName(), methodName));
+ if (i != disallowed.end()) {
+ sendExceptionLH(rte, rtk, cid, i->second, Manageable::STATUS_FORBIDDEN, viaLocal);
+ return;
+ }
+
+ string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
+ if (acl != 0) {
+ map<acl::Property, string> params;
+ params[acl::PROP_SCHEMAPACKAGE] = iter->second->getPackageName();
+ params[acl::PROP_SCHEMACLASS] = iter->second->getClassName();
+
+ if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params)) {
+ sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+ Manageable::STATUS_FORBIDDEN, viaLocal);
+ return;
+ }
+ }
+
+ // invoke the method
+
+ QPID_LOG(debug, "RECV MethodRequest (v2) class=" << iter->second->getPackageName()
+ << ":" << iter->second->getClassName() << " method=" <<
+ methodName << " replyTo=" << rte << "/" << rtk << " objId=" << objId << " inArgs=" << inArgs);
+
+ try {
+ sys::Mutex::ScopedUnlock u(userLock);
+ iter->second->doMethod(methodName, inArgs, callMap, userId);
+ errorCode = callMap["_status_code"].asUint32();
+ if (errorCode == 0) {
+ outMap["_arguments"] = Variant::Map();
+ for (Variant::Map::const_iterator iter = callMap.begin();
+ iter != callMap.end(); iter++)
+ if (iter->first != "_status_code" && iter->first != "_status_text")
+ outMap["_arguments"].asMap()[iter->first] = iter->second;
+ } else
+ error = callMap["_status_text"].asString();
+ } catch(exception& e) {
+ sendExceptionLH(rte, rtk, cid, e.what(), Manageable::STATUS_EXCEPTION, viaLocal);
+ return;
+ }
+
+ if (errorCode != 0) {
+ sendExceptionLH(rte, rtk, cid, error, errorCode, viaLocal);
+ return;
+ }
+
+ MapCodec::encode(outMap, content);
+ sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+ QPID_LOG(debug, "SEND MethodResponse (v2) to=" << rte << "/" << rtk << " seq=" << cid << " map=" << outMap);
+}
+
+
+void ManagementAgent::handleBrokerRequestLH (Buffer&, const string& replyToKey, uint32_t sequence)
+{
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ QPID_LOG(debug, "RECV BrokerRequest replyTo=" << replyToKey);
+
+ encodeHeader (outBuffer, 'b', sequence);
+ uuid.encode (outBuffer);
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND BrokerResponse to=" << replyToKey);
+}
+
+void ManagementAgent::handlePackageQueryLH (Buffer&, const string& replyToKey, uint32_t sequence)
+{
+ QPID_LOG(debug, "RECV PackageQuery replyTo=" << replyToKey);
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ for (PackageMap::iterator pIter = packages.begin ();
+ pIter != packages.end ();
+ pIter++)
+ {
+ encodeHeader (outBuffer, 'p', sequence);
+ encodePackageIndication (outBuffer, pIter);
+ }
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ if (outLen) {
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND PackageInd to=" << replyToKey << " seq=" << sequence);
+ }
+
+ sendCommandCompleteLH(replyToKey, sequence);
+}
+
+void ManagementAgent::handlePackageIndLH (Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+{
+ string packageName;
+
+ inBuffer.getShortString(packageName);
+
+ QPID_LOG(debug, "RECV PackageInd package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ findOrAddPackageLH(packageName);
+}
+
+void ManagementAgent::handleClassQueryLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+{
+ string packageName;
+
+ inBuffer.getShortString(packageName);
+
+ QPID_LOG(debug, "RECV ClassQuery package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end())
+ {
+ typedef std::pair<SchemaClassKey, uint8_t> _ckeyType;
+ std::list<_ckeyType> classes;
+ ClassMap &cMap = pIter->second;
+ for (ClassMap::iterator cIter = cMap.begin();
+ cIter != cMap.end();
+ cIter++) {
+ if (cIter->second.hasSchema()) {
+ classes.push_back(make_pair(cIter->first, cIter->second.kind));
+ }
+ }
+
+ while (classes.size()) {
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'q', sequence);
+ encodeClassIndication(outBuffer, packageName, classes.front().first, classes.front().second);
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND ClassInd class=" << packageName << ":" << classes.front().first.name <<
+ "(" << Uuid(classes.front().first.hash) << ") to=" << replyToKey << " seq=" << sequence);
+ classes.pop_front();
+ }
+
+ }
+ sendCommandCompleteLH(replyToKey, sequence);
+}
+
+void ManagementAgent::handleClassIndLH (Buffer& inBuffer, const string& replyToKey, uint32_t)
+{
+ string packageName;
+ SchemaClassKey key;
+
+ uint8_t kind = inBuffer.getOctet();
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(key.name);
+ inBuffer.getBin128(key.hash);
+
+ QPID_LOG(debug, "RECV ClassInd class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
+ "), replyTo=" << replyToKey);
+
+ PackageMap::iterator pIter = findOrAddPackageLH(packageName);
+ ClassMap::iterator cIter = pIter->second.find(key);
+ if (cIter == pIter->second.end() || !cIter->second.hasSchema()) {
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ uint32_t sequence = nextRequestSequence++;
+
+ // Schema Request
+ encodeHeader (outBuffer, 'S', sequence);
+ outBuffer.putShortString(packageName);
+ key.encode(outBuffer);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
+ "), to=" << replyToKey << " seq=" << sequence);
+
+ if (cIter != pIter->second.end())
+ pIter->second.erase(key);
+
+ pIter->second.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(kind, sequence)));
+ }
+}
+
+void ManagementAgent::SchemaClass::appendSchema(Buffer& buf)
+{
+ // If the management package is attached locally (embedded in the broker or
+ // linked in via plug-in), call the schema handler directly. If the package
+ // is from a remote management agent, send the stored schema information.
+
+ if (writeSchemaCall != 0) {
+ string schema;
+ writeSchemaCall(schema);
+ buf.putRawData(schema);
+ } else
+ buf.putRawData(reinterpret_cast<uint8_t*>(&data[0]), data.size());
+}
+
+void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, const string& rte, const string& rtk, uint32_t sequence)
+{
+ string packageName;
+ SchemaClassKey key;
+
+ inBuffer.getShortString (packageName);
+ key.decode(inBuffer);
+
+ QPID_LOG(debug, "RECV SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
+ "), replyTo=" << rte << "/" << rtk << " seq=" << sequence);
+
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end()) {
+ ClassMap& cMap = pIter->second;
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end()) {
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ SchemaClass& classInfo = cIter->second;
+
+ if (classInfo.hasSchema()) {
+ encodeHeader(outBuffer, 's', sequence);
+ classInfo.appendSchema(outBuffer);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, rte, rtk);
+ QPID_LOG(debug, "SEND SchemaResponse to=" << rte << "/" << rtk << " seq=" << sequence);
+ }
+ else
+ sendCommandCompleteLH(rtk, sequence, 1, "Schema not available");
+ }
+ else
+ sendCommandCompleteLH(rtk, sequence, 1, "Class key not found");
+ }
+ else
+ sendCommandCompleteLH(rtk, sequence, 1, "Package not found");
+}
+
+void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*replyToKey*/, uint32_t sequence)
+{
+ string packageName;
+ SchemaClassKey key;
+
+ inBuffer.record();
+ inBuffer.getOctet();
+ inBuffer.getShortString(packageName);
+ key.decode(inBuffer);
+ inBuffer.restore();
+
+ QPID_LOG(debug, "RECV SchemaResponse class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) << ")" << " seq=" << sequence);
+
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end()) {
+ ClassMap& cMap = pIter->second;
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end() && cIter->second.pendingSequence == sequence) {
+ size_t length = validateSchema(inBuffer, cIter->second.kind);
+ if (length == 0) {
+ QPID_LOG(warning, "Management Agent received invalid schema response: " << packageName << "." << key.name);
+ cMap.erase(key);
+ } else {
+ cIter->second.data.resize(length);
+ inBuffer.getRawData(reinterpret_cast<uint8_t*>(&cIter->second.data[0]), length);
+
+ // Publish a class-indication message
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'q');
+ encodeClassIndication(outBuffer, pIter->first, cIter->first, cIter->second.kind);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, mExchange, "schema.class");
+ QPID_LOG(debug, "SEND ClassInd class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) << ")" <<
+ " to=schema.class");
+ }
+ }
+ }
+}
+
+bool ManagementAgent::bankInUse (uint32_t bank)
+{
+ for (RemoteAgentMap::iterator aIter = remoteAgents.begin();
+ aIter != remoteAgents.end();
+ aIter++)
+ if (aIter->second->agentBank == bank)
+ return true;
+ return false;
+}
+
+uint32_t ManagementAgent::allocateNewBank ()
+{
+ while (bankInUse (nextRemoteBank))
+ nextRemoteBank++;
+
+ uint32_t allocated = nextRemoteBank++;
+ writeData ();
+ return allocated;
+}
+
+uint32_t ManagementAgent::assignBankLH (uint32_t requestedBank)
+{
+ if (requestedBank == 0 || bankInUse (requestedBank))
+ return allocateNewBank ();
+ return requestedBank;
+}
+
+void ManagementAgent::deleteOrphanedAgentsLH()
+{
+ list<ObjectId> deleteList;
+
+ for (RemoteAgentMap::const_iterator aIter = remoteAgents.begin(); aIter != remoteAgents.end(); aIter++) {
+ bool found = false;
+
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ if (iter->first == aIter->first && !iter->second->isDeleted()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ deleteList.push_back(aIter->first);
+ }
+
+ for (list<ObjectId>::const_iterator dIter = deleteList.begin(); dIter != deleteList.end(); dIter++)
+ remoteAgents.erase(*dIter);
+}
+
+void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, const string& replyToKey, uint32_t sequence, const ConnectionToken* connToken)
+{
+ string label;
+ uint32_t requestedBrokerBank, requestedAgentBank;
+ uint32_t assignedBank;
+ ObjectId connectionRef = ((const ConnectionState*) connToken)->GetManagementObject()->getObjectId();
+ Uuid systemId;
+
+ moveNewObjectsLH();
+ deleteOrphanedAgentsLH();
+ RemoteAgentMap::iterator aIter = remoteAgents.find(connectionRef);
+ if (aIter != remoteAgents.end()) {
+ // There already exists an agent on this session. Reject the request.
+ sendCommandCompleteLH(replyToKey, sequence, 1, "Connection already has remote agent");
+ return;
+ }
+
+ inBuffer.getShortString(label);
+ systemId.decode(inBuffer);
+ requestedBrokerBank = inBuffer.getLong();
+ requestedAgentBank = inBuffer.getLong();
+
+ QPID_LOG(debug, "RECV (Agent)AttachRequest label=" << label << " reqBrokerBank=" << requestedBrokerBank <<
+ " reqAgentBank=" << requestedAgentBank << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ assignedBank = assignBankLH(requestedAgentBank);
+
+ boost::shared_ptr<RemoteAgent> agent(new RemoteAgent(*this));
+ agent->brokerBank = brokerBank;
+ agent->agentBank = assignedBank;
+ agent->routingKey = replyToKey;
+ agent->connectionRef = connectionRef;
+ agent->mgmtObject = new _qmf::Agent (this, agent.get());
+ agent->mgmtObject->set_connectionRef(agent->connectionRef);
+ agent->mgmtObject->set_label (label);
+ agent->mgmtObject->set_registeredTo (broker->GetManagementObject()->getObjectId());
+ agent->mgmtObject->set_systemId ((const unsigned char*)systemId.data());
+ agent->mgmtObject->set_brokerBank (brokerBank);
+ agent->mgmtObject->set_agentBank (assignedBank);
+ addObject (agent->mgmtObject, 0);
+ remoteAgents[connectionRef] = agent;
+
+ QPID_LOG(debug, "Remote Agent registered bank=[" << brokerBank << "." << assignedBank << "] replyTo=" << replyToKey);
+
+ // Send an Attach Response
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader (outBuffer, 'a', sequence);
+ outBuffer.putLong (brokerBank);
+ outBuffer.putLong (assignedBank);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND AttachResponse brokerBank=" << brokerBank << " agentBank=" << assignedBank <<
+ " to=" << replyToKey << " seq=" << sequence);
+}
+
+void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKey, uint32_t sequence)
+{
+ FieldTable ft;
+ FieldTable::ValuePtr value;
+
+ moveNewObjectsLH();
+
+ ft.decode(inBuffer);
+
+ QPID_LOG(debug, "RECV GetQuery (v1) query=" << ft << " seq=" << sequence);
+
+ value = ft.get("_class");
+ if (value.get() == 0 || !value->convertsTo<string>()) {
+ value = ft.get("_objectid");
+ if (value.get() == 0 || !value->convertsTo<string>())
+ return;
+
+ ObjectId selector(value->get<string>());
+ ManagementObjectMap::iterator iter = numericFind(selector);
+ if (iter != managementObjects.end()) {
+ ManagementObject* object = iter->second;
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (!object->isDeleted()) {
+ string sBuf;
+ encodeHeader(outBuffer, 'g', sequence);
+ object->writeProperties(sBuf);
+ outBuffer.putRawData(sBuf);
+ sBuf.clear();
+ object->writeStatistics(sBuf, true);
+ outBuffer.putRawData(sBuf);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
+ }
+ }
+ sendCommandCompleteLH(replyToKey, sequence);
+ return;
+ }
+
+ string className (value->get<string>());
+ std::list<ObjectId>matches;
+
+ // build up a set of all objects to be dumped
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ if (object->getClassName () == className) {
+ matches.push_back(object->getObjectId());
+ }
+ }
+
+ // send them (as sendBufferLH drops the userLock)
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ while (matches.size()) {
+ ObjectId objId = matches.front();
+ ManagementObjectMap::iterator oIter = managementObjects.find( objId );
+ if (oIter != managementObjects.end()) {
+ ManagementObject* object = oIter->second;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (!object->isDeleted()) {
+ string sProps, sStats;
+ object->writeProperties(sProps);
+ object->writeStatistics(sStats, true);
+
+ size_t len = 8 + sProps.length() + sStats.length(); // 8 == size of header in bytes.
+ if (len > MA_BUFFER_SIZE) {
+ QPID_LOG(error, "Object " << objId << " too large for output buffer - discarded!");
+ } else {
+ if (outBuffer.available() < len) { // not enough room in current buffer, send it.
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey); // drops lock
+ QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
+ continue; // lock dropped, need to re-find _SAME_ objid as it may have been deleted.
+ }
+ encodeHeader(outBuffer, 'g', sequence);
+ outBuffer.putRawData(sProps);
+ outBuffer.putRawData(sStats);
+ }
+ }
+ }
+ matches.pop_front();
+ }
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ if (outLen) {
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(debug, "SEND GetResponse (v1) to=" << replyToKey << " seq=" << sequence);
+ }
+
+ sendCommandCompleteLH(replyToKey, sequence);
+}
+
+
+void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, const string& rtk, const string& cid, bool viaLocal)
+{
+ moveNewObjectsLH();
+
+ Variant::Map inMap;
+ Variant::Map::const_iterator i;
+ Variant::Map headers;
+
+ MapCodec::decode(body, inMap);
+ QPID_LOG(debug, "RECV GetQuery (v2): map=" << inMap << " seq=" << cid);
+
+ headers["method"] = "response";
+ headers["qmf.opcode"] = "_query_response";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = viaLocal ? "broker" : name_address;
+
+ /*
+ * Unpack the _what element of the query. Currently we only support OBJECT queries.
+ */
+ i = inMap.find("_what");
+ if (i == inMap.end()) {
+ sendExceptionLH(rte, rtk, cid, "_what element missing in Query");
+ return;
+ }
+
+ if (i->second.getType() != qpid::types::VAR_STRING) {
+ sendExceptionLH(rte, rtk, cid, "_what element is not a string");
+ return;
+ }
+
+ if (i->second.asString() != "OBJECT") {
+ sendExceptionLH(rte, rtk, cid, "Query for _what => '" + i->second.asString() + "' not supported");
+ return;
+ }
+
+ string className;
+ string packageName;
+
+ /*
+ * Handle the _schema_id element, if supplied.
+ */
+ i = inMap.find("_schema_id");
+ if (i != inMap.end() && i->second.getType() == qpid::types::VAR_MAP) {
+ const Variant::Map& schemaIdMap(i->second.asMap());
+
+ Variant::Map::const_iterator s_iter = schemaIdMap.find("_class_name");
+ if (s_iter != schemaIdMap.end() && s_iter->second.getType() == qpid::types::VAR_STRING)
+ className = s_iter->second.asString();
+
+ s_iter = schemaIdMap.find("_package_name");
+ if (s_iter != schemaIdMap.end() && s_iter->second.getType() == qpid::types::VAR_STRING)
+ packageName = s_iter->second.asString();
+ }
+
+
+ /*
+ * Unpack the _object_id element of the query if it is present. If it is present, find that one
+ * object and return it. If it is not present, send a class-based result.
+ */
+ i = inMap.find("_object_id");
+ if (i != inMap.end() && i->second.getType() == qpid::types::VAR_MAP) {
+ Variant::List list_;
+ ObjectId objId(i->second.asMap());
+
+ ManagementObjectMap::iterator iter = managementObjects.find(objId);
+ if (iter != managementObjects.end()) {
+ ManagementObject* object = iter->second;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (!object->isDeleted()) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oidMap;
+
+ object->mapEncodeValues(values, true, true); // write both stats and properties
+ objId.mapEncode(oidMap);
+ map_["_values"] = values;
+ map_["_object_id"] = oidMap;
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ "_data",
+ object->getMd5Sum());
+ list_.push_back(map_);
+ }
+
+ string content;
+
+ ListCodec::encode(list_, content);
+ sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ QPID_LOG(debug, "SENT QueryResponse (query by object_id) to=" << rte << "/" << rtk);
+ return;
+ }
+ } else {
+ // send class-based result.
+ Variant::List _list;
+ Variant::List _subList;
+ unsigned int objCount = 0;
+
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ if (object->getClassName() == className &&
+ (packageName.empty() || object->getPackageName() == packageName)) {
+
+
+ if (!object->isDeleted()) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oidMap;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ object->writeTimestamps(map_);
+ object->mapEncodeValues(values, true, true); // write both stats and properties
+ iter->first.mapEncode(oidMap);
+
+ map_["_values"] = values;
+ map_["_object_id"] = oidMap;
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ "_data",
+ object->getMd5Sum());
+ _subList.push_back(map_);
+ if (++objCount >= maxReplyObjs) {
+ objCount = 0;
+ _list.push_back(_subList);
+ _subList.clear();
+ }
+ }
+ }
+ }
+
+ if (_subList.size())
+ _list.push_back(_subList);
+
+ headers["partial"] = Variant();
+ string content;
+ while (_list.size() > 1) {
+ ListCodec::encode(_list.front().asList(), content);
+ sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ _list.pop_front();
+ QPID_LOG(debug, "SENT QueryResponse (partial, query by schema_id) to=" << rte << "/" << rtk << " len=" << content.length());
+ }
+ headers.erase("partial");
+ ListCodec::encode(_list.size() ? _list.front().asList() : Variant::List(), content);
+ sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ QPID_LOG(debug, "SENT QueryResponse (query by schema_id) to=" << rte << "/" << rtk << " len=" << content.length());
+ return;
+ }
+
+ // Unrecognized query - Send empty message to indicate CommandComplete
+ string content;
+ ListCodec::encode(Variant::List(), content);
+ sendBufferLH(content, cid, headers, "amqp/list", rte, rtk);
+ QPID_LOG(debug, "SENT QueryResponse (empty) to=" << rte << "/" << rtk);
+}
+
+
+void ManagementAgent::handleLocateRequestLH(const string&, const string& rte, const string& rtk, const string& cid)
+{
+ QPID_LOG(debug, "RCVD AgentLocateRequest");
+
+ Variant::Map map;
+ Variant::Map headers;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_agent_locate_response";
+ headers["qmf.agent"] = name_address;
+
+ map["_values"] = attrMap;
+ map["_values"].asMap()["_timestamp"] = uint64_t(sys::Duration(sys::EPOCH, sys::now()));
+ map["_values"].asMap()["_heartbeat_interval"] = interval;
+ map["_values"].asMap()["_epoch"] = bootSequence;
+
+ string content;
+ MapCodec::encode(map, content);
+ sendBufferLH(content, cid, headers, "amqp/map", rte, rtk);
+ clientWasAdded = true;
+
+ QPID_LOG(debug, "SENT AgentLocateResponse replyTo=" << rte << "/" << rtk);
+}
+
+
+bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
+{
+ Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE);
+ uint32_t sequence = 0;
+ bool methodReq = false;
+ bool mapMsg = false;
+ string packageName;
+ string className;
+ string methodName;
+ string cid;
+
+ //
+ // If the message is larger than our working buffer size, we can't determine if it's
+ // authorized or not. In this case, return true (authorized) if there is no ACL in place,
+ // otherwise return false;
+ //
+ if (msg.encodedSize() > MA_BUFFER_SIZE)
+ return broker->getAcl() == 0;
+
+ msg.encodeContent(inBuffer);
+ uint32_t bufferLen = inBuffer.getPosition();
+ inBuffer.reset();
+
+ const framing::MessageProperties* p =
+ msg.getFrames().getHeaders()->get<framing::MessageProperties>();
+
+ const framing::FieldTable *headers = msg.getApplicationHeaders();
+
+ if (headers && msg.getAppId() == "qmf2")
+ {
+ mapMsg = true;
+
+ if (p && p->hasCorrelationId()) {
+ cid = p->getCorrelationId();
+ }
+
+ if (headers->getAsString("qmf.opcode") == "_method_request")
+ {
+ methodReq = true;
+
+ // extract object id and method name
+
+ string body;
+ inBuffer.getRawData(body, bufferLen);
+ Variant::Map inMap;
+ MapCodec::decode(body, inMap);
+ Variant::Map::const_iterator oid, mid;
+
+ ObjectId objId;
+
+ if ((oid = inMap.find("_object_id")) == inMap.end() ||
+ (mid = inMap.find("_method_name")) == inMap.end()) {
+ QPID_LOG(warning,
+ "Missing fields in QMF authorize req received.");
+ return false;
+ }
+
+ try {
+ // coversions will throw if input is invalid.
+ objId = ObjectId(oid->second.asMap());
+ methodName = mid->second.getString();
+ } catch(exception& /*e*/) {
+ QPID_LOG(warning,
+ "Badly formatted QMF authorize req received.");
+ return false;
+ }
+
+ // look up schema for object to get package and class name
+
+ ManagementObjectMap::iterator iter = managementObjects.find(objId);
+
+ if (iter == managementObjects.end() || iter->second->isDeleted()) {
+ QPID_LOG(debug, "ManagementAgent::authorizeAgentMessageLH: stale object id " <<
+ objId);
+ return false;
+ }
+
+ packageName = iter->second->getPackageName();
+ className = iter->second->getClassName();
+ }
+ } else { // old style binary message format
+
+ uint8_t opcode;
+
+ if (!checkHeader(inBuffer, &opcode, &sequence))
+ return false;
+
+ if (opcode == 'M') {
+ methodReq = true;
+
+ // extract method name & schema package and class name
+
+ uint8_t hash[16];
+ inBuffer.getLongLong(); // skip over object id
+ inBuffer.getLongLong();
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+ inBuffer.getShortString(methodName);
+
+ }
+ }
+
+ if (methodReq) {
+ // TODO: check method call against ACL list.
+ map<acl::Property, string> params;
+ AclModule* acl = broker->getAcl();
+ if (acl == 0)
+ return true;
+
+ string userId = ((const qpid::broker::ConnectionState*) msg.getPublisher())->getUserId();
+ params[acl::PROP_SCHEMAPACKAGE] = packageName;
+ params[acl::PROP_SCHEMACLASS] = className;
+
+ if (acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params))
+ return true;
+
+ // authorization failed, send reply if replyTo present
+
+ const framing::MessageProperties* p =
+ msg.getFrames().getHeaders()->get<framing::MessageProperties>();
+ if (p && p->hasReplyTo()) {
+ const framing::ReplyTo& rt = p->getReplyTo();
+ string rte = rt.getExchange();
+ string rtk = rt.getRoutingKey();
+ string cid;
+ if (p && p->hasCorrelationId())
+ cid = p->getCorrelationId();
+
+ if (mapMsg) {
+ sendExceptionLH(rte, rtk, cid, Manageable::StatusText(Manageable::STATUS_FORBIDDEN),
+ Manageable::STATUS_FORBIDDEN, false);
+ } else {
+
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'm', sequence);
+ outBuffer.putLong(Manageable::STATUS_FORBIDDEN);
+ outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_FORBIDDEN));
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBufferLH(outBuffer, outLen, rte, rtk);
+ }
+
+ QPID_LOG(debug, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
+{
+ string rte;
+ string rtk;
+ const framing::MessageProperties* p =
+ msg.getFrames().getHeaders()->get<framing::MessageProperties>();
+ if (p && p->hasReplyTo()) {
+ const framing::ReplyTo& rt = p->getReplyTo();
+ rte = rt.getExchange();
+ rtk = rt.getRoutingKey();
+ }
+ else
+ return;
+
+ Buffer inBuffer(inputBuffer, MA_BUFFER_SIZE);
+ uint8_t opcode;
+
+ if (msg.encodedSize() > MA_BUFFER_SIZE) {
+ QPID_LOG(debug, "ManagementAgent::dispatchAgentCommandLH: Message too large: " <<
+ msg.encodedSize());
+ return;
+ }
+
+ msg.encodeContent(inBuffer);
+ uint32_t bufferLen = inBuffer.getPosition();
+ inBuffer.reset();
+
+ ScopedManagementContext context((const qpid::broker::ConnectionState*) msg.getPublisher());
+ const framing::FieldTable *headers = msg.getApplicationHeaders();
+ if (headers && msg.getAppId() == "qmf2")
+ {
+ string opcode = headers->getAsString("qmf.opcode");
+ string contentType = headers->getAsString("qmf.content");
+ string body;
+ string cid;
+ inBuffer.getRawData(body, bufferLen);
+
+ if (p && p->hasCorrelationId()) {
+ cid = p->getCorrelationId();
+ }
+
+ if (opcode == "_method_request")
+ return handleMethodRequestLH(body, rte, rtk, cid, msg.getPublisher(), viaLocal);
+ else if (opcode == "_query_request")
+ return handleGetQueryLH(body, rte, rtk, cid, viaLocal);
+ else if (opcode == "_agent_locate_request")
+ return handleLocateRequestLH(body, rte, rtk, cid);
+
+ QPID_LOG(warning, "Support for QMF Opcode [" << opcode << "] TBD!!!");
+ return;
+ }
+
+ // old preV2 binary messages
+
+ while (inBuffer.getPosition() < bufferLen) {
+ uint32_t sequence;
+ if (!checkHeader(inBuffer, &opcode, &sequence))
+ return;
+
+ if (opcode == 'B') handleBrokerRequestLH (inBuffer, rtk, sequence);
+ else if (opcode == 'P') handlePackageQueryLH (inBuffer, rtk, sequence);
+ else if (opcode == 'p') handlePackageIndLH (inBuffer, rtk, sequence);
+ else if (opcode == 'Q') handleClassQueryLH (inBuffer, rtk, sequence);
+ else if (opcode == 'q') handleClassIndLH (inBuffer, rtk, sequence);
+ else if (opcode == 'S') handleSchemaRequestLH (inBuffer, rte, rtk, sequence);
+ else if (opcode == 's') handleSchemaResponseLH (inBuffer, rtk, sequence);
+ else if (opcode == 'A') handleAttachRequestLH (inBuffer, rtk, sequence, msg.getPublisher());
+ else if (opcode == 'G') handleGetQueryLH (inBuffer, rtk, sequence);
+ else if (opcode == 'M') handleMethodRequestLH (inBuffer, rtk, sequence, msg.getPublisher());
+ }
+}
+
+ManagementAgent::PackageMap::iterator ManagementAgent::findOrAddPackageLH(string name)
+{
+ PackageMap::iterator pIter = packages.find (name);
+ if (pIter != packages.end ())
+ return pIter;
+
+ // No such package found, create a new map entry.
+ pair<PackageMap::iterator, bool> result =
+ packages.insert(pair<string, ClassMap>(name, ClassMap()));
+ QPID_LOG (debug, "ManagementAgent added package " << name);
+
+ // Publish a package-indication message
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader (outBuffer, 'p');
+ encodePackageIndication (outBuffer, result.first);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBufferLH(outBuffer, outLen, mExchange, "schema.package");
+ QPID_LOG(debug, "SEND PackageInd package=" << name << " to=schema.package");
+
+ return result.first;
+}
+
+void ManagementAgent::addClassLH(uint8_t kind,
+ PackageMap::iterator pIter,
+ const string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ SchemaClassKey key;
+ ClassMap& cMap = pIter->second;
+
+ key.name = className;
+ memcpy(&key.hash, md5Sum, 16);
+
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end())
+ return;
+
+ // No such class found, create a new class with local information.
+ QPID_LOG (debug, "ManagementAgent added class " << pIter->first << ":" <<
+ key.name);
+
+ cMap.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(kind, schemaCall)));
+ cIter = cMap.find(key);
+}
+
+void ManagementAgent::encodePackageIndication(Buffer& buf,
+ PackageMap::iterator pIter)
+{
+ buf.putShortString((*pIter).first);
+}
+
+void ManagementAgent::encodeClassIndication(Buffer& buf,
+ const std::string packageName,
+ const SchemaClassKey key,
+ uint8_t kind)
+{
+ buf.putOctet(kind);
+ buf.putShortString(packageName);
+ key.encode(buf);
+}
+
+size_t ManagementAgent::validateSchema(Buffer& inBuffer, uint8_t kind)
+{
+ if (kind == ManagementItem::CLASS_KIND_TABLE)
+ return validateTableSchema(inBuffer);
+ else if (kind == ManagementItem::CLASS_KIND_EVENT)
+ return validateEventSchema(inBuffer);
+ return 0;
+}
+
+size_t ManagementAgent::validateTableSchema(Buffer& inBuffer)
+{
+ uint32_t start = inBuffer.getPosition();
+ uint32_t end;
+ string text;
+ uint8_t hash[16];
+
+ try {
+ inBuffer.record();
+ uint8_t kind = inBuffer.getOctet();
+ if (kind != ManagementItem::CLASS_KIND_TABLE)
+ return 0;
+
+ inBuffer.getShortString(text);
+ inBuffer.getShortString(text);
+ inBuffer.getBin128(hash);
+
+ uint8_t superType = 0; //inBuffer.getOctet();
+
+ uint16_t propCount = inBuffer.getShort();
+ uint16_t statCount = inBuffer.getShort();
+ uint16_t methCount = inBuffer.getShort();
+
+ if (superType == 1) {
+ inBuffer.getShortString(text);
+ inBuffer.getShortString(text);
+ inBuffer.getBin128(hash);
+ }
+
+ for (uint16_t idx = 0; idx < propCount + statCount; idx++) {
+ FieldTable ft;
+ ft.decode(inBuffer);
+ }
+
+ for (uint16_t idx = 0; idx < methCount; idx++) {
+ FieldTable ft;
+ ft.decode(inBuffer);
+ if (!ft.isSet("argCount"))
+ return 0;
+ int argCount = ft.getAsInt("argCount");
+ for (int mIdx = 0; mIdx < argCount; mIdx++) {
+ FieldTable aft;
+ aft.decode(inBuffer);
+ }
+ }
+ } catch (exception& /*e*/) {
+ return 0;
+ }
+
+ end = inBuffer.getPosition();
+ inBuffer.restore(); // restore original position
+ return end - start;
+}
+
+size_t ManagementAgent::validateEventSchema(Buffer& inBuffer)
+{
+ uint32_t start = inBuffer.getPosition();
+ uint32_t end;
+ string text;
+ uint8_t hash[16];
+
+ try {
+ inBuffer.record();
+ uint8_t kind = inBuffer.getOctet();
+ if (kind != ManagementItem::CLASS_KIND_EVENT)
+ return 0;
+
+ inBuffer.getShortString(text);
+ inBuffer.getShortString(text);
+ inBuffer.getBin128(hash);
+
+ uint8_t superType = 0; //inBuffer.getOctet();
+
+ uint16_t argCount = inBuffer.getShort();
+
+ if (superType == 1) {
+ inBuffer.getShortString(text);
+ inBuffer.getShortString(text);
+ inBuffer.getBin128(hash);
+ }
+ for (uint16_t idx = 0; idx < argCount; idx++) {
+ FieldTable ft;
+ ft.decode(inBuffer);
+ }
+ } catch (exception& /*e*/) {
+ return 0;
+ }
+
+ end = inBuffer.getPosition();
+ inBuffer.restore(); // restore original position
+ return end - start;
+}
+
+ManagementObjectMap::iterator ManagementAgent::numericFind(const ObjectId& oid)
+{
+ ManagementObjectMap::iterator iter = managementObjects.begin();
+ for (; iter != managementObjects.end(); iter++) {
+ if (oid.equalV1(iter->first))
+ break;
+ }
+
+ return iter;
+}
+
+void ManagementAgent::disallow(const string& className, const string& methodName, const string& message) {
+ disallowed[make_pair(className, methodName)] = message;
+}
+
+void ManagementAgent::SchemaClassKey::mapEncode(Variant::Map& _map) const {
+ _map["_cname"] = name;
+ _map["_hash"] = qpid::types::Uuid(hash);
+}
+
+void ManagementAgent::SchemaClassKey::mapDecode(const Variant::Map& _map) {
+ Variant::Map::const_iterator i;
+
+ if ((i = _map.find("_cname")) != _map.end()) {
+ name = i->second.asString();
+ }
+
+ if ((i = _map.find("_hash")) != _map.end()) {
+ const qpid::types::Uuid& uuid = i->second.asUuid();
+ memcpy(hash, uuid.data(), uuid.size());
+ }
+}
+
+void ManagementAgent::SchemaClassKey::encode(qpid::framing::Buffer& buffer) const {
+ buffer.checkAvailable(encodedBufSize());
+ buffer.putShortString(name);
+ buffer.putBin128(hash);
+}
+
+void ManagementAgent::SchemaClassKey::decode(qpid::framing::Buffer& buffer) {
+ buffer.checkAvailable(encodedBufSize());
+ buffer.getShortString(name);
+ buffer.getBin128(hash);
+}
+
+uint32_t ManagementAgent::SchemaClassKey::encodedBufSize() const {
+ return 1 + name.size() + 16 /* bin128 */;
+}
+
+void ManagementAgent::SchemaClass::mapEncode(Variant::Map& _map) const {
+ _map["_type"] = kind;
+ _map["_pending_sequence"] = pendingSequence;
+ _map["_data"] = data;
+}
+
+void ManagementAgent::SchemaClass::mapDecode(const Variant::Map& _map) {
+ Variant::Map::const_iterator i;
+
+ if ((i = _map.find("_type")) != _map.end()) {
+ kind = i->second;
+ }
+ if ((i = _map.find("_pending_sequence")) != _map.end()) {
+ pendingSequence = i->second;
+ }
+ if ((i = _map.find("_data")) != _map.end()) {
+ data = i->second.asString();
+ }
+}
+
+void ManagementAgent::exportSchemas(string& out) {
+ Variant::List list_;
+ Variant::Map map_, kmap, cmap;
+
+ for (PackageMap::const_iterator i = packages.begin(); i != packages.end(); ++i) {
+ string name = i->first;
+ const ClassMap& classes = i ->second;
+ for (ClassMap::const_iterator j = classes.begin(); j != classes.end(); ++j) {
+ const SchemaClassKey& key = j->first;
+ const SchemaClass& klass = j->second;
+ if (klass.writeSchemaCall == 0) { // Ignore built-in schemas.
+ // Encode name, schema-key, schema-class
+
+ map_.clear();
+ kmap.clear();
+ cmap.clear();
+
+ key.mapEncode(kmap);
+ klass.mapEncode(cmap);
+
+ map_["_pname"] = name;
+ map_["_key"] = kmap;
+ map_["_class"] = cmap;
+ list_.push_back(map_);
+ }
+ }
+ }
+
+ ListCodec::encode(list_, out);
+}
+
+void ManagementAgent::importSchemas(qpid::framing::Buffer& inBuf) {
+
+ string buf(inBuf.getPointer(), inBuf.available());
+ Variant::List content;
+ ListCodec::decode(buf, content);
+ Variant::List::const_iterator l;
+
+
+ for (l = content.begin(); l != content.end(); l++) {
+ string package;
+ SchemaClassKey key;
+ SchemaClass klass;
+ Variant::Map map_, kmap, cmap;
+ Variant::Map::const_iterator i;
+
+ map_ = l->asMap();
+
+ if ((i = map_.find("_pname")) != map_.end()) {
+ package = i->second.asString();
+
+ if ((i = map_.find("_key")) != map_.end()) {
+ key.mapDecode(i->second.asMap());
+
+ if ((i = map_.find("_class")) != map_.end()) {
+ klass.mapDecode(i->second.asMap());
+
+ packages[package][key] = klass;
+ }
+ }
+ }
+ }
+}
+
+void ManagementAgent::RemoteAgent::mapEncode(Variant::Map& map_) const {
+ Variant::Map _objId, _values;
+
+ map_["_brokerBank"] = brokerBank;
+ map_["_agentBank"] = agentBank;
+ map_["_routingKey"] = routingKey;
+
+ connectionRef.mapEncode(_objId);
+ map_["_object_id"] = _objId;
+
+ mgmtObject->mapEncodeValues(_values, true, false);
+ map_["_values"] = _values;
+}
+
+void ManagementAgent::RemoteAgent::mapDecode(const Variant::Map& map_) {
+ Variant::Map::const_iterator i;
+
+ if ((i = map_.find("_brokerBank")) != map_.end()) {
+ brokerBank = i->second;
+ }
+
+ if ((i = map_.find("_agentBank")) != map_.end()) {
+ agentBank = i->second;
+ }
+
+ if ((i = map_.find("_routingKey")) != map_.end()) {
+ routingKey = i->second.getString();
+ }
+
+ if ((i = map_.find("_object_id")) != map_.end()) {
+ connectionRef.mapDecode(i->second.asMap());
+ }
+
+ mgmtObject = new _qmf::Agent(&agent, this);
+
+ if ((i = map_.find("_values")) != map_.end()) {
+ mgmtObject->mapDecodeValues(i->second.asMap());
+ }
+
+ // TODO aconway 2010-03-04: see comment in encode(), readProperties doesn't set v2key.
+ mgmtObject->set_connectionRef(connectionRef);
+}
+
+void ManagementAgent::exportAgents(string& out) {
+ Variant::List list_;
+ Variant::Map map_, omap, amap;
+
+ for (RemoteAgentMap::const_iterator i = remoteAgents.begin();
+ i != remoteAgents.end();
+ ++i)
+ {
+ // TODO aconway 2010-03-04: see comment in ManagementAgent::RemoteAgent::encode
+ boost::shared_ptr<RemoteAgent> agent(i->second);
+
+ map_.clear();
+ amap.clear();
+
+ agent->mapEncode(amap);
+ map_["_remote_agent"] = amap;
+ list_.push_back(map_);
+ }
+
+ ListCodec::encode(list_, out);
+}
+
+void ManagementAgent::importAgents(qpid::framing::Buffer& inBuf) {
+ string buf(inBuf.getPointer(), inBuf.available());
+ Variant::List content;
+ ListCodec::decode(buf, content);
+ Variant::List::const_iterator l;
+ sys::Mutex::ScopedLock lock(userLock);
+
+ for (l = content.begin(); l != content.end(); l++) {
+ boost::shared_ptr<RemoteAgent> agent(new RemoteAgent(*this));
+ Variant::Map map_;
+ Variant::Map::const_iterator i;
+
+ map_ = l->asMap();
+
+ if ((i = map_.find("_remote_agent")) != map_.end()) {
+
+ agent->mapDecode(i->second.asMap());
+
+ addObject (agent->mgmtObject, 0, false);
+ remoteAgents[agent->connectionRef] = agent;
+ }
+ }
+}
+
+namespace {
+bool isDeletedMap(const ManagementObjectMap::value_type& value) {
+ return value.second->isDeleted();
+}
+
+bool isDeletedVector(const ManagementObjectVector::value_type& value) {
+ return value->isDeleted();
+}
+
+string summarizeMap(const char* name, const ManagementObjectMap& map) {
+ ostringstream o;
+ size_t deleted = std::count_if(map.begin(), map.end(), isDeletedMap);
+ o << map.size() << " " << name << " (" << deleted << " deleted), ";
+ return o.str();
+}
+
+string summarizeVector(const char* name, const ManagementObjectVector& map) {
+ ostringstream o;
+ size_t deleted = std::count_if(map.begin(), map.end(), isDeletedVector);
+ o << map.size() << " " << name << " (" << deleted << " deleted), ";
+ return o.str();
+}
+
+string dumpMap(const ManagementObjectMap& map) {
+ ostringstream o;
+ for (ManagementObjectMap::const_iterator i = map.begin(); i != map.end(); ++i) {
+ o << endl << " " << i->second->getObjectId().getV2Key()
+ << (i->second->isDeleted() ? " (deleted)" : "");
+ }
+ return o.str();
+}
+
+string dumpVector(const ManagementObjectVector& map) {
+ ostringstream o;
+ for (ManagementObjectVector::const_iterator i = map.begin(); i != map.end(); ++i) {
+ o << endl << " " << (*i)->getObjectId().getV2Key()
+ << ((*i)->isDeleted() ? " (deleted)" : "");
+ }
+ return o.str();
+}
+
+} // namespace
+
+string ManagementAgent::summarizeAgents() {
+ ostringstream msg;
+ if (!remoteAgents.empty()) {
+ msg << remoteAgents.size() << " agents(";
+ for (RemoteAgentMap::const_iterator i=remoteAgents.begin();
+ i != remoteAgents.end(); ++i)
+ msg << " " << i->second->routingKey;
+ msg << "), ";
+ }
+ return msg.str();
+}
+
+
+void ManagementAgent::debugSnapshot(const char* title) {
+ QPID_LOG(debug, title << ": management snapshot: "
+ << packages.size() << " packages, "
+ << summarizeMap("objects", managementObjects)
+ << summarizeVector("new objects ", newManagementObjects)
+ << pendingDeletedObjs.size() << " pending deletes"
+ << summarizeAgents());
+
+ QPID_LOG_IF(trace, managementObjects.size(),
+ title << ": objects" << dumpMap(managementObjects));
+ QPID_LOG_IF(trace, newManagementObjects.size(),
+ title << ": new objects" << dumpVector(newManagementObjects));
+}
+
+Variant::Map ManagementAgent::toMap(const FieldTable& from)
+{
+ Variant::Map map;
+
+ for (FieldTable::const_iterator iter = from.begin(); iter != from.end(); iter++) {
+ const string& key(iter->first);
+ const FieldTable::ValuePtr& val(iter->second);
+
+ map[key] = toVariant(val);
+ }
+
+ return map;
+}
+
+Variant::List ManagementAgent::toList(const List& from)
+{
+ Variant::List _list;
+
+ for (List::const_iterator iter = from.begin(); iter != from.end(); iter++) {
+ const List::ValuePtr& val(*iter);
+
+ _list.push_back(toVariant(val));
+ }
+
+ return _list;
+}
+
+qpid::framing::FieldTable ManagementAgent::fromMap(const Variant::Map& from)
+{
+ qpid::framing::FieldTable ft;
+
+ for (Variant::Map::const_iterator iter = from.begin();
+ iter != from.end();
+ iter++) {
+ const string& key(iter->first);
+ const Variant& val(iter->second);
+
+ ft.set(key, toFieldValue(val));
+ }
+
+ return ft;
+}
+
+
+List ManagementAgent::fromList(const Variant::List& from)
+{
+ List fa;
+
+ for (Variant::List::const_iterator iter = from.begin();
+ iter != from.end();
+ iter++) {
+ const Variant& val(*iter);
+
+ fa.push_back(toFieldValue(val));
+ }
+
+ return fa;
+}
+
+
+boost::shared_ptr<FieldValue> ManagementAgent::toFieldValue(const Variant& in)
+{
+
+ switch(in.getType()) {
+
+ case types::VAR_VOID: return boost::shared_ptr<FieldValue>(new VoidValue());
+ case types::VAR_BOOL: return boost::shared_ptr<FieldValue>(new BoolValue(in.asBool()));
+ case types::VAR_UINT8: return boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8()));
+ case types::VAR_UINT16: return boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16()));
+ case types::VAR_UINT32: return boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32()));
+ case types::VAR_UINT64: return boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64()));
+ case types::VAR_INT8: return boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8()));
+ case types::VAR_INT16: return boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16()));
+ case types::VAR_INT32: return boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32()));
+ case types::VAR_INT64: return boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64()));
+ case types::VAR_FLOAT: return boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat()));
+ case types::VAR_DOUBLE: return boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble()));
+ case types::VAR_STRING: return boost::shared_ptr<FieldValue>(new Str16Value(in.asString()));
+ case types::VAR_UUID: return boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data()));
+ case types::VAR_MAP: return boost::shared_ptr<FieldValue>(new FieldTableValue(ManagementAgent::fromMap(in.asMap())));
+ case types::VAR_LIST: return boost::shared_ptr<FieldValue>(new ListValue(ManagementAgent::fromList(in.asList())));
+ }
+
+ QPID_LOG(error, "Unknown Variant type - not converted: [" << in.getType() << "]");
+ return boost::shared_ptr<FieldValue>(new VoidValue());
+}
+
+// stolen from qpid/client/amqp0_10/Codecs.cpp - TODO: make Codecs public, and remove this dup.
+Variant ManagementAgent::toVariant(const boost::shared_ptr<FieldValue>& in)
+{
+ const string iso885915("iso-8859-15");
+ const string utf8("utf8");
+ const string utf16("utf16");
+ //const string binary("binary");
+ const string amqp0_10_binary("amqp0-10:binary");
+ //const string amqp0_10_bit("amqp0-10:bit");
+ const string amqp0_10_datetime("amqp0-10:datetime");
+ const string amqp0_10_struct("amqp0-10:struct");
+ Variant out;
+
+ //based on AMQP 0-10 typecode, pick most appropriate variant type
+ switch (in->getType()) {
+ //Fixed Width types:
+ case 0x00: //bin8
+ case 0x01: out.setEncoding(amqp0_10_binary); // int8
+ case 0x02: out = in->getIntegerValue<int8_t>(); break; //uint8
+ case 0x03: out = in->getIntegerValue<uint8_t>(); break; //
+ // case 0x04: break; //TODO: iso-8859-15 char // char
+ case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t>()); break; // bool int8
+
+ case 0x10: out.setEncoding(amqp0_10_binary); // bin16
+ case 0x11: out = in->getIntegerValue<int16_t, 2>(); break; // int16
+ case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break; //uint16
+
+ case 0x20: out.setEncoding(amqp0_10_binary); // bin32
+ case 0x21: out = in->getIntegerValue<int32_t, 4>(); break; // int32
+ case 0x22: out = in->getIntegerValue<uint32_t, 4>(); break; // uint32
+
+ case 0x23: out = in->get<float>(); break; // float(32)
+
+ // case 0x27: break; //TODO: utf-32 char
+
+ case 0x30: out.setEncoding(amqp0_10_binary); // bin64
+ case 0x31: out = in->getIntegerValue<int64_t, 8>(); break; //int64
+
+ case 0x38: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding
+ case 0x32: out = in->getIntegerValue<uint64_t, 8>(); break; //uint64
+ case 0x33: out = in->get<double>(); break; // double
+
+ case 0x48: // uuid
+ {
+ unsigned char data[16];
+ in->getFixedWidthValue<16>(data);
+ out = qpid::types::Uuid(data);
+ } break;
+
+ //TODO: figure out whether and how to map values with codes 0x40-0xd8
+
+ case 0xf0: break;//void, which is the default value for Variant
+ // case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant
+
+ //Variable Width types:
+ //strings:
+ case 0x80: // str8
+ case 0x90: // str16
+ case 0xa0: // str32
+ out = in->get<string>();
+ out.setEncoding(amqp0_10_binary);
+ break;
+
+ case 0x84: // str8
+ case 0x94: // str16
+ out = in->get<string>();
+ out.setEncoding(iso885915);
+ break;
+
+ case 0x85: // str8
+ case 0x95: // str16
+ out = in->get<string>();
+ out.setEncoding(utf8);
+ break;
+
+ case 0x86: // str8
+ case 0x96: // str16
+ out = in->get<string>();
+ out.setEncoding(utf16);
+ break;
+
+ case 0xab: // str32
+ out = in->get<string>();
+ out.setEncoding(amqp0_10_struct);
+ break;
+
+ case 0xa8: // map
+ out = ManagementAgent::toMap(in->get<FieldTable>());
+ break;
+
+ case 0xa9: // list of variant types
+ out = ManagementAgent::toList(in->get<List>());
+ break;
+ //case 0xaa: //convert amqp0-10 array (uniform type) into variant list
+ // out = Variant::List();
+ // translate<Array>(in, out.asList(), &toVariant);
+ // break;
+
+ default:
+ //error?
+ QPID_LOG(error, "Unknown FieldValue type - not converted: [" << (unsigned int)(in->getType()) << "]");
+ break;
+ }
+
+ return out;
+}
+
+
+// Build up a list of the current set of deleted objects that are pending their
+// next (last) publish-ment.
+void ManagementAgent::exportDeletedObjects(DeletedObjectList& outList)
+{
+ outList.clear();
+
+ sys::Mutex::ScopedLock lock (userLock);
+
+ moveNewObjectsLH();
+ moveDeletedObjectsLH();
+
+ // now copy the pending deletes into the outList
+ for (PendingDeletedObjsMap::iterator mIter = pendingDeletedObjs.begin();
+ mIter != pendingDeletedObjs.end(); mIter++) {
+ for (DeletedObjectList::iterator lIter = mIter->second.begin();
+ lIter != mIter->second.end(); lIter++) {
+ outList.push_back(*lIter);
+ }
+ }
+}
+
+// Called by cluster to reset the management agent's list of deleted
+// objects to match the rest of the cluster.
+void ManagementAgent::importDeletedObjects(const DeletedObjectList& inList)
+{
+ sys::Mutex::ScopedLock lock (userLock);
+ // Clear out any existing deleted objects
+ moveNewObjectsLH();
+ pendingDeletedObjs.clear();
+ ManagementObjectMap::iterator i = managementObjects.begin();
+ // Silently drop any deleted objects left over from receiving the update.
+ while (i != managementObjects.end()) {
+ ManagementObject* object = i->second;
+ if (object->isDeleted()) {
+ delete object;
+ managementObjects.erase(i++);
+ }
+ else ++i;
+ }
+ for (DeletedObjectList::const_iterator lIter = inList.begin(); lIter != inList.end(); lIter++) {
+
+ std::string classkey((*lIter)->packageName + std::string(":") + (*lIter)->className);
+ pendingDeletedObjs[classkey].push_back(*lIter);
+ }
+}
+
+
+// construct a DeletedObject from a management object.
+ManagementAgent::DeletedObject::DeletedObject(ManagementObject *src, bool v1, bool v2)
+ : packageName(src->getPackageName()),
+ className(src->getClassName())
+{
+ bool send_stats = (src->hasInst() && (src->getInstChanged() || src->getForcePublish()));
+
+ stringstream oid;
+ oid << src->getObjectId();
+ objectId = oid.str();
+
+ if (v1) {
+ src->writeProperties(encodedV1Config);
+ if (send_stats) {
+ src->writeStatistics(encodedV1Inst);
+ }
+ }
+
+ if (v2) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oid;
+
+ src->getObjectId().mapEncode(oid);
+ map_["_object_id"] = oid;
+ map_["_schema_id"] = mapEncodeSchemaId(src->getPackageName(),
+ src->getClassName(),
+ "_data",
+ src->getMd5Sum());
+ src->writeTimestamps(map_);
+ src->mapEncodeValues(values, true, send_stats);
+ map_["_values"] = values;
+
+ encodedV2 = map_;
+ }
+}
+
+
+
+// construct a DeletedObject from an encoded representation. Used by
+// clustering to move deleted objects between clustered brokers. See
+// DeletedObject::encode() for the reverse.
+ManagementAgent::DeletedObject::DeletedObject(const std::string& encoded)
+{
+ qpid::types::Variant::Map map_;
+ MapCodec::decode(encoded, map_);
+
+ packageName = map_["_package_name"].getString();
+ className = map_["_class_name"].getString();
+ objectId = map_["_object_id"].getString();
+
+ encodedV1Config = map_["_v1_config"].getString();
+ encodedV1Inst = map_["_v1_inst"].getString();
+ encodedV2 = map_["_v2_data"].asMap();
+}
+
+
+// encode a DeletedObject to a string buffer. Used by
+// clustering to move deleted objects between clustered brokers. See
+// DeletedObject(const std::string&) for the reverse.
+void ManagementAgent::DeletedObject::encode(std::string& toBuffer)
+{
+ qpid::types::Variant::Map map_;
+
+
+ map_["_package_name"] = packageName;
+ map_["_class_name"] = className;
+ map_["_object_id"] = objectId;
+
+ map_["_v1_config"] = encodedV1Config;
+ map_["_v1_inst"] = encodedV1Inst;
+ map_["_v2_data"] = encodedV2;
+
+ MapCodec::encode(map_, toBuffer);
+}
+
+// Remove Deleted objects, and save for later publishing...
+bool ManagementAgent::moveDeletedObjectsLH() {
+ typedef vector<pair<ObjectId, ManagementObject*> > DeleteList;
+ DeleteList deleteList;
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ ++iter)
+ {
+ ManagementObject* object = iter->second;
+ if (object->isDeleted()) deleteList.push_back(*iter);
+ }
+
+ // Iterate in reverse over deleted object list
+ for (DeleteList::reverse_iterator iter = deleteList.rbegin();
+ iter != deleteList.rend();
+ iter++)
+ {
+ ManagementObject* delObj = iter->second;
+ assert(delObj->isDeleted());
+ DeletedObject::shared_ptr dptr(new DeletedObject(delObj, qmf1Support, qmf2Support));
+
+ pendingDeletedObjs[dptr->getKey()].push_back(dptr);
+ managementObjects.erase(iter->first);
+ delete iter->second;
+ }
+ return !deleteList.empty();
+}
+
+namespace qpid {
+namespace management {
+
+namespace {
+QPID_TSS const qpid::broker::ConnectionState* executionContext = 0;
+}
+
+void setManagementExecutionContext(const qpid::broker::ConnectionState* ctxt)
+{
+ executionContext = ctxt;
+}
+const qpid::broker::ConnectionState* getManagementExecutionContext()
+{
+ return executionContext;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.h b/qpid/cpp/src/qpid/management/ManagementAgent.h
new file mode 100644
index 0000000000..fb15dc6ed1
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementAgent.h
@@ -0,0 +1,432 @@
+#ifndef _ManagementAgent_
+#define _ManagementAgent_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/Options.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/broker/ConnectionToken.h"
+#include "qpid/management/ManagementObject.h"
+#include "qpid/management/ManagementEvent.h"
+#include "qpid/management/Manageable.h"
+#include "qmf/org/apache/qpid/broker/Agent.h"
+#include "qpid/types/Variant.h"
+#include <qpid/framing/AMQFrame.h>
+#include <qpid/framing/FieldValue.h>
+#include <qpid/framing/ResizableBuffer.h>
+#include <memory>
+#include <string>
+#include <map>
+
+namespace qpid {
+namespace broker {
+class ConnectionState;
+}
+namespace management {
+
+class ManagementAgent
+{
+private:
+
+ int threadPoolSize;
+
+public:
+ typedef enum {
+ SEV_EMERG = 0,
+ SEV_ALERT = 1,
+ SEV_CRIT = 2,
+ SEV_ERROR = 3,
+ SEV_WARN = 4,
+ SEV_NOTE = 5,
+ SEV_INFO = 6,
+ SEV_DEBUG = 7,
+ SEV_DEFAULT = 8
+ } severity_t;
+
+
+ ManagementAgent (const bool qmfV1, const bool qmfV2);
+ virtual ~ManagementAgent ();
+
+ /** Called before plugins are initialized */
+ void configure (const std::string& dataDir, uint16_t interval,
+ qpid::broker::Broker* broker, int threadPoolSize);
+ /** Called after plugins are initialized. */
+ void pluginsInitialized();
+
+ /** Called by cluster to suppress management output during update. */
+ void suppress(bool s) { suppressed = s; }
+
+ void setName(const std::string& vendor,
+ const std::string& product,
+ const std::string& instance="");
+ void getName(std::string& vendor, std::string& product, std::string& instance);
+ const std::string& getAddress();
+
+ void setInterval(uint16_t _interval) { interval = _interval; }
+ void setExchange(qpid::broker::Exchange::shared_ptr mgmtExchange,
+ qpid::broker::Exchange::shared_ptr directExchange);
+ void setExchangeV2(qpid::broker::Exchange::shared_ptr topicExchange,
+ qpid::broker::Exchange::shared_ptr directExchange);
+
+ int getMaxThreads () { return threadPoolSize; }
+ QPID_BROKER_EXTERN void registerClass (const std::string& packageName,
+ const std::string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall);
+ QPID_BROKER_EXTERN void registerEvent (const std::string& packageName,
+ const std::string& eventName,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall);
+ QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object,
+ uint64_t persistId = 0,
+ bool persistent = false);
+ QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object,
+ const std::string& key,
+ bool persistent = false);
+ QPID_BROKER_EXTERN void raiseEvent(const ManagementEvent& event,
+ severity_t severity = SEV_DEFAULT);
+ QPID_BROKER_EXTERN void clientAdded (const std::string& routingKey);
+
+ QPID_BROKER_EXTERN void clusterUpdate();
+
+ bool dispatchCommand (qpid::broker::Deliverable& msg,
+ const std::string& routingKey,
+ const framing::FieldTable* args,
+ const bool topic,
+ int qmfVersion);
+
+ /** Disallow a method. Attempts to call it will receive an exception with message. */
+ void disallow(const std::string& className, const std::string& methodName, const std::string& message);
+
+ /** Disallow all QMFv1 methods (used in clustered brokers). */
+ void disallowV1Methods() { disallowAllV1Methods = true; }
+
+ /** Serialize my schemas as a binary blob into schemaOut */
+ void exportSchemas(std::string& schemaOut);
+
+ /** Serialize my remote-agent map as a binary blob into agentsOut */
+ void exportAgents(std::string& agentsOut);
+
+ /** Decode a serialized schemas and add to my schema cache */
+ void importSchemas(framing::Buffer& inBuf);
+
+ /** Decode a serialized agent map */
+ void importAgents(framing::Buffer& inBuf);
+
+ // these are in support of the managementSetup-state stuff, for synch'ing clustered brokers
+ uint64_t getNextObjectId(void) { return nextObjectId; }
+ void setNextObjectId(uint64_t o) { nextObjectId = o; }
+
+ uint16_t getBootSequence(void) { return bootSequence; }
+ void setBootSequence(uint16_t b) { bootSequence = b; writeData(); }
+
+ const framing::Uuid& getUuid() const { return uuid; }
+ void setUuid(const framing::Uuid& id) { uuid = id; writeData(); }
+
+ // TODO: remove these when Variant API moved into common library.
+ static types::Variant::Map toMap(const framing::FieldTable& from);
+ static framing::FieldTable fromMap(const types::Variant::Map& from);
+ static types::Variant::List toList(const framing::List& from);
+ static framing::List fromList(const types::Variant::List& from);
+ static boost::shared_ptr<framing::FieldValue> toFieldValue(const types::Variant& in);
+ static types::Variant toVariant(const boost::shared_ptr<framing::FieldValue>& val);
+
+ // For Clustering: management objects that have been marked as
+ // "deleted", but are waiting for their last published object
+ // update are not visible to the cluster replication code. These
+ // interfaces allow clustering to gather up all the management
+ // objects that are deleted in order to allow all clustered
+ // brokers to publish the same set of deleted objects.
+
+ class DeletedObject {
+ public:
+ typedef boost::shared_ptr<DeletedObject> shared_ptr;
+ DeletedObject(ManagementObject *, bool v1, bool v2);
+ DeletedObject( const std::string &encoded );
+ ~DeletedObject() {};
+ void encode( std::string& toBuffer );
+ const std::string getKey() const {
+ // used to batch up objects of the same class type
+ return std::string(packageName + std::string(":") + className);
+ }
+
+ private:
+ friend class ManagementAgent;
+
+ std::string packageName;
+ std::string className;
+ std::string objectId;
+
+ std::string encodedV1Config; // qmfv1 properties
+ std::string encodedV1Inst; // qmfv1 statistics
+ qpid::types::Variant::Map encodedV2;
+ };
+
+ typedef std::vector<DeletedObject::shared_ptr> DeletedObjectList;
+
+ /** returns a snapshot of all currently deleted management objects. */
+ void exportDeletedObjects( DeletedObjectList& outList );
+
+ /** Import a list of deleted objects to send on next publish interval. */
+ void importDeletedObjects( const DeletedObjectList& inList );
+
+private:
+ struct Periodic : public qpid::sys::TimerTask
+ {
+ ManagementAgent& agent;
+
+ Periodic (ManagementAgent& agent, uint32_t seconds);
+ virtual ~Periodic ();
+ void fire ();
+ };
+
+ // Storage for tracking remote management agents, attached via the client
+ // management agent API.
+ //
+ struct RemoteAgent : public Manageable
+ {
+ ManagementAgent& agent;
+ uint32_t brokerBank;
+ uint32_t agentBank;
+ std::string routingKey;
+ ObjectId connectionRef;
+ qmf::org::apache::qpid::broker::Agent* mgmtObject;
+ RemoteAgent(ManagementAgent& _agent) : agent(_agent), mgmtObject(0) {}
+ ManagementObject* GetManagementObject (void) const { return mgmtObject; }
+
+ virtual ~RemoteAgent ();
+ void mapEncode(qpid::types::Variant::Map& _map) const;
+ void mapDecode(const qpid::types::Variant::Map& _map);
+ };
+
+ typedef std::map<ObjectId, boost::shared_ptr<RemoteAgent> > RemoteAgentMap;
+
+ // Storage for known schema classes:
+ //
+ // SchemaClassKey -- Key elements for map lookups
+ // SchemaClassKeyComp -- Comparison class for SchemaClassKey
+ // SchemaClass -- Non-key elements for classes
+ //
+ struct SchemaClassKey
+ {
+ std::string name;
+ uint8_t hash[16];
+
+ void mapEncode(qpid::types::Variant::Map& _map) const;
+ void mapDecode(const qpid::types::Variant::Map& _map);
+ void encode(framing::Buffer& buffer) const;
+ void decode(framing::Buffer& buffer);
+ uint32_t encodedBufSize() const;
+ };
+
+ struct SchemaClassKeyComp
+ {
+ bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const
+ {
+ if (lhs.name != rhs.name)
+ return lhs.name < rhs.name;
+ else
+ for (int i = 0; i < 16; i++)
+ if (lhs.hash[i] != rhs.hash[i])
+ return lhs.hash[i] < rhs.hash[i];
+ return false;
+ }
+ };
+
+
+ struct SchemaClass
+ {
+ uint8_t kind;
+ ManagementObject::writeSchemaCall_t writeSchemaCall;
+ std::string data;
+ uint32_t pendingSequence;
+
+ SchemaClass(uint8_t _kind=0, uint32_t seq=0) :
+ kind(_kind), writeSchemaCall(0), pendingSequence(seq) {}
+ SchemaClass(uint8_t _kind, ManagementObject::writeSchemaCall_t call) :
+ kind(_kind), writeSchemaCall(call), pendingSequence(0) {}
+ bool hasSchema () { return (writeSchemaCall != 0) || !data.empty(); }
+ void appendSchema (framing::Buffer& buf);
+
+ void mapEncode(qpid::types::Variant::Map& _map) const;
+ void mapDecode(const qpid::types::Variant::Map& _map);
+ };
+
+ typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap;
+ typedef std::map<std::string, ClassMap> PackageMap;
+
+ RemoteAgentMap remoteAgents;
+ PackageMap packages;
+
+ //
+ // Protected by userLock
+ //
+ ManagementObjectMap managementObjects;
+
+ //
+ // Protected by addLock
+ //
+ ManagementObjectVector newManagementObjects;
+
+ framing::Uuid uuid;
+
+ //
+ // Lock hierarchy: If a thread needs to take both addLock and userLock,
+ // it MUST take userLock first, then addLock.
+ //
+ sys::Mutex userLock;
+ sys::Mutex addLock;
+
+ qpid::broker::Exchange::shared_ptr mExchange;
+ qpid::broker::Exchange::shared_ptr dExchange;
+ qpid::broker::Exchange::shared_ptr v2Topic;
+ qpid::broker::Exchange::shared_ptr v2Direct;
+ std::string dataDir;
+ uint16_t interval;
+ qpid::broker::Broker* broker;
+ qpid::sys::Timer* timer;
+ uint16_t bootSequence;
+ uint32_t nextObjectId;
+ uint32_t brokerBank;
+ uint32_t nextRemoteBank;
+ uint32_t nextRequestSequence;
+ bool clientWasAdded;
+ const qpid::sys::AbsTime startTime;
+ bool suppressed;
+
+ typedef std::pair<std::string,std::string> MethodName;
+ typedef std::map<MethodName, std::string> DisallowedMethods;
+ DisallowedMethods disallowed;
+ bool disallowAllV1Methods;
+
+ // Agent name and address
+ qpid::types::Variant::Map attrMap;
+ std::string name_address;
+ std::string vendorNameKey; // "." --> "_"
+ std::string productNameKey; // "." --> "_"
+ std::string instanceNameKey; // "." --> "_"
+
+ // supported management protocol
+ bool qmf1Support;
+ bool qmf2Support;
+
+ // Maximum # of objects allowed in a single V2 response
+ // message.
+ uint32_t maxReplyObjs;
+
+ // list of objects that have been deleted, but have yet to be published
+ // one final time.
+ // Indexed by a string composed of the object's package and class name.
+ // Protected by userLock.
+ typedef std::map<std::string, DeletedObjectList> PendingDeletedObjsMap;
+ PendingDeletedObjsMap pendingDeletedObjs;
+
+# define MA_BUFFER_SIZE 65536
+ char inputBuffer[MA_BUFFER_SIZE];
+ char outputBuffer[MA_BUFFER_SIZE];
+ char eventBuffer[MA_BUFFER_SIZE];
+ framing::ResizableBuffer msgBuffer;
+
+ void writeData ();
+ void periodicProcessing (void);
+ void deleteObjectNowLH(const ObjectId& oid);
+ void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
+ bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
+ void sendBufferLH(framing::Buffer& buf,
+ uint32_t length,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const std::string& routingKey);
+ void sendBufferLH(framing::Buffer& buf,
+ uint32_t length,
+ const std::string& exchange,
+ const std::string& routingKey);
+ void sendBufferLH(const std::string& data,
+ const std::string& cid,
+ const qpid::types::Variant::Map& headers,
+ const std::string& content_type,
+ qpid::broker::Exchange::shared_ptr exchange,
+ const std::string& routingKey,
+ uint64_t ttl_msec = 0);
+ void sendBufferLH(const std::string& data,
+ const std::string& cid,
+ const qpid::types::Variant::Map& headers,
+ const std::string& content_type,
+ const std::string& exchange,
+ const std::string& routingKey,
+ uint64_t ttl_msec = 0);
+ void moveNewObjectsLH();
+ bool moveDeletedObjectsLH();
+
+ bool authorizeAgentMessageLH(qpid::broker::Message& msg);
+ void dispatchAgentCommandLH(qpid::broker::Message& msg, bool viaLocal=false);
+
+ PackageMap::iterator findOrAddPackageLH(std::string name);
+ void addClassLH(uint8_t kind,
+ PackageMap::iterator pIter,
+ const std::string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall);
+ void encodePackageIndication (framing::Buffer& buf,
+ PackageMap::iterator pIter);
+ void encodeClassIndication (framing::Buffer& buf,
+ const std::string packageName,
+ const struct SchemaClassKey key,
+ uint8_t kind);
+ bool bankInUse (uint32_t bank);
+ uint32_t allocateNewBank ();
+ uint32_t assignBankLH (uint32_t requestedPrefix);
+ void deleteOrphanedAgentsLH();
+ void sendCommandCompleteLH(const std::string& replyToKey, uint32_t sequence,
+ uint32_t code = 0, const std::string& text = "OK");
+ void sendExceptionLH(const std::string& rte, const std::string& rtk, const std::string& cid, const std::string& text, uint32_t code=1, bool viaLocal=false);
+ void handleBrokerRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handlePackageQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handlePackageIndLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleClassQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleClassIndLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleSchemaRequestLH (framing::Buffer& inBuffer, const std::string& replyToEx, const std::string& replyToKey, uint32_t sequence);
+ void handleSchemaResponseLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleAttachRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
+ void handleGetQueryLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence);
+ void handleMethodRequestLH (framing::Buffer& inBuffer, const std::string& replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
+ void handleGetQueryLH (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, bool viaLocal);
+ void handleMethodRequestLH (const std::string& body, const std::string& replyToEx, const std::string& replyToKey, const std::string& cid, const qpid::broker::ConnectionToken* connToken, bool viaLocal);
+ void handleLocateRequestLH (const std::string& body, const std::string& replyToEx, const std::string &replyToKey, const std::string& cid);
+
+
+ size_t validateSchema(framing::Buffer&, uint8_t kind);
+ size_t validateTableSchema(framing::Buffer&);
+ size_t validateEventSchema(framing::Buffer&);
+ ManagementObjectMap::iterator numericFind(const ObjectId& oid);
+
+ std::string summarizeAgents();
+ void debugSnapshot(const char* title);
+};
+
+void setManagementExecutionContext(const qpid::broker::ConnectionState*);
+const qpid::broker::ConnectionState* getManagementExecutionContext();
+}}
+
+#endif /*!_ManagementAgent_*/
diff --git a/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp
new file mode 100644
index 0000000000..1d5f8bbd6b
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/management/ManagementDirectExchange.h"
+#include "qpid/log/Statement.h"
+#include <assert.h>
+
+using namespace qpid::management;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+ManagementDirectExchange::ManagementDirectExchange(const string& _name, Manageable* _parent, Broker* b) :
+ Exchange (_name, _parent, b),
+ DirectExchange(_name, _parent, b),
+ managementAgent(0) {}
+ManagementDirectExchange::ManagementDirectExchange(const std::string& _name,
+ bool _durable,
+ const FieldTable& _args,
+ Manageable* _parent, Broker* b) :
+ Exchange (_name, _durable, _args, _parent, b),
+ DirectExchange(_name, _durable, _args, _parent, b),
+ managementAgent(0) {}
+
+void ManagementDirectExchange::route(Deliverable& msg,
+ const string& routingKey,
+ const FieldTable* args)
+{
+ bool routeIt = true;
+
+ if (managementAgent)
+ routeIt = managementAgent->dispatchCommand(msg, routingKey, args, false, qmfVersion);
+
+ if (routeIt)
+ DirectExchange::route(msg, routingKey, args);
+}
+
+void ManagementDirectExchange::setManagmentAgent(ManagementAgent* agent, int qv)
+{
+ managementAgent = agent;
+ qmfVersion = qv;
+ assert(qmfVersion == 2); // QMFv1 doesn't use a specialized direct exchange
+}
+
+
+ManagementDirectExchange::~ManagementDirectExchange() {}
+
+const std::string ManagementDirectExchange::typeName("management-direct");
+
diff --git a/qpid/cpp/src/qpid/management/ManagementDirectExchange.h b/qpid/cpp/src/qpid/management/ManagementDirectExchange.h
new file mode 100644
index 0000000000..7507179c06
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementDirectExchange.h
@@ -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.
+ *
+ */
+#ifndef _ManagementDirectExchange_
+#define _ManagementDirectExchange_
+
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/management/ManagementAgent.h"
+
+namespace qpid {
+namespace broker {
+
+class ManagementDirectExchange : public virtual DirectExchange
+{
+ private:
+ management::ManagementAgent* managementAgent;
+ int qmfVersion;
+
+ public:
+ static const std::string typeName;
+
+ ManagementDirectExchange(const std::string& name, Manageable* _parent = 0, Broker* broker = 0);
+ ManagementDirectExchange(const std::string& _name, bool _durable,
+ const qpid::framing::FieldTable& _args,
+ Manageable* _parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion);
+
+ virtual ~ManagementDirectExchange();
+};
+
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/management/ManagementObject.cpp b/qpid/cpp/src/qpid/management/ManagementObject.cpp
new file mode 100644
index 0000000000..b4d469afbe
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementObject.cpp
@@ -0,0 +1,385 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementObject.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/log/Statement.h"
+#include <boost/lexical_cast.hpp>
+
+#include <stdlib.h>
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::management;
+
+void AgentAttachment::setBanks(uint32_t broker, uint32_t bank)
+{
+ first =
+ ((uint64_t) (broker & 0x000fffff)) << 28 |
+ ((uint64_t) (bank & 0x0fffffff));
+}
+
+// Deprecated
+ObjectId::ObjectId(uint8_t flags, uint16_t seq, uint32_t broker, uint64_t object)
+ : agent(0), agentEpoch(seq)
+{
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48 |
+ ((uint64_t) (broker & 0x000fffff)) << 28;
+ second = object;
+}
+
+
+ObjectId::ObjectId(uint8_t flags, uint16_t seq, uint32_t broker)
+ : agent(0), second(0), agentEpoch(seq)
+{
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48 |
+ ((uint64_t) (broker & 0x000fffff)) << 28;
+}
+
+ObjectId::ObjectId(AgentAttachment* _agent, uint8_t flags, uint16_t seq)
+ : agent(_agent), second(0), agentEpoch(seq)
+{
+
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48;
+}
+
+
+ObjectId::ObjectId(istream& in) : agent(0)
+{
+ string text;
+ in >> text;
+ fromString(text);
+}
+
+ObjectId::ObjectId(const string& text) : agent(0)
+{
+ fromString(text);
+}
+
+void ObjectId::fromString(const string& text)
+{
+#define FIELDS 5
+#if defined (_WIN32) && !defined (atoll)
+# define atoll(X) _atoi64(X)
+#endif
+
+ // format:
+ // V1: <flags>-<sequence>-<broker-bank>-<agent-bank>-<uint64-app-id>
+ // V2: Not used
+
+ string copy(text.c_str());
+ char* cText;
+ char* field[FIELDS];
+ bool atFieldStart = true;
+ int idx = 0;
+
+ cText = const_cast<char*>(copy.c_str());
+ for (char* cursor = cText; *cursor; cursor++) {
+ if (atFieldStart) {
+ if (idx >= FIELDS)
+ throw Exception("Invalid ObjectId format");
+ field[idx++] = cursor;
+ atFieldStart = false;
+ } else {
+ if (*cursor == '-') {
+ *cursor = '\0';
+ atFieldStart = true;
+ }
+ }
+ }
+
+ if (idx != FIELDS)
+ throw Exception("Invalid ObjectId format");
+
+ agentEpoch = atoll(field[1]);
+
+ first = (atoll(field[0]) << 60) +
+ (atoll(field[1]) << 48) +
+ (atoll(field[2]) << 28);
+
+ agentName = string(field[3]);
+ second = atoll(field[4]);
+}
+
+
+bool ObjectId::operator==(const ObjectId &other) const
+{
+ return v2Key == other.v2Key;
+}
+
+bool ObjectId::operator<(const ObjectId &other) const
+{
+ return v2Key < other.v2Key;
+}
+
+bool ObjectId::equalV1(const ObjectId &other) const
+{
+ uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL;
+ return first == otherFirst && second == other.second;
+}
+
+// encode as V1-format binary
+void ObjectId::encode(string& buffer) const
+{
+ const uint32_t len = 16;
+ char _data[len];
+ qpid::framing::Buffer body(_data, len);
+
+ if (agent == 0)
+ body.putLongLong(first);
+ else
+ body.putLongLong(first | agent->first);
+ body.putLongLong(second);
+
+ body.reset();
+ body.getRawData(buffer, len);
+}
+
+// decode as V1-format binary
+void ObjectId::decode(const string& buffer)
+{
+ const uint32_t len = 16;
+ char _data[len];
+ qpid::framing::Buffer body(_data, len);
+
+ body.checkAvailable(buffer.length());
+ body.putRawData(buffer);
+ body.reset();
+ first = body.getLongLong();
+ second = body.getLongLong();
+ v2Key = boost::lexical_cast<string>(second);
+}
+
+// generate the V2 key from the index fields defined
+// in the schema.
+void ObjectId::setV2Key(const ManagementObject& object)
+{
+ stringstream oname;
+ oname << object.getPackageName() << ":" << object.getClassName() << ":" << object.getKey();
+ v2Key = oname.str();
+}
+
+
+// encode as V2-format map
+void ObjectId::mapEncode(types::Variant::Map& map) const
+{
+ map["_object_name"] = v2Key;
+ if (!agentName.empty())
+ map["_agent_name"] = agentName;
+ if (agentEpoch)
+ map["_agent_epoch"] = agentEpoch;
+}
+
+// decode as v2-format map
+void ObjectId::mapDecode(const types::Variant::Map& map)
+{
+ types::Variant::Map::const_iterator i;
+
+ if ((i = map.find("_object_name")) != map.end())
+ v2Key = i->second.asString();
+ else
+ throw Exception("Required _object_name field missing.");
+
+ if ((i = map.find("_agent_name")) != map.end())
+ agentName = i->second.asString();
+
+ if ((i = map.find("_agent_epoch")) != map.end())
+ agentEpoch = i->second.asInt64();
+}
+
+
+ObjectId::operator types::Variant::Map() const
+{
+ types::Variant::Map m;
+ mapEncode(m);
+ return m;
+}
+
+
+
+namespace qpid {
+namespace management {
+
+ostream& operator<<(ostream& out, const ObjectId& i)
+{
+ uint64_t virtFirst = i.first;
+ if (i.agent)
+ virtFirst |= i.agent->getFirst();
+
+ out << ((virtFirst & 0xF000000000000000LL) >> 60) <<
+ "-" << ((virtFirst & 0x0FFF000000000000LL) >> 48) <<
+ "-" << ((virtFirst & 0x0000FFFFF0000000LL) >> 28) <<
+ "-" << i.agentName <<
+ "-" << i.second <<
+ "(" << i.v2Key << ")";
+ return out;
+}
+
+}}
+
+ManagementObject::ManagementObject(Manageable* _core) :
+createTime(qpid::sys::Duration(sys::EPOCH, sys::now())),
+ destroyTime(0), updateTime(createTime), configChanged(true),
+ instChanged(true), deleted(false),
+ coreObject(_core), flags(0), forcePublish(false) {}
+
+void ManagementObject::setUpdateTime()
+{
+ updateTime = sys::Duration(sys::EPOCH, sys::now());
+}
+
+void ManagementObject::resourceDestroy()
+{
+ QPID_LOG(trace, "Management object marked deleted: " << getObjectId().getV2Key());
+ destroyTime = sys::Duration(sys::EPOCH, sys::now());
+ deleted = true;
+}
+
+int ManagementObject::maxThreads = 1;
+int ManagementObject::nextThreadIndex = 0;
+
+void ManagementObject::writeTimestamps (string& buf) const
+{
+ char _data[4000];
+ qpid::framing::Buffer body(_data, 4000);
+
+ body.putShortString (getPackageName ());
+ body.putShortString (getClassName ());
+ body.putBin128 (getMd5Sum ());
+ body.putLongLong (updateTime);
+ body.putLongLong (createTime);
+ body.putLongLong (destroyTime);
+
+ uint32_t len = body.getPosition();
+ body.reset();
+ body.getRawData(buf, len);
+
+ string oid;
+ objectId.encode(oid);
+ buf += oid;
+}
+
+void ManagementObject::readTimestamps (const string& buf)
+{
+ char _data[4000];
+ qpid::framing::Buffer body(_data, 4000);
+ string unused;
+ uint8_t unusedUuid[16];
+
+ body.checkAvailable(buf.length());
+ body.putRawData(buf);
+ body.reset();
+
+ body.getShortString(unused);
+ body.getShortString(unused);
+ body.getBin128(unusedUuid);
+ updateTime = body.getLongLong();
+ createTime = body.getLongLong();
+ destroyTime = body.getLongLong();
+}
+
+uint32_t ManagementObject::writeTimestampsSize() const
+{
+ return 1 + getPackageName().length() + // str8
+ 1 + getClassName().length() + // str8
+ 16 + // bin128
+ 8 + // uint64
+ 8 + // uint64
+ 8 + // uint64
+ objectId.encodedSize(); // objectId
+}
+
+
+void ManagementObject::writeTimestamps (types::Variant::Map& map) const
+{
+ // types::Variant::Map oid, sid;
+
+ // sid["_package_name"] = getPackageName();
+ // sid["_class_name"] = getClassName();
+ // sid["_hash"] = qpid::types::Uuid(getMd5Sum());
+ // map["_schema_id"] = sid;
+
+ // objectId.mapEncode(oid);
+ // map["_object_id"] = oid;
+
+ map["_update_ts"] = updateTime;
+ map["_create_ts"] = createTime;
+ map["_delete_ts"] = destroyTime;
+}
+
+void ManagementObject::readTimestamps (const types::Variant::Map& map)
+{
+ types::Variant::Map::const_iterator i;
+
+ if ((i = map.find("_update_ts")) != map.end())
+ updateTime = i->second.asUint64();
+ if ((i = map.find("_create_ts")) != map.end())
+ createTime = i->second.asUint64();
+ if ((i = map.find("_delete_ts")) != map.end())
+ destroyTime = i->second.asUint64();
+}
+
+
+void ManagementObject::setReference(ObjectId) {}
+
+int ManagementObject::getThreadIndex() {
+ static QPID_TSS int thisIndex = -1;
+ if (thisIndex == -1) {
+ Mutex::ScopedLock mutex(accessLock);
+ thisIndex = nextThreadIndex;
+ if (nextThreadIndex < maxThreads - 1)
+ nextThreadIndex++;
+ }
+ return thisIndex;
+}
+
+
+// void ManagementObject::mapEncode(types::Variant::Map& map,
+// bool includeProperties,
+// bool includeStatistics)
+// {
+// types::Variant::Map values;
+
+// writeTimestamps(map);
+
+// mapEncodeValues(values, includeProperties, includeStatistics);
+// map["_values"] = values;
+// }
+
+// void ManagementObject::mapDecode(const types::Variant::Map& map)
+// {
+// types::Variant::Map::const_iterator i;
+
+// readTimestamps(map);
+
+// if ((i = map.find("_values")) != map.end())
+// mapDecodeValues(i->second.asMap());
+// }
diff --git a/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp
new file mode 100644
index 0000000000..ee8657646f
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/management/ManagementTopicExchange.h"
+#include "qpid/log/Statement.h"
+
+using namespace qpid::management;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+ManagementTopicExchange::ManagementTopicExchange(const string& _name, Manageable* _parent, Broker* b) :
+ Exchange (_name, _parent, b),
+ TopicExchange(_name, _parent, b),
+ managementAgent(0) {}
+ManagementTopicExchange::ManagementTopicExchange(const std::string& _name,
+ bool _durable,
+ const FieldTable& _args,
+ Manageable* _parent, Broker* b) :
+ Exchange (_name, _durable, _args, _parent, b),
+ TopicExchange(_name, _durable, _args, _parent, b),
+ managementAgent(0) {}
+
+void ManagementTopicExchange::route(Deliverable& msg,
+ const string& routingKey,
+ const FieldTable* args)
+{
+ bool routeIt = true;
+
+ // Intercept management agent commands
+ if (managementAgent)
+ routeIt = managementAgent->dispatchCommand(msg, routingKey, args, true, qmfVersion);
+
+ if (routeIt)
+ TopicExchange::route(msg, routingKey, args);
+}
+
+bool ManagementTopicExchange::bind(Queue::shared_ptr queue,
+ const string& routingKey,
+ const qpid::framing::FieldTable* args)
+{
+ if (qmfVersion == 1)
+ managementAgent->clientAdded(routingKey);
+ return TopicExchange::bind(queue, routingKey, args);
+}
+
+void ManagementTopicExchange::setManagmentAgent(ManagementAgent* agent, int qv)
+{
+ managementAgent = agent;
+ qmfVersion = qv;
+}
+
+
+ManagementTopicExchange::~ManagementTopicExchange() {}
+
+const std::string ManagementTopicExchange::typeName("management-topic");
+
diff --git a/qpid/cpp/src/qpid/management/ManagementTopicExchange.h b/qpid/cpp/src/qpid/management/ManagementTopicExchange.h
new file mode 100644
index 0000000000..232300265e
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/ManagementTopicExchange.h
@@ -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.
+ *
+ */
+#ifndef _ManagementTopicExchange_
+#define _ManagementTopicExchange_
+
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/management/ManagementAgent.h"
+
+namespace qpid {
+namespace broker {
+
+class ManagementTopicExchange : public virtual TopicExchange
+{
+ private:
+ management::ManagementAgent* managementAgent;
+ int qmfVersion;
+
+ public:
+ static const std::string typeName;
+
+ ManagementTopicExchange(const std::string& name, Manageable* _parent = 0, Broker* broker = 0);
+ ManagementTopicExchange(const std::string& _name, bool _durable,
+ const qpid::framing::FieldTable& _args,
+ Manageable* _parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ virtual void route(Deliverable& msg,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ virtual bool bind(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable* args);
+
+ void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion);
+
+ virtual ~ManagementTopicExchange();
+};
+
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/management/Mutex.cpp b/qpid/cpp/src/qpid/management/Mutex.cpp
new file mode 100644
index 0000000000..f05abb01dc
--- /dev/null
+++ b/qpid/cpp/src/qpid/management/Mutex.cpp
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+
+#include "qpid/management/Mutex.h"
+#include "qpid/sys/Mutex.h"
+
+using namespace std;
+using namespace qpid::management;
+
+Mutex::Mutex() : impl(new sys::Mutex()) {}
+Mutex::~Mutex() { delete impl; }
+void Mutex::lock() { impl->lock(); }
+void Mutex::unlock() { impl->unlock(); }
+
diff --git a/qpid/cpp/src/qpid/memory.h b/qpid/cpp/src/qpid/memory.h
new file mode 100644
index 0000000000..99d7a71e7b
--- /dev/null
+++ b/qpid/cpp/src/qpid/memory.h
@@ -0,0 +1,32 @@
+#ifndef QPID_AUTO_PTR_H
+#define QPID_AUTO_PTR_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <memory>
+namespace qpid {
+/** Convenient template for creating auto_ptr in-place in an argument list. */
+template <class T>
+std::auto_ptr<T> make_auto_ptr(T* ptr) { return std::auto_ptr<T>(ptr); }
+
+} // namespace qpid
+
+
+
+#endif /*!QPID_AUTO_PTR_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp
new file mode 100644
index 0000000000..a516959edb
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Address.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Address.h"
+#include "qpid/framing/Uuid.h"
+#include <sstream>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace messaging {
+
+using namespace qpid::types;
+
+namespace {
+const std::string SUBJECT_DIVIDER = "/";
+const std::string OPTIONS_DIVIDER = ";";
+const std::string SPACE = " ";
+const std::string TYPE = "type";
+}
+class AddressImpl
+{
+ public:
+ std::string name;
+ std::string subject;
+ Variant::Map options;
+
+ AddressImpl() {}
+ AddressImpl(const std::string& n, const std::string& s, const Variant::Map& o) :
+ name(n), subject(s), options(o) {}
+};
+
+class AddressParser
+{
+ public:
+ AddressParser(const std::string&);
+ bool parse(Address& address);
+ private:
+ const std::string& input;
+ std::string::size_type current;
+ static const std::string RESERVED;
+
+ bool readChar(char c);
+ bool readQuotedString(std::string& s);
+ bool readQuotedValue(Variant& value);
+ bool readString(std::string& value, char delimiter);
+ bool readWord(std::string& word, const std::string& delims = RESERVED);
+ bool readSimpleValue(Variant& word);
+ bool readKey(std::string& key);
+ bool readValue(Variant& value);
+ bool readKeyValuePair(Variant::Map& map);
+ bool readMap(Variant& value);
+ bool readList(Variant& value);
+ bool readName(std::string& name);
+ bool readSubject(std::string& subject);
+ bool error(const std::string& message);
+ bool eos();
+ bool iswhitespace();
+ bool in(const std::string& delims);
+ bool isreserved();
+};
+
+Address::Address() : impl(new AddressImpl()) {}
+Address::Address(const std::string& address) : impl(new AddressImpl())
+{
+ AddressParser parser(address);
+ parser.parse(*this);
+}
+Address::Address(const std::string& name, const std::string& subject, const Variant::Map& options,
+ const std::string& type)
+ : impl(new AddressImpl(name, subject, options)) { setType(type); }
+Address::Address(const Address& a) :
+ impl(new AddressImpl(a.impl->name, a.impl->subject, a.impl->options)) {}
+Address::~Address() { delete impl; }
+
+Address& Address::operator=(const Address& a) { *impl = *a.impl; return *this; }
+
+
+std::string Address::str() const
+{
+ std::stringstream out;
+ out << impl->name;
+ if (!impl->subject.empty()) out << SUBJECT_DIVIDER << impl->subject;
+ if (!impl->options.empty()) out << OPTIONS_DIVIDER << impl->options;
+ return out.str();
+}
+Address::operator bool() const { return !impl->name.empty(); }
+bool Address::operator !() const { return impl->name.empty(); }
+
+const std::string& Address::getName() const { return impl->name; }
+void Address::setName(const std::string& name) { impl->name = name; }
+const std::string& Address::getSubject() const { return impl->subject; }
+void Address::setSubject(const std::string& subject) { impl->subject = subject; }
+const Variant::Map& Address::getOptions() const { return impl->options; }
+Variant::Map& Address::getOptions() { return impl->options; }
+void Address::setOptions(const Variant::Map& options) { impl->options = options; }
+
+
+namespace{
+const Variant EMPTY_VARIANT;
+const std::string EMPTY_STRING;
+const std::string NODE_PROPERTIES="node";
+}
+
+const Variant& find(const Variant::Map& map, const std::string& key)
+{
+ Variant::Map::const_iterator i = map.find(key);
+ if (i == map.end()) return EMPTY_VARIANT;
+ else return i->second;
+}
+
+std::string Address::getType() const
+{
+ const Variant& props = find(impl->options, NODE_PROPERTIES);
+ if (props.getType() == VAR_MAP) {
+ const Variant& type = find(props.asMap(), TYPE);
+ if (!type.isVoid()) return type.asString();
+ }
+ return EMPTY_STRING;
+}
+
+void Address::setType(const std::string& type)
+{
+ Variant& props = impl->options[NODE_PROPERTIES];
+ if (props.isVoid()) props = Variant::Map();
+ props.asMap()[TYPE] = type;
+}
+
+std::ostream& operator<<(std::ostream& out, const Address& address)
+{
+ out << address.str();
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/AddressParser.cpp b/qpid/cpp/src/qpid/messaging/AddressParser.cpp
new file mode 100644
index 0000000000..4c8f35fbc5
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/AddressParser.cpp
@@ -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.
+ *
+ */
+#include "AddressParser.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace messaging {
+
+using namespace qpid::types;
+
+AddressParser::AddressParser(const std::string& s) : input(s), current(0) {}
+
+bool AddressParser::error(const std::string& message)
+{
+ throw MalformedAddress((boost::format("%1%, character %2% of %3%") % message % current % input).str());
+}
+
+bool AddressParser::parse(Address& address)
+{
+ std::string name;
+ if (readName(name)) {
+ if (name.find('#') == 0) name = qpid::framing::Uuid(true).str() + name;
+ address.setName(name);
+ if (readChar('/')) {
+ std::string subject;
+ readSubject(subject);
+ address.setSubject(subject);
+ }
+ if (readChar(';')) {
+ Variant options = Variant::Map();
+ if (readMap(options)) {
+ address.setOptions(options.asMap());
+ }
+ }
+ //skip trailing whitespace
+ while (!eos() && iswhitespace()) ++current;
+ return eos() || error("Unexpected chars in address: " + input.substr(current));
+ } else {
+ return input.empty() || error("Expected name");
+ }
+}
+
+bool AddressParser::parseMap(Variant::Map& map)
+{
+ if (readChar('{')) {
+ readMapEntries(map);
+ return readChar('}') || error("Unmatched '{'!");
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::parseList(Variant::List& list)
+{
+ if (readChar('[')) {
+ readListItems(list);
+ return readChar(']') || error("Unmatched '['!");
+ } else {
+ return false;
+ }
+}
+
+
+bool AddressParser::readList(Variant& value)
+{
+ if (readChar('[')) {
+ value = Variant::List();
+ readListItems(value.asList());
+ return readChar(']') || error("Unmatched '['!");
+ } else {
+ return false;
+ }
+}
+
+void AddressParser::readListItems(Variant::List& list)
+{
+ Variant item;
+ while (readValueIfExists(item)) {
+ list.push_back(item);
+ if (!readChar(',')) break;
+ }
+}
+
+bool AddressParser::readMap(Variant& value)
+{
+ if (readChar('{')) {
+ value = Variant::Map();
+ readMapEntries(value.asMap());
+ return readChar('}') || error("Unmatched '{'!");
+ } else {
+ return false;
+ }
+}
+
+void AddressParser::readMapEntries(Variant::Map& map)
+{
+ while (readKeyValuePair(map) && readChar(',')) {}
+}
+
+bool AddressParser::readKeyValuePair(Variant::Map& map)
+{
+ std::string key;
+ Variant value;
+ if (readKey(key)) {
+ if (readChar(':') && readValue(value)) {
+ map[key] = value;
+ return true;
+ } else {
+ return error("Bad key-value pair, expected ':'");
+ }
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readKey(std::string& key)
+{
+ return readWord(key) || readQuotedString(key);
+}
+
+bool AddressParser::readValue(Variant& value)
+{
+ return readValueIfExists(value) || error("Expected value");
+}
+
+bool AddressParser::readValueIfExists(Variant& value)
+{
+ return readSimpleValue(value) || readQuotedValue(value) ||
+ readMap(value) || readList(value);
+}
+
+bool AddressParser::readString(std::string& value, char delimiter)
+{
+ if (readChar(delimiter)) {
+ std::string::size_type start = current++;
+ while (!eos()) {
+ if (input.at(current) == delimiter) {
+ if (current > start) {
+ value = input.substr(start, current - start);
+ } else {
+ value = "";
+ }
+ ++current;
+ return true;
+ } else {
+ ++current;
+ }
+ }
+ return error("Unmatched delimiter");
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readName(std::string& name)
+{
+ return readQuotedString(name) || readWord(name, "/;");
+}
+
+bool AddressParser::readSubject(std::string& subject)
+{
+ return readQuotedString(subject) || readWord(subject, ";");
+}
+
+bool AddressParser::readQuotedString(std::string& s)
+{
+ return readString(s, '"') || readString(s, '\'');
+}
+
+bool AddressParser::readQuotedValue(Variant& value)
+{
+ std::string s;
+ if (readQuotedString(s)) {
+ value = s;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readSimpleValue(Variant& value)
+{
+ std::string s;
+ if (readWord(s)) {
+ value.parse(s);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readWord(std::string& value, const std::string& delims)
+{
+ //skip leading whitespace
+ while (!eos() && iswhitespace()) ++current;
+
+ //read any number of non-whitespace, non-reserved chars into value
+ std::string::size_type start = current;
+ while (!eos() && !iswhitespace() && !in(delims)) ++current;
+
+ if (current > start) {
+ value = input.substr(start, current - start);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readChar(char c)
+{
+ while (!eos()) {
+ if (iswhitespace()) {
+ ++current;
+ } else if (input.at(current) == c) {
+ ++current;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+bool AddressParser::iswhitespace()
+{
+ return ::isspace(input.at(current));
+}
+
+bool AddressParser::isreserved()
+{
+ return in(RESERVED);
+}
+
+bool AddressParser::in(const std::string& chars)
+{
+ return chars.find(input.at(current)) != std::string::npos;
+}
+
+bool AddressParser::eos()
+{
+ return current >= input.size();
+}
+
+const std::string AddressParser::RESERVED = "\'\"{}[],:/";
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/AddressParser.h b/qpid/cpp/src/qpid/messaging/AddressParser.h
new file mode 100644
index 0000000000..1635331d19
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/AddressParser.h
@@ -0,0 +1,66 @@
+#ifndef QPID_MESSAGING_ADDRESSPARSER_H
+#define QPID_MESSAGING_ADDRESSPARSER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Address.h"
+
+namespace qpid {
+namespace messaging {
+
+class AddressParser
+{
+ public:
+ AddressParser(const std::string&);
+ bool parse(Address& address);
+ bool parseMap(qpid::types::Variant::Map& map);
+ bool parseList(qpid::types::Variant::List& list);
+ private:
+ const std::string& input;
+ std::string::size_type current;
+ static const std::string RESERVED;
+
+ bool readChar(char c);
+ bool readQuotedString(std::string& s);
+ bool readQuotedValue(qpid::types::Variant& value);
+ bool readString(std::string& value, char delimiter);
+ bool readWord(std::string& word, const std::string& delims = RESERVED);
+ bool readSimpleValue(qpid::types::Variant& word);
+ bool readKey(std::string& key);
+ bool readValue(qpid::types::Variant& value);
+ bool readValueIfExists(qpid::types::Variant& value);
+ bool readKeyValuePair(qpid::types::Variant::Map& map);
+ bool readMap(qpid::types::Variant& value);
+ bool readList(qpid::types::Variant& value);
+ bool readName(std::string& name);
+ bool readSubject(std::string& subject);
+ bool error(const std::string& message);
+ bool eos();
+ bool iswhitespace();
+ bool in(const std::string& delims);
+ bool isreserved();
+ void readListItems(qpid::types::Variant::List& list);
+ void readMapEntries(qpid::types::Variant::Map& map);
+};
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_ADDRESSPARSER_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp
new file mode 100644
index 0000000000..bd90aa54a7
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Connection.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/messaging/PrivateImplRef.h"
+#include "qpid/client/amqp0_10/ConnectionImpl.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace messaging {
+
+using namespace qpid::types;
+
+typedef PrivateImplRef<qpid::messaging::Connection> PI;
+
+Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); }
+Connection::Connection(const Connection& c) : Handle<ConnectionImpl>() { PI::copy(*this, c); }
+Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); }
+Connection::~Connection() { PI::dtor(*this); }
+
+Connection::Connection(const std::string& url, const std::string& o)
+{
+ Variant::Map options;
+ AddressParser parser(o);
+ if (o.empty() || parser.parseMap(options)) {
+ PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ } else {
+ throw InvalidOptionString("Invalid option string: " + o);
+ }
+}
+Connection::Connection(const std::string& url, const Variant::Map& options)
+{
+ PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+}
+
+Connection::Connection()
+{
+ Variant::Map options;
+ std::string url = "amqp:tcp:127.0.0.1:5672";
+ PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options));
+}
+
+void Connection::open() { impl->open(); }
+bool Connection::isOpen() { return impl->isOpen(); }
+bool Connection::isOpen() const { return impl->isOpen(); }
+void Connection::close() { impl->close(); }
+Session Connection::createSession(const std::string& name) { return impl->newSession(false, name); }
+Session Connection::createTransactionalSession(const std::string& name)
+{
+ return impl->newSession(true, name);
+}
+Session Connection::getSession(const std::string& name) const { return impl->getSession(name); }
+void Connection::setOption(const std::string& name, const Variant& value)
+{
+ impl->setOption(name, value);
+}
+std::string Connection::getAuthenticatedUsername()
+{
+ return impl->getAuthenticatedUsername();
+}
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h
new file mode 100644
index 0000000000..1e11d9a6d5
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h
@@ -0,0 +1,52 @@
+#ifndef QPID_MESSAGING_CONNECTIONIMPL_H
+#define QPID_MESSAGING_CONNECTIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <string>
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+
+namespace types {
+class Variant;
+}
+
+namespace messaging {
+
+class Session;
+
+class ConnectionImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ConnectionImpl() {}
+ virtual void open() = 0;
+ virtual bool isOpen() const = 0;
+ virtual void close() = 0;
+ virtual Session newSession(bool transactional, const std::string& name) = 0;
+ virtual Session getSession(const std::string& name) const = 0;
+ virtual void setOption(const std::string& name, const qpid::types::Variant& value) = 0;
+ virtual std::string getAuthenticatedUsername() = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CONNECTIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Duration.cpp b/qpid/cpp/src/qpid/messaging/Duration.cpp
new file mode 100644
index 0000000000..a23e9f5bcb
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Duration.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Duration.h"
+#include <limits>
+
+namespace qpid {
+namespace messaging {
+
+Duration::Duration(uint64_t ms) : milliseconds(ms) {}
+uint64_t Duration::getMilliseconds() const { return milliseconds; }
+
+Duration operator*(const Duration& duration, uint64_t multiplier)
+{
+ return Duration(duration.getMilliseconds() * multiplier);
+}
+
+Duration operator*(uint64_t multiplier, const Duration& duration)
+{
+ return Duration(duration.getMilliseconds() * multiplier);
+}
+
+bool operator==(const Duration& a, const Duration& b)
+{
+ return a.getMilliseconds() == b.getMilliseconds();
+}
+
+bool operator!=(const Duration& a, const Duration& b)
+{
+ return a.getMilliseconds() != b.getMilliseconds();
+}
+
+const Duration Duration::FOREVER(std::numeric_limits<uint64_t>::max());
+const Duration Duration::IMMEDIATE(0);
+const Duration Duration::SECOND(1000);
+const Duration Duration::MINUTE(SECOND * 60);
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/FailoverUpdates.cpp b/qpid/cpp/src/qpid/messaging/FailoverUpdates.cpp
new file mode 100644
index 0000000000..4f2fcf2e82
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/FailoverUpdates.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/FailoverUpdates.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include "qpid/Url.h"
+#include "qpid/framing/Uuid.h"
+#include <vector>
+
+namespace qpid {
+namespace messaging {
+using framing::Uuid;
+
+struct FailoverUpdatesImpl : qpid::sys::Runnable
+{
+ Connection connection;
+ Session session;
+ Receiver receiver;
+ qpid::sys::Thread thread;
+
+ FailoverUpdatesImpl(Connection& c) : connection(c)
+ {
+ session = connection.createSession("failover-updates."+Uuid(true).str());
+ receiver = session.createReceiver("amq.failover");
+ thread = qpid::sys::Thread(*this);
+ }
+
+ ~FailoverUpdatesImpl() {
+ try {
+ session.close();
+ } catch(...) {} // Squash exceptions in a destructor.
+ thread.join();
+ }
+
+ void run()
+ {
+ try {
+ Message message;
+ while (receiver.fetch(message)) {
+ connection.setOption("reconnect-urls", message.getProperties()["amq.failover"]);
+ QPID_LOG(debug, "Set reconnect-urls to " << message.getProperties()["amq.failover"]);
+ session.acknowledge();
+ }
+ }
+ catch (const ClosedException&) {}
+ catch (const qpid::TransportFailure& e) {
+ QPID_LOG(warning, "Failover updates stopped on loss of connection. " << e.what());
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(warning, "Failover updates stopped due to exception: " << e.what());
+ }
+ }
+};
+
+FailoverUpdates::FailoverUpdates(Connection& connection) : impl(new FailoverUpdatesImpl(connection)) {}
+FailoverUpdates::~FailoverUpdates() { if (impl) { delete impl; } }
+FailoverUpdates::FailoverUpdates(const FailoverUpdates&) : impl(0) {}
+FailoverUpdates& FailoverUpdates::operator=(const FailoverUpdates&) { return *this; }
+
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp b/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp
new file mode 100644
index 0000000000..c9a7680bb4
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/HandleInstantiator.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace messaging {
+
+using namespace qpid::types;
+
+void HandleInstantiatorDoNotCall(void)
+{
+ // This function exists to instantiate various template Handle
+ // bool functions. The instances are then available to
+ // the qpidmessaging DLL and subsequently exported.
+ // This function must not be exported nor called called.
+ // For further information refer to
+ // https://issues.apache.org/jira/browse/QPID-2926
+
+ Connection connection;
+ if (connection.isValid()) connection.close();
+ if (connection.isNull() ) connection.close();
+ if (connection ) connection.close();
+ if (!connection ) connection.close();
+
+ Receiver receiver;
+ if (receiver.isValid()) receiver.close();
+ if (receiver.isNull() ) receiver.close();
+ if (receiver ) receiver.close();
+ if (!receiver ) receiver.close();
+
+ Sender sender;
+ if (sender.isValid()) sender.close();
+ if (sender.isNull() ) sender.close();
+ if (sender ) sender.close();
+ if (!sender ) sender.close();
+
+ Session session;
+ if (session.isValid()) session.close();
+ if (session.isNull() ) session.close();
+ if (session ) session.close();
+ if (!session ) session.close();
+}
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp
new file mode 100644
index 0000000000..83cdfd3c55
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Message.cpp
@@ -0,0 +1,148 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace messaging {
+
+using namespace qpid::types;
+
+Message::Message(const std::string& bytes) : impl(new MessageImpl(bytes)) {}
+Message::Message(const char* bytes, size_t count) : impl(new MessageImpl(bytes, count)) {}
+
+Message::Message(const Message& m) : impl(new MessageImpl(*m.impl)) {}
+Message::~Message() { delete impl; }
+
+Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; }
+
+void Message::setReplyTo(const Address& d) { impl->setReplyTo(d); }
+const Address& Message::getReplyTo() const { return impl->getReplyTo(); }
+
+void Message::setSubject(const std::string& s) { impl->setSubject(s); }
+const std::string& Message::getSubject() const { return impl->getSubject(); }
+
+void Message::setContentType(const std::string& s) { impl->setContentType(s); }
+const std::string& Message::getContentType() const { return impl->getContentType(); }
+
+void Message::setMessageId(const std::string& id) { impl->messageId = id; }
+const std::string& Message::getMessageId() const { return impl->messageId; }
+
+void Message::setUserId(const std::string& id) { impl->userId = id; }
+const std::string& Message::getUserId() const { return impl->userId; }
+
+void Message::setCorrelationId(const std::string& id) { impl->correlationId = id; }
+const std::string& Message::getCorrelationId() const { return impl->correlationId; }
+
+uint8_t Message::getPriority() const { return impl->priority; }
+void Message::setPriority(uint8_t priority) { impl->priority = priority; }
+
+void Message::setTtl(Duration ttl) { impl->ttl = ttl.getMilliseconds(); }
+Duration Message::getTtl() const { return Duration(impl->ttl); }
+
+void Message::setDurable(bool durable) { impl->durable = durable; }
+bool Message::getDurable() const { return impl->durable; }
+
+bool Message::getRedelivered() const { return impl->redelivered; }
+void Message::setRedelivered(bool redelivered) { impl->redelivered = redelivered; }
+
+const Variant::Map& Message::getProperties() const { return impl->getHeaders(); }
+Variant::Map& Message::getProperties() { return impl->getHeaders(); }
+void Message::setProperty(const std::string& k, const qpid::types::Variant& v) { impl->setHeader(k,v); }
+
+void Message::setContent(const std::string& c) { impl->setBytes(c); }
+void Message::setContent(const char* chars, size_t count) { impl->setBytes(chars, count); }
+std::string Message::getContent() const { return impl->getBytes(); }
+
+const char* Message::getContentPtr() const
+{
+ return impl->getBytes().data();
+}
+
+size_t Message::getContentSize() const
+{
+ return impl->getBytes().size();
+}
+
+EncodingException::EncodingException(const std::string& msg) : qpid::types::Exception(msg) {}
+
+const std::string BAD_ENCODING("Unsupported encoding: %1% (only %2% is supported at present).");
+
+template <class C> struct MessageCodec
+{
+ static bool checkEncoding(const std::string& requested)
+ {
+ if (requested.size()) {
+ if (requested == C::contentType) return true;
+ else throw EncodingException((boost::format(BAD_ENCODING) % requested % C::contentType).str());
+ } else {
+ return false;
+ }
+ }
+
+ /*
+ * Currently only support a single encoding type for both list and
+ * map, based on AMQP 0-10, though wider support is anticipated in the
+ * future. This method simply checks that the desired encoding (if one
+ * is specified, either through the message-content or through an
+ * override) is indeed supported.
+ */
+ static void checkEncoding(const Message& message, const std::string& requested)
+ {
+ checkEncoding(requested) || checkEncoding(message.getContentType());
+ }
+
+ static void decode(const Message& message, typename C::ObjectType& object, const std::string& encoding)
+ {
+ checkEncoding(message, encoding);
+ C::decode(message.getContent(), object);
+ }
+
+ static void encode(const typename C::ObjectType& map, Message& message, const std::string& encoding)
+ {
+ checkEncoding(message, encoding);
+ std::string content;
+ C::encode(map, content);
+ message.setContentType(C::contentType);
+ message.setContent(content);
+ }
+};
+
+void decode(const Message& message, Variant::Map& map, const std::string& encoding)
+{
+ MessageCodec<qpid::amqp_0_10::MapCodec>::decode(message, map, encoding);
+}
+void decode(const Message& message, Variant::List& list, const std::string& encoding)
+{
+ MessageCodec<qpid::amqp_0_10::ListCodec>::decode(message, list, encoding);
+}
+void encode(const Variant::Map& map, Message& message, const std::string& encoding)
+{
+ MessageCodec<qpid::amqp_0_10::MapCodec>::encode(map, message, encoding);
+}
+void encode(const Variant::List& list, Message& message, const std::string& encoding)
+{
+ MessageCodec<qpid::amqp_0_10::ListCodec>::encode(list, message, encoding);
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp
new file mode 100644
index 0000000000..0601800e46
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp
@@ -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.
+ *
+ */
+#include "MessageImpl.h"
+#include "qpid/messaging/Message.h"
+
+namespace qpid {
+namespace messaging {
+
+namespace {
+const std::string EMPTY_STRING = "";
+}
+
+using namespace qpid::types;
+
+MessageImpl::MessageImpl(const std::string& c) :
+ priority(0),
+ ttl(0),
+ durable(false),
+ redelivered(false),
+ bytes(c),
+ internalId(0) {}
+MessageImpl::MessageImpl(const char* chars, size_t count) :
+ priority(0),
+ ttl(0),
+ durable (false),
+ redelivered(false),
+ bytes(chars, count),
+ internalId(0) {}
+
+void MessageImpl::setReplyTo(const Address& d) { replyTo = d; }
+const Address& MessageImpl::getReplyTo() const { return replyTo; }
+
+void MessageImpl::setSubject(const std::string& s) { subject = s; }
+const std::string& MessageImpl::getSubject() const { return subject; }
+
+void MessageImpl::setContentType(const std::string& s) { contentType = s; }
+const std::string& MessageImpl::getContentType() const { return contentType; }
+
+const Variant::Map& MessageImpl::getHeaders() const { return headers; }
+Variant::Map& MessageImpl::getHeaders() { return headers; }
+void MessageImpl::setHeader(const std::string& key, const qpid::types::Variant& val) { headers[key] = val; }
+
+//should these methods be on MessageContent?
+void MessageImpl::setBytes(const std::string& c) { bytes = c; }
+void MessageImpl::setBytes(const char* chars, size_t count) { bytes.assign(chars, count); }
+const std::string& MessageImpl::getBytes() const { return bytes; }
+std::string& MessageImpl::getBytes() { return bytes; }
+
+void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; }
+qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; }
+
+MessageImpl& MessageImplAccess::get(Message& msg)
+{
+ return *msg.impl;
+}
+const MessageImpl& MessageImplAccess::get(const Message& msg)
+{
+ return *msg.impl;
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/MessageImpl.h b/qpid/cpp/src/qpid/messaging/MessageImpl.h
new file mode 100644
index 0000000000..57df6b3fda
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/MessageImpl.h
@@ -0,0 +1,90 @@
+#ifndef QPID_MESSAGING_MESSAGEIMPL_H
+#define QPID_MESSAGING_MESSAGEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/messaging/Address.h"
+#include "qpid/types/Variant.h"
+#include "qpid/framing/SequenceNumber.h"
+
+namespace qpid {
+namespace messaging {
+
+struct MessageImpl
+{
+ Address replyTo;
+ std::string subject;
+ std::string contentType;
+ std::string messageId;
+ std::string userId;
+ std::string correlationId;
+ uint8_t priority;
+ uint64_t ttl;
+ bool durable;
+ bool redelivered;
+ qpid::types::Variant::Map headers;
+
+ std::string bytes;
+
+ qpid::framing::SequenceNumber internalId;
+
+ MessageImpl(const std::string& c);
+ MessageImpl(const char* chars, size_t count);
+
+ void setReplyTo(const Address& d);
+ const Address& getReplyTo() const;
+
+ void setSubject(const std::string& s);
+ const std::string& getSubject() const;
+
+ void setContentType(const std::string& s);
+ const std::string& getContentType() const;
+
+ const qpid::types::Variant::Map& getHeaders() const;
+ qpid::types::Variant::Map& getHeaders();
+ void setHeader(const std::string& key, const qpid::types::Variant& val);
+
+ void setBytes(const std::string& bytes);
+ void setBytes(const char* chars, size_t count);
+ const std::string& getBytes() const;
+ std::string& getBytes();
+
+ void setInternalId(qpid::framing::SequenceNumber id);
+ qpid::framing::SequenceNumber getInternalId();
+
+};
+
+class Message;
+
+/**
+ * Provides access to the internal MessageImpl for a message which is
+ * useful when accessing any message state not exposed directly
+ * through the public API.
+ */
+struct MessageImplAccess
+{
+ static MessageImpl& get(Message&);
+ static const MessageImpl& get(const Message&);
+};
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_MESSAGEIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/PrivateImplRef.h b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h
new file mode 100644
index 0000000000..e77c58d071
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h
@@ -0,0 +1,94 @@
+#ifndef QPID_MESSAGING_PRIVATEIMPL_H
+#define QPID_MESSAGING_PRIVATEIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/messaging/ImportExport.h"
+#include <boost/intrusive_ptr.hpp>
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace messaging {
+
+/**
+ * Helper class to implement a class with a private, reference counted
+ * implementation and reference semantics.
+ *
+ * Such classes are used in the public API to hide implementation, they
+ * should. Example of use:
+ *
+ * === Foo.h
+ *
+ * template <class T> PrivateImplRef;
+ * class FooImpl;
+ *
+ * Foo : public Handle<FooImpl> {
+ * public:
+ * Foo(FooImpl* = 0);
+ * Foo(const Foo&);
+ * ~Foo();
+ * Foo& operator=(const Foo&);
+ *
+ * int fooDo(); // and other Foo functions...
+ *
+ * private:
+ * typedef FooImpl Impl;
+ * Impl* impl;
+ * friend class PrivateImplRef<Foo>;
+ *
+ * === Foo.cpp
+ *
+ * typedef PrivateImplRef<Foo> PI;
+ * Foo::Foo(FooImpl* p) { PI::ctor(*this, p); }
+ * Foo::Foo(const Foo& c) : Handle<FooImpl>() { PI::copy(*this, c); }
+ * Foo::~Foo() { PI::dtor(*this); }
+ * Foo& Foo::operator=(const Foo& c) { return PI::assign(*this, c); }
+ *
+ * int foo::fooDo() { return impl->fooDo(); }
+ *
+ */
+template <class T> class PrivateImplRef {
+ public:
+ typedef typename T::Impl Impl;
+ typedef boost::intrusive_ptr<Impl> intrusive_ptr;
+
+ /** Get the implementation pointer from a handle */
+ static intrusive_ptr get(const T& t) { return intrusive_ptr(t.impl); }
+
+ /** Set the implementation pointer in a handle */
+ static void set(T& t, const intrusive_ptr& p) {
+ if (t.impl == p) return;
+ if (t.impl) boost::intrusive_ptr_release(t.impl);
+ t.impl = p.get();
+ if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ }
+
+ // Helper functions to implement the ctor, dtor, copy, assign
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
+ static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
+};
+
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_PRIVATEIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp
new file mode 100644
index 0000000000..78e0c5daa3
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/ReceiverImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/PrivateImplRef.h"
+
+namespace qpid {
+namespace messaging {
+
+typedef PrivateImplRef<qpid::messaging::Receiver> PI;
+
+Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); }
+Receiver::Receiver(const Receiver& s) : Handle<ReceiverImpl>() { PI::copy(*this, s); }
+Receiver::~Receiver() { PI::dtor(*this); }
+Receiver& Receiver::operator=(const Receiver& s) { return PI::assign(*this, s); }
+bool Receiver::get(Message& message, Duration timeout) { return impl->get(message, timeout); }
+Message Receiver::get(Duration timeout) { return impl->get(timeout); }
+bool Receiver::fetch(Message& message, Duration timeout) { return impl->fetch(message, timeout); }
+Message Receiver::fetch(Duration timeout) { return impl->fetch(timeout); }
+void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); }
+uint32_t Receiver::getCapacity() { return impl->getCapacity(); }
+uint32_t Receiver::getAvailable() { return impl->getAvailable(); }
+uint32_t Receiver::getUnsettled() { return impl->getUnsettled(); }
+void Receiver::close() { impl->close(); }
+const std::string& Receiver::getName() const { return impl->getName(); }
+Session Receiver::getSession() const { return impl->getSession(); }
+bool Receiver::isClosed() const { return impl->isClosed(); }
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h
new file mode 100644
index 0000000000..57059bfd28
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h
@@ -0,0 +1,52 @@
+#ifndef QPID_MESSAGING_RECEIVERIMPL_H
+#define QPID_MESSAGING_RECEIVERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace messaging {
+
+class Message;
+class MessageListener;
+class Session;
+
+class ReceiverImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ReceiverImpl() {}
+ virtual bool get(Message& message, Duration timeout) = 0;
+ virtual Message get(Duration timeout) = 0;
+ virtual bool fetch(Message& message, Duration timeout) = 0;
+ virtual Message fetch(Duration timeout) = 0;
+ virtual void setCapacity(uint32_t) = 0;
+ virtual uint32_t getCapacity() = 0;
+ virtual uint32_t getAvailable() = 0;
+ virtual uint32_t getUnsettled() = 0;
+ virtual void close() = 0;
+ virtual const std::string& getName() const = 0;
+ virtual Session getSession() const = 0;
+ virtual bool isClosed() const = 0;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_RECEIVERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp
new file mode 100644
index 0000000000..53dbb69777
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Sender.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/SenderImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/PrivateImplRef.h"
+
+namespace qpid {
+namespace messaging {
+typedef PrivateImplRef<qpid::messaging::Sender> PI;
+
+Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); }
+Sender::Sender(const Sender& s) : qpid::messaging::Handle<SenderImpl>() { PI::copy(*this, s); }
+Sender::~Sender() { PI::dtor(*this); }
+Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); }
+void Sender::send(const Message& message, bool sync) { impl->send(message, sync); }
+void Sender::close() { impl->close(); }
+void Sender::setCapacity(uint32_t c) { impl->setCapacity(c); }
+uint32_t Sender::getCapacity() { return impl->getCapacity(); }
+uint32_t Sender::getUnsettled() { return impl->getUnsettled(); }
+uint32_t Sender::getAvailable() { return getCapacity() - getUnsettled(); }
+const std::string& Sender::getName() const { return impl->getName(); }
+Session Sender::getSession() const { return impl->getSession(); }
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h
new file mode 100644
index 0000000000..a1ca02c72c
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h
@@ -0,0 +1,47 @@
+#ifndef QPID_MESSAGING_SENDERIMPL_H
+#define QPID_MESSAGING_SENDERIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+
+namespace qpid {
+namespace messaging {
+
+class Message;
+class Session;
+
+class SenderImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~SenderImpl() {}
+ virtual void send(const Message& message, bool sync) = 0;
+ virtual void close() = 0;
+ virtual void setCapacity(uint32_t) = 0;
+ virtual uint32_t getCapacity() = 0;
+ virtual uint32_t getUnsettled() = 0;
+ virtual const std::string& getName() const = 0;
+ virtual Session getSession() const = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SENDERIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp
new file mode 100644
index 0000000000..496953a8e5
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/Session.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/messaging/PrivateImplRef.h"
+
+namespace qpid {
+namespace messaging {
+
+typedef PrivateImplRef<qpid::messaging::Session> PI;
+
+Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); }
+Session::Session(const Session& s) : Handle<SessionImpl>() { PI::copy(*this, s); }
+Session::~Session() { PI::dtor(*this); }
+Session& Session::operator=(const Session& s) { return PI::assign(*this, s); }
+void Session::commit() { impl->commit(); }
+void Session::rollback() { impl->rollback(); }
+void Session::acknowledge(bool sync) { impl->acknowledge(sync); }
+void Session::acknowledge(Message& m, bool s) { impl->acknowledge(m); sync(s); }
+void Session::reject(Message& m) { impl->reject(m); }
+void Session::release(Message& m) { impl->release(m); }
+void Session::close() { impl->close(); }
+
+Sender Session::createSender(const Address& address)
+{
+ return impl->createSender(address);
+}
+Receiver Session::createReceiver(const Address& address)
+{
+ return impl->createReceiver(address);
+}
+
+Sender Session::createSender(const std::string& address)
+{
+ return impl->createSender(Address(address));
+}
+Receiver Session::createReceiver(const std::string& address)
+{
+ return impl->createReceiver(Address(address));
+}
+
+void Session::sync(bool block)
+{
+ impl->sync(block);
+}
+
+bool Session::nextReceiver(Receiver& receiver, Duration timeout)
+{
+ return impl->nextReceiver(receiver, timeout);
+}
+
+
+Receiver Session::nextReceiver(Duration timeout)
+{
+ return impl->nextReceiver(timeout);
+}
+
+uint32_t Session::getReceivable() { return impl->getReceivable(); }
+uint32_t Session::getUnsettledAcks() { return impl->getUnsettledAcks(); }
+
+Sender Session::getSender(const std::string& name) const
+{
+ return impl->getSender(name);
+}
+Receiver Session::getReceiver(const std::string& name) const
+{
+ return impl->getReceiver(name);
+}
+
+Connection Session::getConnection() const
+{
+ return impl->getConnection();
+}
+
+void Session::checkError() { impl->checkError(); }
+bool Session::hasError()
+{
+ try {
+ checkError();
+ return false;
+ } catch (const std::exception&) {
+ return true;
+ }
+}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h
new file mode 100644
index 0000000000..02a254e4f2
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h
@@ -0,0 +1,63 @@
+#ifndef QPID_MESSAGING_SESSIONIMPL_H
+#define QPID_MESSAGING_SESSIONIMPL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/RefCounted.h"
+#include <string>
+#include "qpid/messaging/Duration.h"
+
+namespace qpid {
+namespace messaging {
+
+class Address;
+class Connection;
+class Message;
+class Sender;
+class Receiver;
+
+class SessionImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~SessionImpl() {}
+ virtual void commit() = 0;
+ virtual void rollback() = 0;
+ virtual void acknowledge(bool sync) = 0;
+ virtual void acknowledge(Message&) = 0;
+ virtual void reject(Message&) = 0;
+ virtual void release(Message&) = 0;
+ virtual void close() = 0;
+ virtual void sync(bool block) = 0;
+ virtual Sender createSender(const Address& address) = 0;
+ virtual Receiver createReceiver(const Address& address) = 0;
+ virtual bool nextReceiver(Receiver& receiver, Duration timeout) = 0;
+ virtual Receiver nextReceiver(Duration timeout) = 0;
+ virtual uint32_t getReceivable() = 0;
+ virtual uint32_t getUnsettledAcks() = 0;
+ virtual Sender getSender(const std::string& name) const = 0;
+ virtual Receiver getReceiver(const std::string& name) const = 0;
+ virtual Connection getConnection() const = 0;
+ virtual void checkError() = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SESSIONIMPL_H*/
diff --git a/qpid/cpp/src/qpid/messaging/exceptions.cpp b/qpid/cpp/src/qpid/messaging/exceptions.cpp
new file mode 100644
index 0000000000..5d2683fffe
--- /dev/null
+++ b/qpid/cpp/src/qpid/messaging/exceptions.cpp
@@ -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.
+ *
+ */
+#include "qpid/messaging/exceptions.h"
+
+namespace qpid {
+namespace messaging {
+
+MessagingException::MessagingException(const std::string& msg) : qpid::types::Exception(msg) {}
+MessagingException::~MessagingException() throw() {}
+
+InvalidOptionString::InvalidOptionString(const std::string& msg) : MessagingException(msg) {}
+KeyError::KeyError(const std::string& msg) : MessagingException(msg) {}
+
+
+LinkError::LinkError(const std::string& msg) : MessagingException(msg) {}
+
+AddressError::AddressError(const std::string& msg) : LinkError(msg) {}
+ResolutionError::ResolutionError(const std::string& msg) : AddressError(msg) {}
+MalformedAddress::MalformedAddress(const std::string& msg) : AddressError(msg) {}
+AssertionFailed::AssertionFailed(const std::string& msg) : ResolutionError(msg) {}
+NotFound::NotFound(const std::string& msg) : ResolutionError(msg) {}
+
+ReceiverError::ReceiverError(const std::string& msg) : LinkError(msg) {}
+FetchError::FetchError(const std::string& msg) : ReceiverError(msg) {}
+NoMessageAvailable::NoMessageAvailable() : FetchError("No message to fetch") {}
+
+SenderError::SenderError(const std::string& msg) : LinkError(msg) {}
+SendError::SendError(const std::string& msg) : SenderError(msg) {}
+TargetCapacityExceeded::TargetCapacityExceeded(const std::string& msg) : SendError(msg) {}
+
+SessionError::SessionError(const std::string& msg) : MessagingException(msg) {}
+TransactionError::TransactionError(const std::string& msg) : SessionError(msg) {}
+TransactionAborted::TransactionAborted(const std::string& msg) : TransactionError(msg) {}
+UnauthorizedAccess::UnauthorizedAccess(const std::string& msg) : SessionError(msg) {}
+
+ConnectionError::ConnectionError(const std::string& msg) : MessagingException(msg) {}
+
+TransportFailure::TransportFailure(const std::string& msg) : MessagingException(msg) {}
+
+}} // namespace qpid::messaging
diff --git a/qpid/cpp/src/qpid/pointer_to_other.h b/qpid/cpp/src/qpid/pointer_to_other.h
new file mode 100644
index 0000000000..a99dc89658
--- /dev/null
+++ b/qpid/cpp/src/qpid/pointer_to_other.h
@@ -0,0 +1,62 @@
+#ifndef QPID_POINTERTOOTHER_H
+#define QPID_POINTERTOOTHER_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qpid {
+
+// Defines the same pointer type (raw or smart) to another pointee type
+
+template<class T, class U>
+struct pointer_to_other;
+
+template<class T, class U,
+ template<class> class Sp>
+struct pointer_to_other< Sp<T>, U >
+ {
+ typedef Sp<U> type;
+ };
+
+template<class T, class T2, class U,
+ template<class, class> class Sp>
+struct pointer_to_other< Sp<T, T2>, U >
+ {
+ typedef Sp<U, T2> type;
+ };
+
+template<class T, class T2, class T3, class U,
+ template<class, class, class> class Sp>
+struct pointer_to_other< Sp<T, T2, T3>, U >
+ {
+ typedef Sp<U, T2, T3> type;
+ };
+
+template<class T, class U>
+struct pointer_to_other< T*, U >
+{
+ typedef U* type;
+};
+
+} // namespace qpid
+
+
+
+#endif /*!QPID_POINTERTOOTHER_H*/
diff --git a/qpid/cpp/src/qpid/ptr_map.h b/qpid/cpp/src/qpid/ptr_map.h
new file mode 100644
index 0000000000..6ffcd48e89
--- /dev/null
+++ b/qpid/cpp/src/qpid/ptr_map.h
@@ -0,0 +1,57 @@
+#ifndef QPID_PTR_MAP
+#define QPID_PTR_MAP
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/ptr_container/ptr_map.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <boost/type_traits/remove_const.hpp>
+
+namespace qpid {
+
+/** @file
+ * Workaround for API change between boost 1.33 and 1.34.
+ *
+ * To be portable across these versions, code using boost::ptr_map
+ * iterators should use ptr_map_ptr(i) to get the pointer from
+ * boost::ptr_map::iterator i.
+ *
+ * @see http://www.boost.org/libs/ptr_container/doc/ptr_container.html#upgrading-from-boost-v-1-33
+ */
+
+
+typedef boost::is_same<boost::ptr_map<int, int>::iterator::value_type, int> IsOldPtrMap;
+
+template <class Iter>
+typename boost::enable_if<IsOldPtrMap, typename Iter::value_type*>::type
+ptr_map_ptr(const Iter& i) { return &*i; }
+
+template <class Iter>
+typename boost::disable_if<IsOldPtrMap,
+ typename boost::remove_const<typename Iter::value_type::second_type>::type
+ >::type
+ptr_map_ptr(const Iter& i) { return i->second; }
+
+} // namespace qpid
+
+#endif /*!QPID_PTR_MAP*/
diff --git a/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp
new file mode 100644
index 0000000000..b7d52372f4
--- /dev/null
+++ b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp
@@ -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.
+ *
+ */
+#include "qpid/replication/ReplicatingEventListener.h"
+#include "qpid/replication/constants.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace replication {
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::replication::constants;
+
+void ReplicatingEventListener::handle(QueueEvents::Event event)
+{
+ switch (event.type) {
+ case QueueEvents::ENQUEUE:
+ deliverEnqueueMessage(event.msg);
+ QPID_LOG(debug, "Queuing 'enqueue' event on " << event.msg.queue->getName() << " for replication");
+ break;
+ case QueueEvents::DEQUEUE:
+ deliverDequeueMessage(event.msg);
+ QPID_LOG(debug, "Queuing 'dequeue' event from " << event.msg.queue->getName() << " for replication, (from position "
+ << event.msg.position << ")");
+ break;
+ }
+}
+
+namespace {
+const std::string EMPTY;
+}
+
+void ReplicatingEventListener::deliverDequeueMessage(const QueuedMessage& dequeued)
+{
+ FieldTable headers;
+ headers.setString(REPLICATION_TARGET_QUEUE, dequeued.queue->getName());
+ headers.setInt(REPLICATION_EVENT_TYPE, DEQUEUE);
+ headers.setInt(DEQUEUED_MESSAGE_POSITION, dequeued.position);
+ boost::intrusive_ptr<Message> msg(createMessage(headers));
+ DeliveryProperties* props = msg->getFrames().getHeaders()->get<DeliveryProperties>(true);
+ props->setRoutingKey(dequeued.queue->getName());
+ route(msg);
+}
+
+void ReplicatingEventListener::deliverEnqueueMessage(const QueuedMessage& enqueued)
+{
+ boost::intrusive_ptr<Message> msg(cloneMessage(*(enqueued.queue), enqueued.payload));
+ FieldTable& headers = msg->getProperties<MessageProperties>()->getApplicationHeaders();
+ headers.setString(REPLICATION_TARGET_QUEUE, enqueued.queue->getName());
+ headers.setInt(REPLICATION_EVENT_TYPE, ENQUEUE);
+ headers.setInt(QUEUE_MESSAGE_POSITION,enqueued.position);
+ route(msg);
+}
+
+void ReplicatingEventListener::route(boost::intrusive_ptr<qpid::broker::Message> msg)
+{
+ try {
+ if (exchange) {
+ DeliverableMessage deliverable(msg);
+ exchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders());
+ } else if (queue) {
+ queue->deliver(msg);
+ } else {
+ QPID_LOG(error, "Cannot route replication event, neither replication queue nor exchange configured");
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error enqueing replication event: " << e.what());
+ }
+}
+
+
+boost::intrusive_ptr<Message> ReplicatingEventListener::createMessage(const FieldTable& headers)
+{
+ boost::intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), EMPTY, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ header.setBof(false);
+ header.setEof(true);
+ header.setBos(true);
+ header.setEos(true);
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setApplicationHeaders(headers);
+ return msg;
+}
+
+struct AppendingHandler : FrameHandler
+{
+ boost::intrusive_ptr<Message> msg;
+
+ AppendingHandler(boost::intrusive_ptr<Message> m) : msg(m) {}
+
+ void handle(AMQFrame& f)
+ {
+ msg->getFrames().append(f);
+ }
+};
+
+boost::intrusive_ptr<Message> ReplicatingEventListener::cloneMessage(Queue& queue, boost::intrusive_ptr<Message> original)
+{
+ boost::intrusive_ptr<Message> copy(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), EMPTY, 0, 0)));
+ AppendingHandler handler(copy);
+ handler.handle(method);
+
+ //To avoid modifying original headers, create new frame with
+ //cloned body:
+ AMQFrame header(*original->getFrames().getHeaders());
+ header.setBof(false);
+ header.setEof(!original->getFrames().getContentSize());//if there is any content then the header is not the end of the frameset
+ header.setBos(true);
+ header.setEos(true);
+ handler.handle(header);
+
+ original->sendContent(queue, handler, std::numeric_limits<int16_t>::max());
+ return copy;
+}
+
+Options* ReplicatingEventListener::getOptions()
+{
+ return &options;
+}
+
+void ReplicatingEventListener::initialize(Plugin::Target& target)
+{
+ Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ if (broker) {
+ broker->addFinalizer(boost::bind(&ReplicatingEventListener::shutdown, this));
+ if (!options.exchange.empty()) {
+ if (!options.queue.empty()) {
+ QPID_LOG(warning, "Replication queue option ignored as replication exchange has been specified");
+ }
+ try {
+ exchange = broker->getExchanges().declare(options.exchange, options.exchangeType).first;
+ } catch (const UnknownExchangeTypeException&) {
+ QPID_LOG(error, "Replication disabled due to invalid type: " << options.exchangeType);
+ }
+ } else if (!options.queue.empty()) {
+ if (options.createQueue) {
+ queue = broker->getQueues().declare(options.queue).first;
+ } else {
+ queue = broker->getQueues().find(options.queue);
+ }
+ if (queue) {
+ queue->insertSequenceNumbers(REPLICATION_EVENT_SEQNO);
+ } else {
+ QPID_LOG(error, "Replication queue named '" << options.queue << "' does not exist; replication plugin disabled.");
+ }
+ }
+ if (queue || exchange) {
+ QueueEvents::EventListener callback = boost::bind(&ReplicatingEventListener::handle, this, _1);
+ broker->getQueueEvents().registerListener(options.name, callback);
+ QPID_LOG(info, "Registered replicating queue event listener");
+ }
+ }
+}
+
+void ReplicatingEventListener::earlyInitialize(Target&) {}
+void ReplicatingEventListener::shutdown() { queue.reset(); exchange.reset(); }
+
+ReplicatingEventListener::PluginOptions::PluginOptions() : Options("Queue Replication Options"),
+ exchangeType("direct"),
+ name("replicator"),
+ createQueue(false)
+{
+ addOptions()
+ ("replication-exchange-name", optValue(exchange, "EXCHANGE"), "Exchange to which events for other queues are routed")
+ ("replication-exchange-type", optValue(exchangeType, "direct|topic etc"), "Type of exchange to use")
+ ("replication-queue", optValue(queue, "QUEUE"), "Queue on which events for other queues are recorded")
+ ("replication-listener-name", optValue(name, "NAME"), "name by which to register the replicating event listener")
+ ("create-replication-queue", optValue(createQueue), "if set, the replication will be created if it does not exist");
+}
+
+static ReplicatingEventListener plugin;
+
+}} // namespace qpid::replication
diff --git a/qpid/cpp/src/qpid/replication/ReplicatingEventListener.h b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.h
new file mode 100644
index 0000000000..74418d00e6
--- /dev/null
+++ b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.h
@@ -0,0 +1,78 @@
+#ifndef QPID_REPLICATION_REPLICATINGEVENTLISTENER_H
+#define QPID_REPLICATION_REPLICATINGEVENTLISTENER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/SequenceNumber.h"
+
+namespace qpid {
+namespace replication {
+
+/**
+ * An event listener plugin that records queue events as messages on a
+ * replication queue, from where they can be consumed (e.g. by an
+ * inter-broker link to the corresponding QueueReplicationExchange
+ * plugin.
+ */
+class ReplicatingEventListener : public Plugin
+{
+ public:
+ Options* getOptions();
+ void earlyInitialize(Plugin::Target& target);
+ void initialize(Plugin::Target& target);
+ void handle(qpid::broker::QueueEvents::Event);
+ private:
+ struct PluginOptions : public Options
+ {
+ std::string queue;
+ std::string exchange;
+ std::string exchangeType;
+ std::string name;
+ bool createQueue;
+
+ PluginOptions();
+ };
+
+ PluginOptions options;
+ qpid::broker::Queue::shared_ptr queue;
+ qpid::broker::Exchange::shared_ptr exchange;
+
+ void deliverDequeueMessage(const qpid::broker::QueuedMessage& enqueued);
+ void deliverEnqueueMessage(const qpid::broker::QueuedMessage& enqueued);
+ void route(boost::intrusive_ptr<qpid::broker::Message>);
+ void shutdown();
+
+ boost::intrusive_ptr<qpid::broker::Message> createMessage(const qpid::framing::FieldTable& headers);
+ boost::intrusive_ptr<qpid::broker::Message> cloneMessage(qpid::broker::Queue& queue,
+ boost::intrusive_ptr<qpid::broker::Message> original);
+};
+
+}} // namespace qpid::replication
+
+#endif /*!QPID_REPLICATION_REPLICATINGEVENTLISTENER_H*/
diff --git a/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp b/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp
new file mode 100644
index 0000000000..4b6d25ac7d
--- /dev/null
+++ b/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp
@@ -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.
+ *
+ */
+#include "qpid/replication/ReplicationExchange.h"
+#include "qpid/replication/constants.h"
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace replication {
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::replication::constants;
+
+const std::string SEQUENCE_VALUE("qpid.replication-event.sequence");
+ReplicationExchange::ReplicationExchange(const std::string& name, bool durable,
+ const FieldTable& _args,
+ QueueRegistry& qr,
+ Manageable* parent, Broker* broker)
+ : Exchange(name, durable, _args, parent, broker), queues(qr), sequence(args.getAsInt64(SEQUENCE_VALUE)), init(false)
+{
+ args.setInt64(SEQUENCE_VALUE, sequence);
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type(typeName);
+}
+
+std::string ReplicationExchange::getType() const { return typeName; }
+
+void ReplicationExchange::route(Deliverable& msg, const std::string& /*routingKey*/, const FieldTable* args)
+{
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgReceives();
+ mgmtExchange->inc_byteReceives(msg.contentSize());
+ }
+ if (args) {
+ int eventType = args->getAsInt(REPLICATION_EVENT_TYPE);
+ if (eventType) {
+ if (isDuplicate(args)) return;
+ switch (eventType) {
+ case ENQUEUE:
+ handleEnqueueEvent(args, msg);
+ return;
+ case DEQUEUE:
+ handleDequeueEvent(args, msg);
+ return;
+ default:
+ throw IllegalArgumentException(QPID_MSG("Illegal value for " << REPLICATION_EVENT_TYPE << ": " << eventType));
+ }
+ }
+ } else {
+ QPID_LOG(warning, "Dropping unexpected message with no headers");
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ }
+}
+
+void ReplicationExchange::handleEnqueueEvent(const FieldTable* args, Deliverable& msg)
+{
+ std::string queueName = args->getAsString(REPLICATION_TARGET_QUEUE);
+ Queue::shared_ptr queue = queues.find(queueName);
+ if (queue) {
+
+ SequenceNumber seqno1(args->getAsInt(QUEUE_MESSAGE_POSITION));
+
+ // note that queue will ++ before enqueue.
+ if (queue->getPosition() > --seqno1) // test queue.pos < seqnumber
+ {
+ QPID_LOG(error, "Cannot enqueue replicated message. Destination Queue " << queueName << " ahead of source queue");
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ } else {
+ queue->setPosition(seqno1);
+
+ FieldTable& headers = msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders();
+ headers.erase(REPLICATION_TARGET_QUEUE);
+ headers.erase(REPLICATION_EVENT_SEQNO);
+ headers.erase(REPLICATION_EVENT_TYPE);
+ headers.erase(QUEUE_MESSAGE_POSITION);
+ msg.deliverTo(queue);
+ QPID_LOG(debug, "Enqueued replicated message onto " << queueName);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgRoutes();
+ mgmtExchange->inc_byteRoutes( msg.contentSize());
+ }
+ }
+ } else {
+ QPID_LOG(error, "Cannot enqueue replicated message. Queue " << queueName << " does not exist");
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ }
+}
+
+void ReplicationExchange::handleDequeueEvent(const FieldTable* args, Deliverable& msg)
+{
+ std::string queueName = args->getAsString(REPLICATION_TARGET_QUEUE);
+ Queue::shared_ptr queue = queues.find(queueName);
+ if (queue) {
+ SequenceNumber position(args->getAsInt(DEQUEUED_MESSAGE_POSITION));
+ QueuedMessage dequeued;
+ if (queue->acquireMessageAt(position, dequeued)) {
+ queue->dequeue(0, dequeued);
+ QPID_LOG(debug, "Processed replicated 'dequeue' event from " << queueName << " at position " << position);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgRoutes();
+ mgmtExchange->inc_byteRoutes(msg.contentSize());
+ }
+ } else {
+ QPID_LOG(warning, "Could not acquire message " << position << " from " << queueName);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ }
+ } else {
+ QPID_LOG(error, "Cannot process replicated 'dequeue' event. Queue " << queueName << " does not exist");
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ }
+}
+
+bool ReplicationExchange::isDuplicate(const FieldTable* args)
+{
+ if (!args->get(REPLICATION_EVENT_SEQNO)) return false;
+ SequenceNumber seqno(args->getAsInt(REPLICATION_EVENT_SEQNO));
+ if (!init) {
+ init = true;
+ sequence = seqno;
+ return false;
+ } else if (seqno > sequence) {
+ if (seqno - sequence > 1) {
+ QPID_LOG(error, "Gap in replication event sequence between: " << sequence << " and " << seqno);
+ }
+ sequence = seqno;
+ return false;
+ } else {
+ QPID_LOG(info, "Duplicate detected: seqno=" << seqno << " (last seqno=" << sequence << ")");
+ return true;
+ }
+}
+
+bool ReplicationExchange::bind(Queue::shared_ptr /*queue*/, const std::string& /*routingKey*/, const FieldTable* /*args*/)
+{
+ throw NotImplementedException("Replication exchange does not support bind operation");
+}
+
+bool ReplicationExchange::unbind(Queue::shared_ptr /*queue*/, const std::string& /*routingKey*/, const FieldTable* /*args*/)
+{
+ throw NotImplementedException("Replication exchange does not support unbind operation");
+}
+
+bool ReplicationExchange::isBound(Queue::shared_ptr /*queue*/, const string* const /*routingKey*/, const FieldTable* const /*args*/)
+{
+ return false;
+}
+
+const std::string ReplicationExchange::typeName("replication");
+
+
+void ReplicationExchange::encode(Buffer& buffer) const
+{
+ args.setInt64(std::string(SEQUENCE_VALUE), sequence);
+ Exchange::encode(buffer);
+}
+
+
+struct ReplicationExchangePlugin : Plugin
+{
+ Broker* broker;
+
+ ReplicationExchangePlugin();
+ void earlyInitialize(Plugin::Target& target);
+ void initialize(Plugin::Target& target);
+ Exchange::shared_ptr create(const std::string& name, bool durable,
+ const framing::FieldTable& args,
+ management::Manageable* parent,
+ qpid::broker::Broker* broker);
+};
+
+ReplicationExchangePlugin::ReplicationExchangePlugin() : broker(0) {}
+
+Exchange::shared_ptr ReplicationExchangePlugin::create(const std::string& name, bool durable,
+ const framing::FieldTable& args,
+ management::Manageable* parent, qpid::broker::Broker* broker)
+{
+ Exchange::shared_ptr e(new ReplicationExchange(name, durable, args, broker->getQueues(), parent, broker));
+ return e;
+}
+
+
+void ReplicationExchangePlugin::earlyInitialize(Plugin::Target& target)
+{
+ broker = dynamic_cast<broker::Broker*>(&target);
+ if (broker) {
+ ExchangeRegistry::FactoryFunction f = boost::bind(&ReplicationExchangePlugin::create, this, _1, _2, _3, _4, _5);
+ broker->getExchanges().registerType(ReplicationExchange::typeName, f);
+ QPID_LOG(info, "Registered replication exchange");
+ }
+}
+
+void ReplicationExchangePlugin::initialize(Target&) {}
+
+static ReplicationExchangePlugin exchangePlugin;
+
+}} // namespace qpid::replication
diff --git a/qpid/cpp/src/qpid/replication/ReplicationExchange.h b/qpid/cpp/src/qpid/replication/ReplicationExchange.h
new file mode 100644
index 0000000000..4b34e0df13
--- /dev/null
+++ b/qpid/cpp/src/qpid/replication/ReplicationExchange.h
@@ -0,0 +1,72 @@
+#ifndef QPID_REPLICATION_REPLICATIONEXCHANGE_H
+#define QPID_REPLICATION_REPLICATIONEXCHANGE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/SequenceNumber.h"
+
+namespace qpid {
+
+namespace broker {
+class QueueRegistry;
+}
+
+namespace replication {
+
+/**
+ * A custom exchange plugin that processes incoming messages
+ * representing enqueue or dequeue events for particular queues and
+ * carries out the corresponding action to replicate that on the local
+ * broker.
+ */
+class ReplicationExchange : public qpid::broker::Exchange
+{
+ public:
+ static const std::string typeName;
+
+ ReplicationExchange(const std::string& name, bool durable,
+ const qpid::framing::FieldTable& args,
+ qpid::broker::QueueRegistry& queues,
+ qpid::management::Manageable* parent = 0,
+ qpid::broker::Broker* broker = 0);
+
+ std::string getType() const;
+
+ void route(qpid::broker::Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ bool isBound(boost::shared_ptr<broker::Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args);
+ private:
+ qpid::broker::QueueRegistry& queues;
+ qpid::framing::SequenceNumber sequence;
+ bool init;
+
+ bool isDuplicate(const qpid::framing::FieldTable* args);
+ void handleEnqueueEvent(const qpid::framing::FieldTable* args, qpid::broker::Deliverable& msg);
+ void handleDequeueEvent(const qpid::framing::FieldTable* args, qpid::broker::Deliverable& msg);
+ void encode(framing::Buffer& buffer) const;
+};
+}} // namespace qpid::replication
+
+#endif /*!QPID_REPLICATION_REPLICATIONEXCHANGE_H*/
diff --git a/qpid/cpp/src/qpid/replication/constants.h b/qpid/cpp/src/qpid/replication/constants.h
new file mode 100644
index 0000000000..c5ba7d3d6a
--- /dev/null
+++ b/qpid/cpp/src/qpid/replication/constants.h
@@ -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.
+ *
+ */
+namespace qpid {
+namespace replication {
+namespace constants {
+
+const std::string REPLICATION_EVENT_TYPE("qpid.replication.type");
+const std::string REPLICATION_EVENT_SEQNO("qpid.replication.seqno");
+const std::string REPLICATION_TARGET_QUEUE("qpid.replication.target_queue");
+const std::string DEQUEUED_MESSAGE_POSITION("qpid.replication.message");
+const std::string QUEUE_MESSAGE_POSITION("qpid.replication.queue.position");
+
+const int ENQUEUE(1);
+const int DEQUEUE(2);
+
+}}}
diff --git a/qpid/cpp/src/qpid/store/CMakeLists.txt b/qpid/cpp/src/qpid/store/CMakeLists.txt
new file mode 100644
index 0000000000..464d2de052
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/CMakeLists.txt
@@ -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(qpidc_store)
+
+#set (CMAKE_VERBOSE_MAKEFILE ON) # for debugging
+
+include_directories( ${Boost_INCLUDE_DIR} )
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+include_directories( ${CMAKE_HOME_DIRECTORY}/include )
+
+link_directories( ${Boost_LIBRARY_DIRS} )
+
+set (store_SOURCES
+ MessageStorePlugin.cpp
+ )
+add_library (store MODULE ${store_SOURCES})
+target_link_libraries (store qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties (store PROPERTIES
+ PREFIX ""
+ LINK_FLAGS -Wl,--no-undefined)
+endif (CMAKE_COMPILER_IS_GNUCXX)
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (MSVC)
+ add_definitions(
+ /D "NOMINMAX"
+ /D "WIN32_LEAN_AND_MEAN"
+ )
+ endif (MSVC)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+set_target_properties (store PROPERTIES VERSION ${qpidc_version})
+install (TARGETS store # RUNTIME
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+# Build the MS SQL Storage Provider plugin
+set (mssql_default ON)
+if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set(mssql_default OFF)
+endif (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+option(BUILD_MSSQL "Build MS SQL Store provider plugin" ${mssql_default})
+if (BUILD_MSSQL)
+ add_library (mssql_store MODULE
+ ms-sql/MsSqlProvider.cpp
+ ms-sql/AmqpTransaction.cpp
+ ms-sql/BindingRecordset.cpp
+ ms-sql/BlobAdapter.cpp
+ ms-sql/BlobEncoder.cpp
+ ms-sql/BlobRecordset.cpp
+ ms-sql/DatabaseConnection.cpp
+ ms-sql/MessageMapRecordset.cpp
+ ms-sql/MessageRecordset.cpp
+ ms-sql/Recordset.cpp
+ ms-sql/SqlTransaction.cpp
+ ms-sql/State.cpp
+ ms-sql/TplRecordset.cpp
+ ms-sql/VariantHelper.cpp)
+ target_link_libraries (mssql_store qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ install (TARGETS mssql_store # RUNTIME
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+endif (BUILD_MSSQL)
+
+# Build the MS SQL-CLFS Storage Provider plugin
+set (msclfs_default ON)
+if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set(msclfs_default OFF)
+endif (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+option(BUILD_MSCLFS "Build MS hybrid SQL-CLFS Store provider plugin" ${msclfs_default})
+if (BUILD_MSCLFS)
+ add_library (msclfs_store MODULE
+ ms-clfs/MsSqlClfsProvider.cpp
+ ms-clfs/Log.cpp
+ ms-clfs/MessageLog.cpp
+ ms-clfs/Messages.cpp
+ ms-clfs/Transaction.cpp
+ ms-clfs/TransactionLog.cpp
+ ms-sql/BindingRecordset.cpp
+ ms-sql/BlobAdapter.cpp
+ ms-sql/BlobEncoder.cpp
+ ms-sql/BlobRecordset.cpp
+ ms-sql/DatabaseConnection.cpp
+ ms-sql/Recordset.cpp
+ ms-sql/State.cpp
+ ms-sql/VariantHelper.cpp)
+ include_directories(ms-sql)
+ target_link_libraries (msclfs_store qpidbroker qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY} clfsw32.lib)
+ install (TARGETS msclfs_store # RUNTIME
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+endif (BUILD_MSCLFS)
diff --git a/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp b/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp
new file mode 100644
index 0000000000..2a8d971987
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp
@@ -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.
+ *
+ */
+
+#include "MessageStorePlugin.h"
+#include "StorageProvider.h"
+#include "StoreException.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/DataDir.h"
+#include "qpid/log/Statement.h"
+
+/*
+ * The MessageStore pointer given to the Broker points to static storage.
+ * Thus, it cannot be deleted, especially by the broker. To prevent deletion,
+ * this no-op deleter is used with the boost::shared_ptr. When the last
+ * shared_ptr is destroyed, the deleter is called rather than delete().
+ */
+namespace {
+ class NoopDeleter {
+ public:
+ NoopDeleter() {}
+ void operator()(qpid::broker::MessageStore * /*p*/) {}
+ };
+}
+
+namespace qpid {
+namespace store {
+
+static MessageStorePlugin static_instance_registers_plugin;
+
+
+MessageStorePlugin::StoreOptions::StoreOptions(const std::string& name) :
+ qpid::Options(name)
+{
+ addOptions()
+ ("storage-provider", qpid::optValue(providerName, "PROVIDER"),
+ "Name of the storage provider to use.")
+ ;
+}
+
+
+void
+MessageStorePlugin::earlyInitialize (qpid::Plugin::Target& target)
+{
+ qpid::broker::Broker* b =
+ dynamic_cast<qpid::broker::Broker*>(&target);
+ if (0 == b)
+ return; // Only listen to Broker targets
+
+ broker = b;
+
+ // See if there are any storage provider plugins ready. If not, we can't
+ // do a message store.
+ qpid::Plugin::earlyInitAll(*this);
+
+ if (providers.empty()) {
+ QPID_LOG(warning,
+ "Message store plugin: No storage providers available.");
+ provider = providers.end();
+ return;
+ }
+ if (!options.providerName.empty()) {
+ // If specific one was chosen, locate it in loaded set of providers.
+ provider = providers.find(options.providerName);
+ if (provider == providers.end())
+ throw Exception("Message store plugin: storage provider '" +
+ options.providerName +
+ "' does not exist.");
+ }
+ else {
+ // No specific provider chosen; if there's only one, use it. Else
+ // report the need to pick one.
+ if (providers.size() > 1) {
+ provider = providers.end();
+ throw Exception("Message store plugin: multiple provider plugins "
+ "loaded; must either load only one or select one "
+ "using --storage-provider");
+ }
+ provider = providers.begin();
+ }
+
+ provider->second->activate(*this);
+ NoopDeleter d;
+ boost::shared_ptr<qpid::broker::MessageStore> sp(this, d);
+ broker->setStore(sp);
+ target.addFinalizer(boost::bind(&MessageStorePlugin::finalizeMe, this));
+}
+
+void
+MessageStorePlugin::initialize(qpid::Plugin::Target& target)
+{
+ qpid::broker::Broker* broker =
+ dynamic_cast<qpid::broker::Broker*>(&target);
+ if (0 == broker)
+ return; // Only listen to Broker targets
+
+ // Pass along the initialize step to the provider that's activated.
+ if (provider != providers.end()) {
+ provider->second->initialize(*this);
+ }
+ // qpid::Plugin::initializeAll(*this);
+}
+
+void
+MessageStorePlugin::finalizeMe()
+{
+ finalize(); // Call finalizers on any Provider plugins
+}
+
+void
+MessageStorePlugin::providerAvailable(const std::string name,
+ StorageProvider *be)
+{
+ ProviderMap::value_type newSp(name, be);
+ std::pair<ProviderMap::iterator, bool> inserted = providers.insert(newSp);
+ if (inserted.second == false)
+ QPID_LOG(warning, "Storage provider " << name << " duplicate; ignored.");
+}
+
+void
+MessageStorePlugin::truncateInit(const bool /*saveStoreContent*/)
+{
+ QPID_LOG(info, "Store: truncateInit");
+}
+
+
+/**
+ * Record the existence of a durable queue
+ */
+void
+MessageStorePlugin::create(broker::PersistableQueue& queue,
+ const framing::FieldTable& args)
+{
+ if (queue.getName().size() == 0)
+ {
+ QPID_LOG(error,
+ "Cannot create store for empty (null) queue name - "
+ "ignoring and attempting to continue.");
+ return;
+ }
+ if (queue.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("Queue already created: " + queue.getName());
+ }
+ provider->second->create(queue, args);
+}
+
+/**
+ * Destroy a durable queue
+ */
+void
+MessageStorePlugin::destroy(broker::PersistableQueue& queue)
+{
+ provider->second->destroy(queue);
+}
+
+/**
+ * Record the existence of a durable exchange
+ */
+void
+MessageStorePlugin::create(const broker::PersistableExchange& exchange,
+ const framing::FieldTable& args)
+{
+ if (exchange.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("Exchange already created: " + exchange.getName());
+ }
+ provider->second->create(exchange, args);
+}
+
+/**
+ * Destroy a durable exchange
+ */
+void
+MessageStorePlugin::destroy(const broker::PersistableExchange& exchange)
+{
+ provider->second->destroy(exchange);
+}
+
+/**
+ * Record a binding
+ */
+void
+MessageStorePlugin::bind(const broker::PersistableExchange& exchange,
+ const broker::PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args)
+{
+ provider->second->bind(exchange, queue, key, args);
+}
+
+/**
+ * Forget a binding
+ */
+void
+MessageStorePlugin::unbind(const broker::PersistableExchange& exchange,
+ const broker::PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args)
+{
+ provider->second->unbind(exchange, queue, key, args);
+}
+
+/**
+ * Record generic durable configuration
+ */
+void
+MessageStorePlugin::create(const broker::PersistableConfig& config)
+{
+ if (config.getPersistenceId()) {
+ THROW_STORE_EXCEPTION("Config item already created: " +
+ config.getName());
+ }
+ provider->second->create(config);
+}
+
+/**
+ * Destroy generic durable configuration
+ */
+void
+MessageStorePlugin::destroy(const broker::PersistableConfig& config)
+{
+ provider->second->destroy(config);
+}
+
+/**
+ * Stores a message before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point).
+ */
+void
+MessageStorePlugin::stage(const boost::intrusive_ptr<broker::PersistableMessage>& msg)
+{
+ if (msg->getPersistenceId() == 0 && !msg->isContentReleased()) {
+ provider->second->stage(msg);
+ }
+}
+
+/**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+void
+MessageStorePlugin::destroy(broker::PersistableMessage& msg)
+{
+ if (msg.getPersistenceId())
+ provider->second->destroy(msg);
+}
+
+/**
+ * Appends content to a previously staged message
+ */
+void
+MessageStorePlugin::appendContent
+ (const boost::intrusive_ptr<const broker::PersistableMessage>& msg,
+ const std::string& data)
+{
+ if (msg->getPersistenceId())
+ provider->second->appendContent(msg, data);
+ else
+ THROW_STORE_EXCEPTION("Cannot append content. Message not known to store!");
+}
+
+/**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+void
+MessageStorePlugin::loadContent(const broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const broker::PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+ if (msg->getPersistenceId())
+ provider->second->loadContent(queue, msg, data, offset, length);
+ else
+ THROW_STORE_EXCEPTION("Cannot load content. Message not known to store!");
+}
+
+/**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ */
+void
+MessageStorePlugin::enqueue(broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<broker::PersistableMessage>& msg,
+ const broker::PersistableQueue& queue)
+{
+ if (queue.getPersistenceId() == 0) {
+ THROW_STORE_EXCEPTION("Queue not created: " + queue.getName());
+ }
+ provider->second->enqueue(ctxt, msg, queue);
+}
+
+/**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ */
+void
+MessageStorePlugin::dequeue(broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<broker::PersistableMessage>& msg,
+ const broker::PersistableQueue& queue)
+{
+ provider->second->dequeue(ctxt, msg, queue);
+}
+
+/**
+ * Flushes all async messages to disk for the specified queue
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ */
+void
+MessageStorePlugin::flush(const broker::PersistableQueue& queue)
+{
+ provider->second->flush(queue);
+}
+
+/**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk.
+ */
+uint32_t
+MessageStorePlugin::outstandingQueueAIO(const broker::PersistableQueue& queue)
+{
+ return provider->second->outstandingQueueAIO(queue);
+}
+
+std::auto_ptr<broker::TransactionContext>
+MessageStorePlugin::begin()
+{
+ return provider->second->begin();
+}
+
+std::auto_ptr<broker::TPCTransactionContext>
+MessageStorePlugin::begin(const std::string& xid)
+{
+ return provider->second->begin(xid);
+}
+
+void
+MessageStorePlugin::prepare(broker::TPCTransactionContext& ctxt)
+{
+ provider->second->prepare(ctxt);
+}
+
+void
+MessageStorePlugin::commit(broker::TransactionContext& ctxt)
+{
+ provider->second->commit(ctxt);
+}
+
+void
+MessageStorePlugin::abort(broker::TransactionContext& ctxt)
+{
+ provider->second->abort(ctxt);
+}
+
+void
+MessageStorePlugin::collectPreparedXids(std::set<std::string>& xids)
+{
+ provider->second->collectPreparedXids(xids);
+}
+
+/**
+ * Request recovery of queue and message state; inherited from Recoverable
+ */
+void
+MessageStorePlugin::recover(broker::RecoveryManager& recoverer)
+{
+ ExchangeMap exchanges;
+ QueueMap queues;
+ MessageMap messages;
+ MessageQueueMap messageQueueMap;
+ std::vector<std::string> xids;
+ PreparedTransactionMap dtxMap;
+
+ provider->second->recoverConfigs(recoverer);
+ provider->second->recoverExchanges(recoverer, exchanges);
+ provider->second->recoverQueues(recoverer, queues);
+ provider->second->recoverBindings(recoverer, exchanges, queues);
+ // Important to recover messages before transactions in the SQL-CLFS
+ // case. If this becomes a problem, it may be possible to resolve it.
+ // If in doubt please raise a jira and notify Steve Huston
+ // <shuston@riverace.com>.
+ provider->second->recoverMessages(recoverer, messages, messageQueueMap);
+ provider->second->recoverTransactions(recoverer, dtxMap);
+ // Enqueue msgs where needed.
+ for (MessageQueueMap::const_iterator i = messageQueueMap.begin();
+ i != messageQueueMap.end();
+ ++i) {
+ // Locate the message corresponding to the current message Id
+ MessageMap::const_iterator iMsg = messages.find(i->first);
+ if (iMsg == messages.end()) {
+ std::ostringstream oss;
+ oss << "No matching message trying to re-enqueue message "
+ << i->first;
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ broker::RecoverableMessage::shared_ptr msg = iMsg->second;
+ // Now for each queue referenced in the queue map, locate it
+ // and re-enqueue the message.
+ for (std::vector<QueueEntry>::const_iterator j = i->second.begin();
+ j != i->second.end();
+ ++j) {
+ // Locate the queue corresponding to the current queue Id
+ QueueMap::const_iterator iQ = queues.find(j->queueId);
+ if (iQ == queues.end()) {
+ std::ostringstream oss;
+ oss << "No matching queue trying to re-enqueue message "
+ << " on queue Id " << j->queueId;
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ // Messages involved in prepared transactions have their status
+ // updated accordingly. First, though, restore a message that
+ // is expected to be on a queue, including non-transacted
+ // messages and those pending dequeue in a dtx.
+ if (j->tplStatus != QueueEntry::ADDING)
+ iQ->second->recover(msg);
+ switch(j->tplStatus) {
+ case QueueEntry::ADDING:
+ dtxMap[j->xid]->enqueue(iQ->second, msg);
+ break;
+ case QueueEntry::REMOVING:
+ dtxMap[j->xid]->dequeue(iQ->second, msg);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+}} // namespace qpid::store
diff --git a/qpid/cpp/src/qpid/store/MessageStorePlugin.h b/qpid/cpp/src/qpid/store/MessageStorePlugin.h
new file mode 100644
index 0000000000..4a9bb2aecb
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/MessageStorePlugin.h
@@ -0,0 +1,288 @@
+#ifndef QPID_STORE_MESSAGESTOREPLUGIN_H
+#define QPID_STORE_MESSAGESTOREPLUGIN_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/PersistableExchange.h"
+#include "qpid/broker/PersistableMessage.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/management/Manageable.h"
+
+#include <string>
+
+using namespace qpid;
+
+namespace qpid {
+namespace store {
+
+class StorageProvider;
+
+/**
+ * @class MessageStorePlugin
+ *
+ * MessageStorePlugin is the front end of the persistent message store
+ * plugin. It is responsible for coordinating recovery, initialization,
+ * transactions (both local and distributed), flow-to-disk loading and
+ * unloading and persisting broker state (queues, bindings etc.).
+ * Actual storage operations are carried out by a message store storage
+ * provider that implements the qpid::store::StorageProvider interface.
+ */
+class MessageStorePlugin :
+ public qpid::Plugin,
+ public qpid::broker::MessageStore, // Frontend classes
+ public qpid::Plugin::Target // Provider target
+ // @TODO Need a mgmt story for this. Maybe allow r/o access to provider store info? public qpid::management::Manageable
+{
+ public:
+ MessageStorePlugin() : broker(0) {}
+
+ /**
+ * @name Methods inherited from qpid::Plugin
+ */
+ //@{
+ virtual Options* getOptions() { return &options; }
+ virtual void earlyInitialize (Plugin::Target& target);
+ virtual void initialize(Plugin::Target& target);
+ //@}
+
+ /// Finalizer; calls Target::finalize() to run finalizers on
+ /// StorageProviders.
+ void finalizeMe();
+
+ /**
+ * Called by StorageProvider instances during the earlyInitialize sequence.
+ * Each StorageProvider must supply a unique name by which it is known and a
+ * pointer to itself.
+ */
+ virtual void providerAvailable(const std::string name, StorageProvider *be);
+
+ /**
+ * @name Methods inherited from qpid::broker::MessageStore
+ */
+ //@{
+ /**
+ * If called before recovery, will discard the database and reinitialize
+ * using an empty store. This is used when cluster nodes recover and
+ * must get their content from a cluster sync rather than directly from
+ * the store.
+ *
+ * @param saveStoreContent If true, the store's contents should be
+ * saved to a backup location before
+ * reinitializing the store content.
+ */
+ virtual void truncateInit(const bool saveStoreContent = false);
+
+ /**
+ * Record the existence of a durable queue
+ */
+ virtual void create(broker::PersistableQueue& queue,
+ const framing::FieldTable& args);
+ /**
+ * Destroy a durable queue
+ */
+ virtual void destroy(broker::PersistableQueue& queue);
+
+ /**
+ * Record the existence of a durable exchange
+ */
+ virtual void create(const broker::PersistableExchange& exchange,
+ const framing::FieldTable& args);
+ /**
+ * Destroy a durable exchange
+ */
+ virtual void destroy(const broker::PersistableExchange& exchange);
+
+ /**
+ * Record a binding
+ */
+ virtual void bind(const broker::PersistableExchange& exchange,
+ const broker::PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args);
+
+ /**
+ * Forget a binding
+ */
+ virtual void unbind(const broker::PersistableExchange& exchange,
+ const broker::PersistableQueue& queue,
+ const std::string& key,
+ const framing::FieldTable& args);
+
+ /**
+ * Record generic durable configuration
+ */
+ virtual void create(const broker::PersistableConfig& config);
+
+ /**
+ * Destroy generic durable configuration
+ */
+ virtual void destroy(const broker::PersistableConfig& config);
+
+ /**
+ * Stores a message before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+ virtual void stage(const boost::intrusive_ptr<broker::PersistableMessage>& msg);
+
+ /**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+ virtual void destroy(broker::PersistableMessage& msg);
+
+ /**
+ * Appends content to a previously staged message
+ */
+ virtual void appendContent(const boost::intrusive_ptr<const broker::PersistableMessage>& msg,
+ const std::string& data);
+
+ /**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+ virtual void loadContent(const broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const broker::PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ /**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ *
+ * @param msg the message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void enqueue(broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<broker::PersistableMessage>& msg,
+ const broker::PersistableQueue& queue);
+
+ /**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ *
+ * @param msg the message to dequeue
+ * @param queue the name of the queue from which it is to be dequeued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void dequeue(broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<broker::PersistableMessage>& msg,
+ const broker::PersistableQueue& queue);
+
+ /**
+ * Flushes all async messages to disk for the specified queue
+ *
+ *
+ * Note: The operation is asynchronous so the return of this function does
+ * not mean the operation is complete.
+ *
+ * @param queue the name of the queue from which it is to be dequeued
+ */
+ virtual void flush(const broker::PersistableQueue& queue);
+
+ /**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk
+ *
+ * @param queue the name of the queue to check for outstanding AIO
+ */
+ virtual uint32_t outstandingQueueAIO(const broker::PersistableQueue& queue);
+ //@}
+
+ /**
+ * @name Methods inherited from qpid::broker::TransactionalStore
+ */
+ //@{
+ std::auto_ptr<broker::TransactionContext> begin();
+
+ std::auto_ptr<broker::TPCTransactionContext> begin(const std::string& xid);
+
+ void prepare(broker::TPCTransactionContext& ctxt);
+
+ void commit(broker::TransactionContext& ctxt);
+
+ void abort(broker::TransactionContext& ctxt);
+
+ void collectPreparedXids(std::set<std::string>& xids);
+ //@}
+
+ /**
+ * Request recovery of queue and message state; inherited from Recoverable
+ */
+ virtual void recover(broker::RecoveryManager& recoverer);
+
+ // inline management::Manageable::status_t ManagementMethod (uint32_t, management::Args&, std::string&)
+ // { return management::Manageable::STATUS_OK; }
+
+ // So storage provider can get the broker info.
+ broker::Broker *getBroker() { return broker; }
+
+ protected:
+
+ struct StoreOptions : public qpid::Options {
+ StoreOptions(const std::string& name="Store Options");
+ std::string providerName;
+ };
+ StoreOptions options;
+
+ typedef std::map<const std::string, StorageProvider*> ProviderMap;
+ ProviderMap providers;
+ ProviderMap::const_iterator provider;
+
+ broker::Broker *broker;
+
+}; // class MessageStoreImpl
+
+}} // namespace qpid::store
+
+#endif /* QPID_SERIALIZER_H */
diff --git a/qpid/cpp/src/qpid/store/StorageProvider.h b/qpid/cpp/src/qpid/store/StorageProvider.h
new file mode 100644
index 0000000000..bc8d187517
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/StorageProvider.h
@@ -0,0 +1,343 @@
+#ifndef QPID_STORE_STORAGEPROVIDER_H
+#define QPID_STORE_STORAGEPROVIDER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <map>
+#include <stdexcept>
+#include <vector>
+#include "qpid/Exception.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/broker/MessageStore.h"
+
+using qpid::broker::PersistableConfig;
+using qpid::broker::PersistableExchange;
+using qpid::broker::PersistableMessage;
+using qpid::broker::PersistableQueue;
+
+namespace qpid {
+namespace store {
+
+typedef std::map<uint64_t, qpid::broker::RecoverableExchange::shared_ptr>
+ ExchangeMap;
+typedef std::map<uint64_t, qpid::broker::RecoverableQueue::shared_ptr>
+ QueueMap;
+typedef std::map<uint64_t, qpid::broker::RecoverableMessage::shared_ptr>
+ MessageMap;
+// Msg Id -> vector of queue entries where message is queued
+struct QueueEntry {
+ enum TplStatus { NONE = 0, ADDING = 1, REMOVING = 2 };
+ uint64_t queueId;
+ TplStatus tplStatus;
+ std::string xid;
+
+ QueueEntry(uint64_t id, TplStatus tpl = NONE, const std::string& x = "")
+ : queueId(id), tplStatus(tpl), xid(x) {}
+
+ bool operator==(const QueueEntry& rhs) {
+ if (queueId != rhs.queueId) return false;
+ if (tplStatus == NONE && rhs.tplStatus == NONE) return true;
+ return xid == rhs.xid;
+ }
+};
+typedef std::map<uint64_t, std::vector<QueueEntry> > MessageQueueMap;
+typedef std::map<std::string, qpid::broker::RecoverableTransaction::shared_ptr>
+ PreparedTransactionMap;
+
+class MessageStorePlugin;
+
+/**
+ * @class StorageProvider
+ *
+ * StorageProvider defines the interface for the storage provider plugin to the
+ * Qpid broker persistence store plugin.
+ *
+ * @TODO Should StorageProvider also inherit from MessageStore? If so, then
+ * maybe remove Recoverable from MessageStore's inheritance and move it
+ * to MessageStorePlugin? In any event, somehow the discardInit() feature
+ * needs to get added here.
+ */
+class StorageProvider : public qpid::Plugin, public qpid::broker::MessageStore
+{
+public:
+
+ class Exception : public qpid::Exception
+ {
+ public:
+ virtual ~Exception() throw() {}
+ virtual const char *what() const throw() = 0;
+ };
+
+ /**
+ * @name Methods inherited from qpid::Plugin
+ */
+ //@{
+ /**
+ * Return a pointer to the provider's options. The options will be
+ * updated during option parsing by the host program; therefore, the
+ * referenced Options object must remain valid past this function's return.
+ *
+ * @return An options group or 0 for no options. Default returns 0.
+ * Plugin retains ownership of return value.
+ */
+ virtual qpid::Options* getOptions() = 0;
+
+ /**
+ * Initialize Plugin functionality on a Target, called before
+ * initializing the target.
+ *
+ * StorageProviders should respond only to Targets of class
+ * qpid::store::MessageStorePlugin and ignore all others.
+ *
+ * When called, the provider should invoke the method
+ * qpid::store::MessageStorePlugin::providerAvailable() to alert the
+ * message store of StorageProvider's availability.
+ *
+ * Called before the target itself is initialized.
+ */
+ virtual void earlyInitialize (Plugin::Target& target) = 0;
+
+ /**
+ * Initialize StorageProvider functionality. Called after initializing
+ * the target.
+ *
+ * StorageProviders should respond only to Targets of class
+ * qpid::store::MessageStorePlugin and ignore all others.
+ *
+ * Called after the target is fully initialized.
+ */
+ virtual void initialize(Plugin::Target& target) = 0;
+ //@}
+
+ /**
+ * Receive notification that this provider is the one that will actively
+ * handle storage for the target. If the provider is to be used, this
+ * method will be called after earlyInitialize() and before any
+ * recovery operations (recovery, in turn, precedes call to initialize()).
+ * Thus, it is wise to not actually do any database ops from within
+ * earlyInitialize() - they can wait until activate() is called because
+ * at that point it is certain the database will be needed.
+ */
+ virtual void activate(MessageStorePlugin &store) = 0;
+
+ /**
+ * @name Methods inherited from qpid::broker::MessageStore
+ */
+ //@{
+ /**
+ * If called after init() but before recovery, will discard the database
+ * and reinitialize using an empty store dir. If @a pushDownStoreFiles
+ * is true, the content of the store dir will be moved to a backup dir
+ * inside the store dir. This is used when cluster nodes recover and must
+ * get thier content from a cluster sync rather than directly fromt the
+ * store.
+ *
+ * @param pushDownStoreFiles If true, will move content of the store dir
+ * into a subdir, leaving the store dir
+ * otherwise empty.
+ */
+ virtual void truncateInit(const bool pushDownStoreFiles = false) = 0;
+
+ /**
+ * Record the existence of a durable queue
+ */
+ virtual void create(PersistableQueue& queue,
+ const qpid::framing::FieldTable& args) = 0;
+ /**
+ * Destroy a durable queue
+ */
+ virtual void destroy(PersistableQueue& queue) = 0;
+
+ /**
+ * Record the existence of a durable exchange
+ */
+ virtual void create(const PersistableExchange& exchange,
+ const qpid::framing::FieldTable& args) = 0;
+ /**
+ * Destroy a durable exchange
+ */
+ virtual void destroy(const PersistableExchange& exchange) = 0;
+
+ /**
+ * Record a binding
+ */
+ virtual void bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args) = 0;
+
+ /**
+ * Forget a binding
+ */
+ virtual void unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args) = 0;
+
+ /**
+ * Record generic durable configuration
+ */
+ virtual void create(const PersistableConfig& config) = 0;
+
+ /**
+ * Destroy generic durable configuration
+ */
+ virtual void destroy(const PersistableConfig& config) = 0;
+
+ /**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+ virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg) = 0;
+
+ /**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+ virtual void destroy(PersistableMessage& msg) = 0;
+
+ /**
+ * Appends content to a previously staged message
+ */
+ virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data) = 0;
+
+ /**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+ virtual void loadContent(const PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length) = 0;
+
+ /**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue) = 0;
+
+ /**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to dequeue
+ * @param queue the name of the queue from which it is to be dequeued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue) = 0;
+
+ /**
+ * Flushes all async messages to disk for the specified queue
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param queue the name of the queue from which it is to be dequeued
+ */
+ virtual void flush(const qpid::broker::PersistableQueue& queue) = 0;
+
+ /**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk
+ *
+ * @param queue the name of the queue to check for outstanding AIO
+ */
+ virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue) = 0;
+ //@}
+
+ /**
+ * @TODO This should probably not be here - it's only here because
+ * MessageStore inherits from Recoverable... maybe move that derivation.
+ *
+ * As it is now, we don't use this. Separate recover methods are
+ * declared below for individual types, which also set up maps of
+ * messages, queues, transactions for the main store plugin to handle
+ * properly.
+ *
+ * Request recovery of queue and message state.
+ */
+ virtual void recover(qpid::broker::RecoveryManager& /*recoverer*/) {}
+
+ /**
+ * @name Methods that do the recovery of the various objects that
+ * were saved.
+ */
+ //@{
+
+ /**
+ * Recover bindings.
+ */
+ virtual void recoverConfigs(qpid::broker::RecoveryManager& recoverer) = 0;
+ virtual void recoverExchanges(qpid::broker::RecoveryManager& recoverer,
+ ExchangeMap& exchangeMap) = 0;
+ virtual void recoverQueues(qpid::broker::RecoveryManager& recoverer,
+ QueueMap& queueMap) = 0;
+ virtual void recoverBindings(qpid::broker::RecoveryManager& recoverer,
+ const ExchangeMap& exchangeMap,
+ const QueueMap& queueMap) = 0;
+ virtual void recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap) = 0;
+ virtual void recoverTransactions(qpid::broker::RecoveryManager& recoverer,
+ PreparedTransactionMap& dtxMap) = 0;
+ //@}
+};
+
+}} // namespace qpid::store
+
+#endif /* QPID_STORE_STORAGEPROVIDER_H */
diff --git a/qpid/cpp/src/qpid/store/StoreException.h b/qpid/cpp/src/qpid/store/StoreException.h
new file mode 100644
index 0000000000..1dc7f670ec
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/StoreException.h
@@ -0,0 +1,49 @@
+#ifndef QPID_STORE_STOREEXCEPTION_H
+#define QPID_STORE_STOREEXCEPTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <exception>
+#include <boost/format.hpp>
+#include "StorageProvider.h"
+
+namespace qpid {
+namespace store {
+
+class StoreException : public std::exception
+{
+ std::string text;
+public:
+ StoreException(const std::string& _text) : text(_text) {}
+ StoreException(const std::string& _text,
+ const StorageProvider::Exception& cause)
+ : text(_text + ": " + cause.what()) {}
+ virtual ~StoreException() throw() {}
+ virtual const char* what() const throw() { return text.c_str(); }
+};
+
+#define THROW_STORE_EXCEPTION(MESSAGE) throw qpid::store::StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__))
+#define THROW_STORE_EXCEPTION_2(MESSAGE, EXCEPTION) throw qpid::store::StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__), EXCEPTION)
+
+}} // namespace qpid::store
+
+#endif /* QPID_STORE_STOREEXCEPTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Log.cpp b/qpid/cpp/src/qpid/store/ms-clfs/Log.cpp
new file mode 100644
index 0000000000..e6cb10c133
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Log.cpp
@@ -0,0 +1,182 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <windows.h>
+#include <clfsw32.h>
+#include <clfsmgmtw32.h>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <stdlib.h>
+#include <qpid/sys/windows/check.h>
+
+#include "Log.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+Log::~Log()
+{
+ if (marshal != 0)
+ ::DeleteLogMarshallingArea(marshal);
+ ::CloseHandle(handle);
+}
+
+void
+Log::open(const std::string& path, const TuningParameters& params)
+{
+ this->containerSize = static_cast<ULONGLONG>(params.containerSize);
+ logPath = path;
+ std::string logSpec = "log:" + path;
+ size_t specLength = logSpec.length();
+ std::auto_ptr<wchar_t> wLogSpec(new wchar_t[specLength + 1]);
+ size_t converted;
+ mbstowcs_s(&converted,
+ wLogSpec.get(), specLength+1,
+ logSpec.c_str(), specLength);
+ handle = ::CreateLogFile(wLogSpec.get(),
+ GENERIC_WRITE | GENERIC_READ,
+ 0,
+ 0,
+ OPEN_ALWAYS,
+ 0);
+ QPID_WINDOWS_CHECK_NOT(handle, INVALID_HANDLE_VALUE);
+ CLFS_INFORMATION info;
+ ULONG infoSize = sizeof(info);
+ BOOL ok = ::GetLogFileInformation(handle, &info, &infoSize);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+ ok = ::RegisterManageableLogClient(handle, 0);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ // Set up policies for how many containers to initially create and how
+ // large each container should be. Also, auto-grow the log when container
+ // space runs out.
+ CLFS_MGMT_POLICY logPolicy;
+ logPolicy.Version = CLFS_MGMT_POLICY_VERSION;
+ logPolicy.LengthInBytes = sizeof(logPolicy);
+ logPolicy.PolicyFlags = 0;
+
+ // If this is the first time this log is opened, give an opportunity to
+ // initialize its content.
+ bool needInitialize(false);
+ if (info.TotalContainers == 0) {
+ // New log; set the configured container size and create the
+ // initial set of containers.
+ logPolicy.PolicyType = ClfsMgmtPolicyNewContainerSize;
+ logPolicy.PolicyParameters.NewContainerSize.SizeInBytes = containerSize;
+ ok = ::InstallLogPolicy(handle, &logPolicy);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ ULONGLONG desired(params.containers), actual(0);
+ ok = ::SetLogFileSizeWithPolicy(handle, &desired, &actual);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ needInitialize = true;
+ }
+ // Ensure that the log is extended as needed and will shrink when 50%
+ // becomes unused.
+ logPolicy.PolicyType = ClfsMgmtPolicyAutoGrow;
+ logPolicy.PolicyParameters.AutoGrow.Enabled = 1;
+ ok = ::InstallLogPolicy(handle, &logPolicy);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+ logPolicy.PolicyType = ClfsMgmtPolicyAutoShrink;
+ logPolicy.PolicyParameters.AutoShrink.Percentage = params.shrinkPct;
+ ok = ::InstallLogPolicy(handle, &logPolicy);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ // Need a marshaling area
+ ok = ::CreateLogMarshallingArea(handle,
+ NULL, NULL, NULL, // Alloc, free, context
+ marshallingBufferSize(),
+ params.maxWriteBuffers,
+ 1, // Max read buffers
+ &marshal);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+ if (needInitialize)
+ initialize();
+}
+
+uint32_t
+Log::marshallingBufferSize()
+{
+ // Default implementation returns the minimum marshalling buffer size;
+ // derived ones should come up with a more fitting value.
+ //
+ // Find the directory name part of the log specification, including the
+ // trailing '\'.
+ size_t dirMarker = logPath.rfind('\\');
+ if (dirMarker == std::string::npos)
+ dirMarker = logPath.rfind('/');
+ DWORD bytesPerSector;
+ DWORD dontCare;
+ ::GetDiskFreeSpace(logPath.substr(0, dirMarker).c_str(),
+ &dontCare,
+ &bytesPerSector,
+ &dontCare,
+ &dontCare);
+ return bytesPerSector;
+}
+
+CLFS_LSN
+Log::write(void* entry, uint32_t length, CLFS_LSN* prev)
+{
+ CLFS_WRITE_ENTRY desc;
+ desc.Buffer = entry;
+ desc.ByteLength = length;
+ CLFS_LSN lsn;
+ BOOL ok = ::ReserveAndAppendLog(marshal,
+ &desc, 1, // Buffer descriptor
+ 0, prev, // Undo-Next, Prev
+ 0, 0, // Reservation
+ CLFS_FLAG_FORCE_FLUSH,
+ &lsn,
+ 0);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+ return lsn;
+}
+
+// Get the current base LSN of the log.
+CLFS_LSN
+Log::getBase()
+{
+ CLFS_INFORMATION info;
+ ULONG infoSize = sizeof(info);
+ BOOL ok = ::GetLogFileInformation(handle, &info, &infoSize);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+ return info.BaseLsn;
+}
+
+void
+Log::moveTail(const CLFS_LSN& oldest)
+{
+ BOOL ok = ::AdvanceLogBase(marshal,
+ const_cast<PCLFS_LSN>(&oldest),
+ 0, NULL);
+ // If multiple threads are manipulating things they may get out of
+ // order when moving the tail; if someone already moved it further
+ // than this, it's ok - ignore it.
+ if (ok || ::GetLastError() == ERROR_LOG_START_OF_LOG)
+ return;
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+}
+
+}}} // namespace qpid::store::ms_clfs
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Log.h b/qpid/cpp/src/qpid/store/ms-clfs/Log.h
new file mode 100644
index 0000000000..2f7eb6cada
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Log.h
@@ -0,0 +1,78 @@
+#ifndef QPID_STORE_MSCLFS_LOG_H
+#define QPID_STORE_MSCLFS_LOG_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include <windows.h>
+#include <clfsw32.h>
+#include <qpid/sys/IntegerTypes.h>
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+/**
+ * @class Log
+ *
+ * Represents a CLFS-housed log.
+ */
+class Log {
+
+protected:
+ HANDLE handle;
+ ULONGLONG containerSize;
+ std::string logPath;
+ PVOID marshal;
+
+ // Give subclasses a chance to initialize a new log. Called after a new
+ // log is created, initial set of containers is added, and marshalling
+ // area is allocated.
+ virtual void initialize() {}
+
+public:
+ struct TuningParameters {
+ size_t containerSize;
+ unsigned short containers;
+ unsigned short shrinkPct;
+ uint32_t maxWriteBuffers;
+ };
+
+ Log() : handle(INVALID_HANDLE_VALUE), containerSize(0), marshal(0) {}
+ virtual ~Log();
+
+ void open(const std::string& path, const TuningParameters& params);
+
+ virtual uint32_t marshallingBufferSize();
+
+ CLFS_LSN write(void* entry, uint32_t length, CLFS_LSN* prev = 0);
+
+ // Get the current base LSN of the log.
+ CLFS_LSN getBase();
+
+ // Move the log tail to the indicated LSN.
+ void moveTail(const CLFS_LSN& oldest);
+};
+
+}}} // namespace qpid::store::ms_clfs
+
+#endif /* QPID_STORE_MSCLFS_LOG_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Lsn.h b/qpid/cpp/src/qpid/store/ms-clfs/Lsn.h
new file mode 100644
index 0000000000..7f46c1f266
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Lsn.h
@@ -0,0 +1,36 @@
+#ifndef QPID_STORE_MSCLFS_LSN_H
+#define QPID_STORE_MSCLFS_LSN_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <clfsw32.h>
+
+namespace {
+ // Make it easy to assign LSNs
+ inline CLFS_LSN idToLsn(const uint64_t val)
+ { CLFS_LSN lsn; lsn.Internal = val; return lsn; }
+
+ inline uint64_t lsnToId(const CLFS_LSN& lsn)
+ { uint64_t val = lsn.Internal; return val; }
+}
+
+#endif /* QPID_STORE_MSCLFS_LSN_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp b/qpid/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp
new file mode 100644
index 0000000000..586aaaf980
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/MSSqlClfsProvider.cpp
@@ -0,0 +1,1121 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <list>
+#include <map>
+#include <set>
+#include <stdlib.h>
+#include <string>
+#include <windows.h>
+#include <clfsw32.h>
+#include <qpid/broker/RecoverableQueue.h>
+#include <qpid/log/Statement.h>
+#include <qpid/store/MessageStorePlugin.h>
+#include <qpid/store/StoreException.h>
+#include <qpid/store/StorageProvider.h>
+#include <qpid/sys/Mutex.h>
+#include <boost/foreach.hpp>
+#include <boost/make_shared.hpp>
+
+// From ms-sql...
+#include "BlobAdapter.h"
+#include "BlobRecordset.h"
+#include "BindingRecordset.h"
+#include "DatabaseConnection.h"
+#include "Exception.h"
+#include "State.h"
+#include "VariantHelper.h"
+using qpid::store::ms_sql::BlobAdapter;
+using qpid::store::ms_sql::BlobRecordset;
+using qpid::store::ms_sql::BindingRecordset;
+using qpid::store::ms_sql::DatabaseConnection;
+using qpid::store::ms_sql::ADOException;
+using qpid::store::ms_sql::State;
+using qpid::store::ms_sql::VariantHelper;
+
+#include "Log.h"
+#include "Messages.h"
+#include "Transaction.h"
+#include "TransactionLog.h"
+
+// Bring in ADO 2.8 (yes, I know it says "15", but that's it...)
+#import "C:\Program Files\Common Files\System\ado\msado15.dll" \
+ no_namespace rename("EOF", "EndOfFile")
+#include <comdef.h>
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+
+// Table names
+const std::string TblBinding("tblBinding");
+const std::string TblConfig("tblConfig");
+const std::string TblExchange("tblExchange");
+const std::string TblQueue("tblQueue");
+
+}
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+/**
+ * @class MSSqlClfsProvider
+ *
+ * Implements a qpid::store::StorageProvider that uses a hybrid Microsoft
+ * SQL Server and Windows CLFS approach as the backend data store for Qpid.
+ */
+class MSSqlClfsProvider : public qpid::store::StorageProvider
+{
+protected:
+ void finalizeMe();
+
+ void dump();
+
+public:
+ MSSqlClfsProvider();
+ ~MSSqlClfsProvider();
+
+ virtual qpid::Options* getOptions() { return &options; }
+
+ virtual void earlyInitialize (Plugin::Target& target);
+ virtual void initialize(Plugin::Target& target);
+
+ /**
+ * Receive notification that this provider is the one that will actively
+ * handle provider storage for the target. If the provider is to be used,
+ * this method will be called after earlyInitialize() and before any
+ * recovery operations (recovery, in turn, precedes call to initialize()).
+ */
+ virtual void activate(MessageStorePlugin &store);
+
+ /**
+ * @name Methods inherited from qpid::broker::MessageStore
+ */
+ //@{
+ /**
+ * If called after init() but before recovery, will discard the database
+ * and reinitialize using an empty store dir. If @a pushDownStoreFiles
+ * is true, the content of the store dir will be moved to a backup dir
+ * inside the store dir. This is used when cluster nodes recover and must
+ * get their content from a cluster sync rather than directly from the
+ * store.
+ *
+ * @param pushDownStoreFiles If true, will move content of the store dir
+ * into a subdir, leaving the store dir
+ * otherwise empty.
+ */
+ virtual void truncateInit(const bool pushDownStoreFiles = false);
+
+ /**
+ * Record the existence of a durable queue
+ */
+ virtual void create(PersistableQueue& queue,
+ const qpid::framing::FieldTable& args);
+ /**
+ * Destroy a durable queue
+ */
+ virtual void destroy(PersistableQueue& queue);
+
+ /**
+ * Record the existence of a durable exchange
+ */
+ virtual void create(const PersistableExchange& exchange,
+ const qpid::framing::FieldTable& args);
+ /**
+ * Destroy a durable exchange
+ */
+ virtual void destroy(const PersistableExchange& exchange);
+
+ /**
+ * Record a binding
+ */
+ virtual void bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ /**
+ * Forget a binding
+ */
+ virtual void unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ /**
+ * Record generic durable configuration
+ */
+ virtual void create(const PersistableConfig& config);
+
+ /**
+ * Destroy generic durable configuration
+ */
+ virtual void destroy(const PersistableConfig& config);
+
+ /**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+ virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg);
+
+ /**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+ virtual void destroy(PersistableMessage& msg);
+
+ /**
+ * Appends content to a previously staged message
+ */
+ virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data);
+
+ /**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+ virtual void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ /**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+
+ /**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to dequeue
+ * @param queue the name of the queue from which it is to be dequeued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+
+ /**
+ * Flushes all async messages to disk for the specified queue
+ *
+ * Note: this is a no-op for this provider.
+ *
+ * @param queue the name of the queue from which it is to be dequeued
+ */
+ virtual void flush(const PersistableQueue& queue) {};
+
+ /**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk
+ *
+ * @param queue the name of the queue to check for outstanding AIO
+ */
+ virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue)
+ {return 0;}
+ //@}
+
+ /**
+ * @name Methods inherited from qpid::broker::TransactionalStore
+ */
+ //@{
+ virtual std::auto_ptr<qpid::broker::TransactionContext> begin();
+ virtual std::auto_ptr<qpid::broker::TPCTransactionContext> begin(const std::string& xid);
+ virtual void prepare(qpid::broker::TPCTransactionContext& txn);
+ virtual void commit(qpid::broker::TransactionContext& txn);
+ virtual void abort(qpid::broker::TransactionContext& txn);
+ virtual void collectPreparedXids(std::set<std::string>& xids);
+ //@}
+
+ virtual void recoverConfigs(qpid::broker::RecoveryManager& recoverer);
+ virtual void recoverExchanges(qpid::broker::RecoveryManager& recoverer,
+ ExchangeMap& exchangeMap);
+ virtual void recoverQueues(qpid::broker::RecoveryManager& recoverer,
+ QueueMap& queueMap);
+ virtual void recoverBindings(qpid::broker::RecoveryManager& recoverer,
+ const ExchangeMap& exchangeMap,
+ const QueueMap& queueMap);
+ virtual void recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap);
+ virtual void recoverTransactions(qpid::broker::RecoveryManager& recoverer,
+ PreparedTransactionMap& dtxMap);
+
+private:
+ struct ProviderOptions : public qpid::Options
+ {
+ std::string connectString;
+ std::string catalogName;
+ std::string storeDir;
+ size_t containerSize;
+ unsigned short initialContainers;
+ uint32_t maxWriteBuffers;
+
+ ProviderOptions(const std::string &name)
+ : qpid::Options(name),
+ catalogName("QpidStore"),
+ containerSize(1024 * 1024),
+ initialContainers(2),
+ maxWriteBuffers(10)
+ {
+ const enum { NAMELEN = MAX_COMPUTERNAME_LENGTH + 1 };
+ TCHAR myName[NAMELEN];
+ DWORD myNameLen = NAMELEN;
+ GetComputerName(myName, &myNameLen);
+ connectString = "Data Source=";
+ connectString += myName;
+ connectString += "\\SQLEXPRESS;Integrated Security=SSPI";
+ addOptions()
+ ("connect",
+ qpid::optValue(connectString, "STRING"),
+ "Connection string for the database to use. Will prepend "
+ "Provider=SQLOLEDB;")
+ ("catalog",
+ qpid::optValue(catalogName, "DB NAME"),
+ "Catalog (database) name")
+ ("store-dir",
+ qpid::optValue(storeDir, "DIR"),
+ "Location to store message and transaction data "
+ "(default uses data-dir if available)")
+ ("container-size",
+ qpid::optValue(containerSize, "VALUE"),
+ "Bytes per container; min 512K. Only used when creating "
+ "a new log")
+ ("initial-containers",
+ qpid::optValue(initialContainers, "VALUE"),
+ "Number of containers to add if creating a new log")
+ ("max-write-buffers",
+ qpid::optValue(maxWriteBuffers, "VALUE"),
+ "Maximum write buffers outstanding before log is flushed "
+ "(0 means no limit)")
+ ;
+ }
+ };
+ ProviderOptions options;
+ std::string brokerDataDir;
+ Messages messages;
+ // TransactionLog requires itself to have a shared_ptr reference to start.
+ TransactionLog::shared_ptr transactions;
+
+ // Each thread has a separate connection to the database and also needs
+ // to manage its COM initialize/finalize individually. This is done by
+ // keeping a thread-specific State.
+ boost::thread_specific_ptr<State> dbState;
+
+ State *initState();
+ DatabaseConnection *initConnection(void);
+ void createDb(DatabaseConnection *db, const std::string &name);
+ void createLogs();
+};
+
+static MSSqlClfsProvider static_instance_registers_plugin;
+
+void
+MSSqlClfsProvider::finalizeMe()
+{
+ dbState.reset();
+}
+
+MSSqlClfsProvider::MSSqlClfsProvider()
+ : options("MS SQL/CLFS Provider options")
+{
+ transactions = boost::make_shared<TransactionLog>();
+}
+
+MSSqlClfsProvider::~MSSqlClfsProvider()
+{
+}
+
+void
+MSSqlClfsProvider::earlyInitialize(Plugin::Target &target)
+{
+ MessageStorePlugin *store = dynamic_cast<MessageStorePlugin *>(&target);
+ if (store) {
+ // Check the store dir option; if not specified, need to
+ // grab the broker's data dir.
+ if (options.storeDir.empty()) {
+ DataDir& dir = store->getBroker()->getDataDir();
+ if (dir.isEnabled()) {
+ options.storeDir = dir.getPath();
+ }
+ else {
+ QPID_LOG(error,
+ "MSSQL-CLFS: --store-dir required if --no-data-dir specified");
+ return;
+ }
+ }
+
+ // If CLFS is not available on this system, give up now.
+ try {
+ Log::TuningParameters params;
+ params.containerSize = options.containerSize;
+ params.containers = options.initialContainers;
+ params.shrinkPct = 50;
+ params.maxWriteBuffers = options.maxWriteBuffers;
+ std::string msgPath = options.storeDir + "\\" + "messages";
+ messages.openLog(msgPath, params);
+ std::string transPath = options.storeDir + "\\" + "transactions";
+ transactions->open(transPath, params);
+ }
+ catch (std::exception &e) {
+ QPID_LOG(error, e.what());
+ return;
+ }
+
+ // If the database init fails, report it and don't register; give
+ // the rest of the broker a chance to run.
+ //
+ // Don't try to initConnection() since that will fail if the
+ // database doesn't exist. Instead, try to open a connection without
+ // a database name, then search for the database. There's still a
+ // chance this provider won't be selected for the store too, so be
+ // be sure to close the database connection before return to avoid
+ // leaving a connection up that will not be used.
+ try {
+ initState(); // This initializes COM
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection());
+ db->open(options.connectString, "");
+ _ConnectionPtr conn(*db);
+ _RecordsetPtr pCatalogs = NULL;
+ VariantHelper<std::string> catalogName(options.catalogName);
+ pCatalogs = conn->OpenSchema(adSchemaCatalogs, catalogName);
+ if (pCatalogs->EndOfFile) {
+ // Database doesn't exist; create it
+ QPID_LOG(notice,
+ "MSSQL-CLFS: Creating database " + options.catalogName);
+ createDb(db.get(), options.catalogName);
+ }
+ else {
+ QPID_LOG(notice,
+ "MSSQL-CLFS: Database located: " + options.catalogName);
+ }
+ if (pCatalogs) {
+ if (pCatalogs->State == adStateOpen)
+ pCatalogs->Close();
+ pCatalogs = 0;
+ }
+ db->close();
+ store->providerAvailable("MSSQL-CLFS", this);
+ }
+ catch (qpid::Exception &e) {
+ QPID_LOG(error, e.what());
+ return;
+ }
+ store->addFinalizer(boost::bind(&MSSqlClfsProvider::finalizeMe, this));
+ }
+}
+
+void
+MSSqlClfsProvider::initialize(Plugin::Target& target)
+{
+}
+
+void
+MSSqlClfsProvider::activate(MessageStorePlugin &store)
+{
+ QPID_LOG(info, "MS SQL/CLFS Provider is up");
+}
+
+void
+MSSqlClfsProvider::truncateInit(const bool pushDownStoreFiles)
+{
+}
+
+void
+MSSqlClfsProvider::create(PersistableQueue& queue,
+ const qpid::framing::FieldTable& /*args needed for jrnl*/)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ try {
+ db->beginTransaction();
+ rsQueues.open(db, TblQueue);
+ rsQueues.add(queue);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating queue " + queue.getName(), e, errs);
+ }
+ catch(std::exception& e) {
+ db->rollbackTransaction();
+ THROW_STORE_EXCEPTION(e.what());
+ }
+}
+
+/**
+ * Destroy a durable queue
+ */
+void
+MSSqlClfsProvider::destroy(PersistableQueue& queue)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsQueues.open(db, TblQueue);
+ rsBindings.open(db, TblBinding);
+ // Remove bindings first; the queue IDs can't be ripped out from
+ // under the references in the bindings table.
+ rsBindings.removeForQueue(queue.getPersistenceId());
+ rsQueues.remove(queue);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting queue " + queue.getName(), e, errs);
+ }
+
+ /*
+ * Now that the SQL stuff has recorded the queue deletion, expunge
+ * all record of the queue from the messages set. Any errors logging
+ * these removals are swallowed because during a recovery the queue
+ * Id won't be present (the SQL stuff already committed) so any references
+ * to it in message operations will be removed.
+ */
+ messages.expunge(queue.getPersistenceId());
+}
+
+/**
+ * Record the existence of a durable exchange
+ */
+void
+MSSqlClfsProvider::create(const PersistableExchange& exchange,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsExchanges;
+ try {
+ db->beginTransaction();
+ rsExchanges.open(db, TblExchange);
+ rsExchanges.add(exchange);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating exchange " + exchange.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Destroy a durable exchange
+ */
+void
+MSSqlClfsProvider::destroy(const PersistableExchange& exchange)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsExchanges;
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsExchanges.open(db, TblExchange);
+ rsBindings.open(db, TblBinding);
+ // Remove bindings first; the exchange IDs can't be ripped out from
+ // under the references in the bindings table.
+ rsBindings.removeForExchange(exchange.getPersistenceId());
+ rsExchanges.remove(exchange);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting exchange " + exchange.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Record a binding
+ */
+void
+MSSqlClfsProvider::bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsBindings.open(db, TblBinding);
+ rsBindings.add(exchange.getPersistenceId(),
+ queue.getPersistenceId(),
+ key,
+ args);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error binding exchange " + exchange.getName() +
+ " to queue " + queue.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Forget a binding
+ */
+void
+MSSqlClfsProvider::unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsBindings.open(db, TblBinding);
+ rsBindings.remove(exchange.getPersistenceId(),
+ queue.getPersistenceId(),
+ key,
+ args);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error unbinding exchange " + exchange.getName() +
+ " from queue " + queue.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Record generic durable configuration
+ */
+void
+MSSqlClfsProvider::create(const PersistableConfig& config)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsConfigs;
+ try {
+ db->beginTransaction();
+ rsConfigs.open(db, TblConfig);
+ rsConfigs.add(config);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating config " + config.getName(), e, errs);
+ }
+}
+
+/**
+ * Destroy generic durable configuration
+ */
+void
+MSSqlClfsProvider::destroy(const PersistableConfig& config)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsConfigs;
+ try {
+ db->beginTransaction();
+ rsConfigs.open(db, TblConfig);
+ rsConfigs.remove(config);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting config " + config.getName(), e, errs);
+ }
+}
+
+/**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+void
+MSSqlClfsProvider::stage(const boost::intrusive_ptr<PersistableMessage>& msg)
+{
+#if 0
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.add(msg);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error staging message", e, errs);
+ }
+#endif
+}
+
+/**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+void
+MSSqlClfsProvider::destroy(PersistableMessage& msg)
+{
+#if 0
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.remove(msg);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting message", e, errs);
+ }
+#endif
+}
+
+/**
+ * Appends content to a previously staged message
+ */
+void
+MSSqlClfsProvider::appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data)
+{
+#if 0
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.append(msg, data);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error appending to message", e, errs);
+ }
+#endif
+}
+
+/**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+void
+MSSqlClfsProvider::loadContent(const qpid::broker::PersistableQueue& /*queue*/,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+ // Message log keeps all messages in one log, so we don't need the
+ // queue reference.
+ messages.loadContent(msg->getPersistenceId(), data, offset, length);
+}
+
+/**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * @param ctxt The transaction context under which this enqueue happens.
+ * @param msg The message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ */
+void
+MSSqlClfsProvider::enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ Transaction::shared_ptr t;
+ TransactionContext *ctx = dynamic_cast<TransactionContext*>(ctxt);
+ if (ctx)
+ t = ctx->getTransaction();
+ else {
+ TPCTransactionContext *tctx;
+ tctx = dynamic_cast<TPCTransactionContext*>(ctxt);
+ if (tctx)
+ t = tctx->getTransaction();
+ }
+ uint64_t msgId = msg->getPersistenceId();
+ if (msgId == 0) {
+ messages.add(msg);
+ msgId = msg->getPersistenceId();
+ }
+ messages.enqueue(msgId, queue.getPersistenceId(), t);
+ msg->enqueueComplete();
+}
+
+/**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * @param ctxt The transaction context under which this dequeue happens.
+ * @param msg The message to dequeue
+ * @param queue The queue from which it is to be dequeued
+ */
+void
+MSSqlClfsProvider::dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ Transaction::shared_ptr t;
+ TransactionContext *ctx = dynamic_cast<TransactionContext*>(ctxt);
+ if (ctx)
+ t = ctx->getTransaction();
+ else {
+ TPCTransactionContext *tctx;
+ tctx = dynamic_cast<TPCTransactionContext*>(ctxt);
+ if (tctx)
+ t = tctx->getTransaction();
+ }
+ messages.dequeue(msg->getPersistenceId(), queue.getPersistenceId(), t);
+ msg->dequeueComplete();
+}
+
+std::auto_ptr<qpid::broker::TransactionContext>
+MSSqlClfsProvider::begin()
+{
+ Transaction::shared_ptr t = transactions->begin();
+ std::auto_ptr<qpid::broker::TransactionContext> tc(new TransactionContext(t));
+ return tc;
+}
+
+std::auto_ptr<qpid::broker::TPCTransactionContext>
+MSSqlClfsProvider::begin(const std::string& xid)
+{
+ TPCTransaction::shared_ptr t = transactions->begin(xid);
+ std::auto_ptr<qpid::broker::TPCTransactionContext> tc(new TPCTransactionContext(t));
+ return tc;
+}
+
+void
+MSSqlClfsProvider::prepare(qpid::broker::TPCTransactionContext& txn)
+{
+ TPCTransactionContext *ctx = dynamic_cast<TPCTransactionContext*> (&txn);
+ if (ctx == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ ctx->getTransaction()->prepare();
+}
+
+void
+MSSqlClfsProvider::commit(qpid::broker::TransactionContext& txn)
+{
+ Transaction::shared_ptr t;
+ TransactionContext *ctx = dynamic_cast<TransactionContext*>(&txn);
+ if (ctx)
+ t = ctx->getTransaction();
+ else {
+ TPCTransactionContext *tctx;
+ tctx = dynamic_cast<TPCTransactionContext*>(&txn);
+ if (tctx == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ t = tctx->getTransaction();
+ }
+ t->commit(messages);
+}
+
+void
+MSSqlClfsProvider::abort(qpid::broker::TransactionContext& txn)
+{
+ Transaction::shared_ptr t;
+ TransactionContext *ctx = dynamic_cast<TransactionContext*>(&txn);
+ if (ctx)
+ t = ctx->getTransaction();
+ else {
+ TPCTransactionContext *tctx;
+ tctx = dynamic_cast<TPCTransactionContext*>(&txn);
+ if (tctx == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ t = tctx->getTransaction();
+ }
+ t->abort(messages);
+}
+
+void
+MSSqlClfsProvider::collectPreparedXids(std::set<std::string>& xids)
+{
+ std::map<std::string, TPCTransaction::shared_ptr> preparedMap;
+ transactions->collectPreparedXids(preparedMap);
+ std::map<std::string, TPCTransaction::shared_ptr>::const_iterator i;
+ for (i = preparedMap.begin(); i != preparedMap.end(); ++i) {
+ xids.insert(i->first);
+ }
+}
+
+// @TODO Much of this recovery code is way too similar... refactor to
+// a recover template method on BlobRecordset.
+
+void
+MSSqlClfsProvider::recoverConfigs(qpid::broker::RecoveryManager& recoverer)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsConfigs;
+ rsConfigs.open(db, TblConfig);
+ _RecordsetPtr p = (_RecordsetPtr)rsConfigs;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Config instance and reset its ID.
+ broker::RecoverableConfig::shared_ptr config =
+ recoverer.recoverConfig(blob);
+ config->setPersistenceId(id);
+ p->MoveNext();
+ }
+}
+
+void
+MSSqlClfsProvider::recoverExchanges(qpid::broker::RecoveryManager& recoverer,
+ ExchangeMap& exchangeMap)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsExchanges;
+ rsExchanges.open(db, TblExchange);
+ _RecordsetPtr p = (_RecordsetPtr)rsExchanges;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Exchange instance, reset its ID, and remember the
+ // ones restored for matching up when recovering bindings.
+ broker::RecoverableExchange::shared_ptr exchange =
+ recoverer.recoverExchange(blob);
+ exchange->setPersistenceId(id);
+ exchangeMap[id] = exchange;
+ p->MoveNext();
+ }
+}
+
+void
+MSSqlClfsProvider::recoverQueues(qpid::broker::RecoveryManager& recoverer,
+ QueueMap& queueMap)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ rsQueues.open(db, TblQueue);
+ _RecordsetPtr p = (_RecordsetPtr)rsQueues;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Queue instance and reset its ID.
+ broker::RecoverableQueue::shared_ptr queue =
+ recoverer.recoverQueue(blob);
+ queue->setPersistenceId(id);
+ queueMap[id] = queue;
+ p->MoveNext();
+ }
+}
+
+void
+MSSqlClfsProvider::recoverBindings(qpid::broker::RecoveryManager& recoverer,
+ const ExchangeMap& exchangeMap,
+ const QueueMap& queueMap)
+{
+ DatabaseConnection *db = initConnection();
+ BindingRecordset rsBindings;
+ rsBindings.open(db, TblBinding);
+ rsBindings.recover(recoverer, exchangeMap, queueMap);
+}
+
+void
+MSSqlClfsProvider::recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap)
+{
+ // Read the list of valid queue Ids to ensure that no broken msg->queue
+ // refs get restored.
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ rsQueues.open(db, TblQueue);
+ _RecordsetPtr p = (_RecordsetPtr)rsQueues;
+ std::set<uint64_t> validQueues;
+ if (!(p->BOF && p->EndOfFile)) {
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ validQueues.insert(id);
+ p->MoveNext();
+ }
+ }
+ std::map<uint64_t, Transaction::shared_ptr> transMap;
+ transactions->recover(transMap);
+ messages.recover(recoverer,
+ validQueues,
+ transMap,
+ messageMap,
+ messageQueueMap);
+}
+
+void
+MSSqlClfsProvider::recoverTransactions(qpid::broker::RecoveryManager& recoverer,
+ PreparedTransactionMap& dtxMap)
+{
+ std::map<std::string, TPCTransaction::shared_ptr> preparedMap;
+ transactions->collectPreparedXids(preparedMap);
+ std::map<std::string, TPCTransaction::shared_ptr>::const_iterator i;
+ for (i = preparedMap.begin(); i != preparedMap.end(); ++i) {
+ std::auto_ptr<TPCTransactionContext> ctx(new TPCTransactionContext(i->second));
+ std::auto_ptr<qpid::broker::TPCTransactionContext> brokerCtx(ctx);
+ dtxMap[i->first] = recoverer.recoverTransaction(i->first, brokerCtx);
+ }
+}
+
+////////////// Internal Methods
+
+State *
+MSSqlClfsProvider::initState()
+{
+ State *state = dbState.get(); // See if thread has initialized
+ if (!state) {
+ state = new State;
+ dbState.reset(state);
+ }
+ return state;
+}
+
+DatabaseConnection *
+MSSqlClfsProvider::initConnection(void)
+{
+ State *state = initState();
+ if (state->dbConn != 0)
+ return state->dbConn; // And the DatabaseConnection is set up too
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ state->dbConn = db.release();
+ return state->dbConn;
+}
+
+void
+MSSqlClfsProvider::createDb(DatabaseConnection *db, const std::string &name)
+{
+ const std::string dbCmd = "CREATE DATABASE " + name;
+ const std::string useCmd = "USE " + name;
+ const std::string tableCmd = "CREATE TABLE ";
+ const std::string colSpecs =
+ " (persistenceId bigint PRIMARY KEY NOT NULL IDENTITY(1,1),"
+ " fieldTableBlob varbinary(MAX) NOT NULL)";
+ const std::string bindingSpecs =
+ " (exchangeId bigint REFERENCES tblExchange(persistenceId) NOT NULL,"
+ " queueId bigint REFERENCES tblQueue(persistenceId) NOT NULL,"
+ " routingKey varchar(255),"
+ " fieldTableBlob varbinary(MAX))";
+
+ _variant_t unused;
+ _bstr_t dbStr = dbCmd.c_str();
+ _ConnectionPtr conn(*db);
+ try {
+ conn->Execute(dbStr, &unused, adExecuteNoRecords);
+ _bstr_t useStr = useCmd.c_str();
+ conn->Execute(useStr, &unused, adExecuteNoRecords);
+ std::string makeTable = tableCmd + TblQueue + colSpecs;
+ _bstr_t makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblExchange + colSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblConfig + colSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblBinding + bindingSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ }
+ catch(_com_error &e) {
+ throw ADOException("MSSQL can't create " + name, e, db->getErrors());
+ }
+}
+
+void
+MSSqlClfsProvider::dump()
+{
+ // dump all db records to qpid_log
+ QPID_LOG(notice, "DB Dump: (not dumping anything)");
+ // rsQueues.dump();
+}
+
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp
new file mode 100644
index 0000000000..14d63a4cd4
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp
@@ -0,0 +1,406 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <windows.h>
+#include <clfsw32.h>
+#include <exception>
+#include <malloc.h>
+#include <memory.h>
+#include <qpid/framing/Buffer.h>
+#include <qpid/log/Statement.h>
+#include <qpid/sys/IntegerTypes.h>
+#include <qpid/sys/windows/check.h>
+
+#include "MessageLog.h"
+#include "Lsn.h"
+
+namespace {
+
+// Structures that hold log records. Each has a type field at the start.
+enum MessageEntryType {
+ MessageStartEntry = 1,
+ MessageChunkEntry = 2,
+ MessageDeleteEntry = 3,
+ MessageEnqueueEntry = 4,
+ MessageDequeueEntry = 5
+};
+static const uint32_t MaxMessageContentLength = 64 * 1024;
+
+// Message-Start
+struct MessageStart {
+ MessageEntryType type;
+ // If the complete message encoding doesn't fit, remainder is in
+ // MessageChunk records to follow.
+ // headerLength is the size of the message's header in content. It is
+ // part of the totalLength and the segmentLength.
+ uint32_t headerLength;
+ uint32_t totalLength;
+ uint32_t segmentLength;
+ char content[MaxMessageContentLength];
+
+ MessageStart()
+ : type(MessageStartEntry),
+ headerLength(0),
+ totalLength(0),
+ segmentLength(0) {}
+};
+// Message-Chunk
+struct MessageChunk {
+ MessageEntryType type;
+ uint32_t segmentLength;
+ char content[MaxMessageContentLength];
+
+ MessageChunk() : type(MessageChunkEntry), segmentLength(0) {}
+};
+// Message-Delete
+struct MessageDelete {
+ MessageEntryType type;
+
+ MessageDelete() : type(MessageDeleteEntry) {}
+};
+// Message-Enqueue
+struct MessageEnqueue {
+ MessageEntryType type;
+ uint64_t queueId;
+ uint64_t transId;
+
+ MessageEnqueue(uint64_t qId = 0, uint64_t tId = 0)
+ : type(MessageEnqueueEntry), queueId(qId), transId(tId) {}
+};
+// Message-Dequeue
+struct MessageDequeue {
+ MessageEntryType type;
+ uint64_t queueId;
+ uint64_t transId;
+
+ MessageDequeue(uint64_t qId = 0, uint64_t tId = 0)
+ : type(MessageDequeueEntry), queueId(qId), transId(tId) {}
+};
+
+} // namespace
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+void
+MessageLog::initialize()
+{
+ // Write something to occupy the first record, preventing a real message
+ // from being lsn/id 0. Delete of a non-existant id is easily tossed
+ // during recovery if no other messages have caused the tail to be moved
+ // up past this dummy record by then.
+ deleteMessage(0, 0);
+}
+
+uint32_t
+MessageLog::marshallingBufferSize()
+{
+ size_t biggestNeed = std::max(sizeof(MessageStart), sizeof(MessageEnqueue));
+ uint32_t defSize = static_cast<uint32_t>(biggestNeed);
+ uint32_t minSize = Log::marshallingBufferSize();
+ if (defSize <= minSize)
+ return minSize;
+ // Round up to multiple of minSize
+ return (defSize + minSize) / minSize * minSize;
+}
+
+uint64_t
+MessageLog::add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg)
+{
+ // The message may be too long to fit in one record; if so, write
+ // Message-Chunk records to contain the rest. If it does all fit in one
+ // record, though, optimize the encoding by going straight to the
+ // Message-Start record rather than encoding then copying to the record.
+ // In all case
+ MessageStart entry;
+ uint32_t encodedMessageLength = msg->encodedSize();
+ entry.headerLength = msg->encodedHeaderSize();
+ entry.totalLength = encodedMessageLength;
+ CLFS_LSN location, lastChunkLsn;
+ std::auto_ptr<char> encodeStage;
+ char *encodeBuff = 0;
+ bool oneRecord = encodedMessageLength <= MaxMessageContentLength;
+ if (oneRecord) {
+ encodeBuff = entry.content;
+ entry.segmentLength = encodedMessageLength;
+ }
+ else {
+ encodeStage.reset(new char[encodedMessageLength]);
+ encodeBuff = encodeStage.get();
+ entry.segmentLength = MaxMessageContentLength;
+ }
+ qpid::framing::Buffer buff(encodeBuff, encodedMessageLength);
+ msg->encode(buff);
+ if (!oneRecord)
+ memcpy_s(entry.content, sizeof(entry.content),
+ encodeBuff, entry.segmentLength);
+ uint32_t entryLength = static_cast<uint32_t>(sizeof(entry));
+ entryLength -= (MaxMessageContentLength - entry.segmentLength);
+ location = write(&entry, entryLength);
+ // Write any Message-Chunk records before setting the message's id.
+ uint32_t sent = entry.segmentLength;
+ uint32_t remaining = encodedMessageLength - entry.segmentLength;
+ while (remaining > 0) {
+ MessageChunk chunk;
+ chunk.segmentLength = std::max(MaxMessageContentLength, remaining);
+ memcpy_s(chunk.content, sizeof(chunk.content),
+ encodeStage.get() + sent, chunk.segmentLength);
+ entryLength = static_cast<uint32_t>(sizeof(chunk));
+ entryLength -= (MaxMessageContentLength - chunk.segmentLength);
+ lastChunkLsn = write(&chunk, entryLength, &location);
+ sent += chunk.segmentLength;
+ remaining -= chunk.segmentLength;
+ }
+ return lsnToId(location);
+}
+
+void
+MessageLog::deleteMessage(uint64_t messageId, uint64_t newFirstId)
+{
+ MessageDelete deleteEntry;
+ CLFS_LSN msgLsn = idToLsn(messageId);
+ write(&deleteEntry, sizeof(deleteEntry), &msgLsn);
+ if (newFirstId != 0)
+ moveTail(idToLsn(newFirstId));
+}
+
+// Load part or all of a message's content from previously stored
+// log record(s).
+void
+MessageLog::loadContent(uint64_t messageId,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+}
+
+void
+MessageLog::recordEnqueue (uint64_t messageId,
+ uint64_t queueId,
+ uint64_t transactionId)
+{
+ MessageEnqueue entry(queueId, transactionId);
+ CLFS_LSN msgLsn = idToLsn(messageId);
+ write(&entry, sizeof(entry), &msgLsn);
+}
+
+void
+MessageLog::recordDequeue (uint64_t messageId,
+ uint64_t queueId,
+ uint64_t transactionId)
+{
+ MessageDequeue entry(queueId, transactionId);
+ CLFS_LSN msgLsn = idToLsn(messageId);
+ write(&entry, sizeof(entry), &msgLsn);
+}
+
+void
+MessageLog::recover(qpid::broker::RecoveryManager& recoverer,
+ qpid::store::MessageMap& messageMap,
+ std::map<uint64_t, std::vector<RecoveredMsgOp> >& messageOps)
+{
+ // If context and content needs to be saved while reassembling messages
+ // split across log records, save the info and reassembly buffer.
+ struct MessageBlocks {
+ uint32_t totalLength;
+ uint32_t soFarLength;
+ boost::shared_ptr<char> content;
+
+ MessageBlocks() : totalLength(0), soFarLength(0), content((char*)0) {}
+ };
+ std::map<uint64_t, MessageBlocks> reassemblies;
+ std::map<uint64_t, MessageBlocks>::iterator at;
+
+ QPID_LOG(debug, "Recovering message log");
+
+ // Note that there may be message refs in the log which are deleted, so
+ // be sure to only add msgs at message-start record, and ignore those
+ // that don't have an existing message record.
+ // Get the base LSN - that's how to say "start reading at the beginning"
+ CLFS_INFORMATION info;
+ ULONG infoLength = sizeof (info);
+ BOOL ok = ::GetLogFileInformation(handle, &info, &infoLength);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ // Pointers for the various record types that can be assigned in the
+ // reading loop below.
+ MessageStart *start;
+ MessageChunk *chunk;
+ MessageEnqueue *enqueue;
+ MessageDequeue *dequeue;
+
+ qpid::store::MessageMap::iterator messageMapSpot;
+ qpid::store::MessageQueueMap::iterator queueMapSpot;
+ PVOID recordPointer;
+ ULONG recordLength;
+ CLFS_RECORD_TYPE recordType = ClfsDataRecord;
+ CLFS_LSN messageLsn, current, undoNext;
+ PVOID readContext;
+ uint64_t msgId;
+ // Note 'current' in case it's needed below; ReadNextLogRecord returns it
+ // via a parameter.
+ current = info.BaseLsn;
+ ok = ::ReadLogRecord(marshal,
+ &info.BaseLsn,
+ ClfsContextForward,
+ &recordPointer,
+ &recordLength,
+ &recordType,
+ &undoNext,
+ &messageLsn,
+ &readContext,
+ 0);
+ while (ok) {
+ // All the record types this class writes have a MessageEntryType in the
+ // beginning. Based on that, do what's needed.
+ MessageEntryType *t =
+ reinterpret_cast<MessageEntryType *>(recordPointer);
+ switch(*t) {
+ case MessageStartEntry:
+ start = reinterpret_cast<MessageStart *>(recordPointer);
+ msgId = lsnToId(current);
+ QPID_LOG(debug, "Message Start, id " << msgId);
+ // If the message content is split across multiple log records, save
+ // this content off to the side until the remaining record(s) are
+ // located.
+ if (start->totalLength == start->segmentLength) { // Whole thing
+ // Start by recovering the header then see if the rest of
+ // the content is desired.
+ qpid::framing::Buffer buff(start->content, start->headerLength);
+ qpid::broker::RecoverableMessage::shared_ptr m =
+ recoverer.recoverMessage(buff);
+ m->setPersistenceId(msgId);
+ messageMap[msgId] = m;
+ uint32_t contentLength =
+ start->totalLength - start->headerLength;
+ if (m->loadContent(contentLength)) {
+ qpid::framing::Buffer content(&(start->content[start->headerLength]),
+ contentLength);
+ m->decodeContent(content);
+ }
+ }
+ else {
+ // Save it in a block big enough.
+ MessageBlocks b;
+ b.totalLength = start->totalLength;
+ b.soFarLength = start->segmentLength;
+ b.content.reset(new char[b.totalLength]);
+ memcpy_s(b.content.get(), b.totalLength,
+ start->content, start->segmentLength);
+ reassemblies[msgId] = b;
+ }
+ break;
+ case MessageChunkEntry:
+ chunk = reinterpret_cast<MessageChunk *>(recordPointer);
+ // Remember, all entries chained to MessageStart via previous.
+ msgId = lsnToId(messageLsn);
+ QPID_LOG(debug, "Message Chunk for id " << msgId);
+ at = reassemblies.find(msgId);
+ if (at == reassemblies.end()) {
+ QPID_LOG(debug, "Message frag for " << msgId <<
+ " but no start; discarded");
+ }
+ else {
+ MessageBlocks *b = &(at->second);
+ if (b->soFarLength + chunk->segmentLength > b->totalLength)
+ throw std::runtime_error("Invalid message chunk length");
+ memcpy_s(b->content.get() + b->soFarLength,
+ b->totalLength - b->soFarLength,
+ chunk->content,
+ chunk->segmentLength);
+ b->soFarLength += chunk->segmentLength;
+ if (b->totalLength == b->soFarLength) {
+ qpid::framing::Buffer buff(b->content.get(),
+ b->totalLength);
+ qpid::broker::RecoverableMessage::shared_ptr m =
+ recoverer.recoverMessage(buff);
+ m->setPersistenceId(msgId);
+ messageMap[msgId] = m;
+ reassemblies.erase(at);
+ }
+ }
+ break;
+ case MessageDeleteEntry:
+ msgId = lsnToId(messageLsn);
+ QPID_LOG(debug, "Message Delete, id " << msgId);
+ messageMap.erase(msgId);
+ messageOps.erase(msgId);
+ break;
+ case MessageEnqueueEntry:
+ enqueue = reinterpret_cast<MessageEnqueue *>(recordPointer);
+ msgId = lsnToId(messageLsn);
+ QPID_LOG(debug, "Message " << msgId << " Enqueue on queue " <<
+ enqueue->queueId << ", txn " << enqueue->transId);
+ if (messageMap.find(msgId) == messageMap.end()) {
+ QPID_LOG(debug,
+ "Message " << msgId << " doesn't exist; discarded");
+ }
+ else {
+ std::vector<RecoveredMsgOp>& ops = messageOps[msgId];
+ RecoveredMsgOp op(RECOVERED_ENQUEUE,
+ enqueue->queueId,
+ enqueue->transId);
+ ops.push_back(op);
+ }
+ break;
+ case MessageDequeueEntry:
+ dequeue = reinterpret_cast<MessageDequeue *>(recordPointer);
+ msgId = lsnToId(messageLsn);
+ QPID_LOG(debug, "Message " << msgId << " Dequeue from queue " <<
+ dequeue->queueId);
+ if (messageMap.find(msgId) == messageMap.end()) {
+ QPID_LOG(debug,
+ "Message " << msgId << " doesn't exist; discarded");
+ }
+ else {
+ std::vector<RecoveredMsgOp>& ops = messageOps[msgId];
+ RecoveredMsgOp op(RECOVERED_DEQUEUE,
+ dequeue->queueId,
+ dequeue->transId);
+ ops.push_back(op);
+ }
+ break;
+ default:
+ throw std::runtime_error("Bad message log entry type");
+ }
+
+ recordType = ClfsDataRecord;
+ ok = ::ReadNextLogRecord(readContext,
+ &recordPointer,
+ &recordLength,
+ &recordType,
+ 0, // No userLsn
+ &undoNext,
+ &messageLsn,
+ &current,
+ 0);
+ }
+ DWORD status = ::GetLastError();
+ ::TerminateReadLog(readContext);
+ if (status == ERROR_HANDLE_EOF) { // No more records
+ QPID_LOG(debug, "Message log recovered");
+ return;
+ }
+ throw QPID_WINDOWS_ERROR(status);
+}
+
+}}} // namespace qpid::store::ms_clfs
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.h b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.h
new file mode 100644
index 0000000000..b3705287a6
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.h
@@ -0,0 +1,107 @@
+#ifndef QPID_STORE_MSCLFS_MESSAGELOG_H
+#define QPID_STORE_MSCLFS_MESSAGELOG_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/intrusive_ptr.hpp>
+#include <qpid/broker/PersistableMessage.h>
+#include <qpid/broker/RecoveryManager.h>
+#include <qpid/sys/IntegerTypes.h>
+#include <qpid/store/StorageProvider.h>
+
+#include "Log.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+/**
+ * @class MessageLog
+ *
+ * Represents a CLFS-housed message log.
+ */
+class MessageLog : public Log {
+
+protected:
+ // Message log needs to have a no-op first record written in the log
+ // to ensure that no real message gets an ID 0.
+ virtual void initialize();
+
+public:
+ // Inherited and reimplemented from Log. Figure the minimum marshalling
+ // buffer size needed for the records this class writes.
+ virtual uint32_t marshallingBufferSize();
+
+ // Add the specified message to the log; Return the persistence Id.
+ uint64_t add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg);
+
+ // Write a Delete entry for messageId. If newFirstId is not 0, it is now
+ // the earliest valid message in the log, so move the tail up to it.
+ void deleteMessage(uint64_t messageId, uint64_t newFirstId);
+
+ // Load part or all of a message's content from previously stored
+ // log record(s).
+ void loadContent(uint64_t messageId,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ // Enqueue and dequeue operations track messages' transit across
+ // queues; each operation may be associated with a transaction. If
+ // the transactionId is 0 the operation is not associated with a
+ // transaction.
+ void recordEnqueue (uint64_t messageId,
+ uint64_t queueId,
+ uint64_t transactionId);
+ void recordDequeue (uint64_t messageId,
+ uint64_t queueId,
+ uint64_t transactionId);
+
+ // Recover the messages and their queueing records from the log.
+ // @param recoverer Recovery manager used to recreate broker objects from
+ // encoded framing buffers recovered from the log.
+ // @param messageMap This method fills in the map of id -> ptr of
+ // recovered messages.
+ // @param messageOps This method fills in the map of msg id ->
+ // vector of operations involving the message that were
+ // recovered from the log. It is the caller's
+ // responsibility to sort the operations out and
+ // ascertain which operations should be acted on. The
+ // order of operations in the vector is as they were
+ // read in order from the log.
+ typedef enum { RECOVERED_ENQUEUE = 1, RECOVERED_DEQUEUE } RecoveredOpType;
+ struct RecoveredMsgOp {
+ RecoveredOpType op;
+ uint64_t queueId;
+ uint64_t txnId;
+
+ RecoveredMsgOp(RecoveredOpType o, const uint64_t& q, const uint64_t& t)
+ : op(o), queueId(q), txnId(t) {}
+ };
+ void recover(qpid::broker::RecoveryManager& recoverer,
+ qpid::store::MessageMap& messageMap,
+ std::map<uint64_t, std::vector<RecoveredMsgOp> >& messageOps);
+};
+
+}}} // namespace qpid::store::ms_clfs
+
+#endif /* QPID_STORE_MSCLFS_MESSAGELOG_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Messages.cpp b/qpid/cpp/src/qpid/store/ms-clfs/Messages.cpp
new file mode 100644
index 0000000000..db5d2ebf4c
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Messages.cpp
@@ -0,0 +1,472 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/log/Statement.h>
+
+#include "Messages.h"
+#include "Lsn.h"
+#include "qpid/store/StoreException.h"
+#include <boost/foreach.hpp>
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+void
+Messages::openLog(const std::string& path, const Log::TuningParameters& params)
+{
+ log.open (path, params);
+}
+
+void
+Messages::add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg)
+{
+ uint64_t id = log.add(msg);
+ msg->setPersistenceId(id);
+ std::auto_ptr<MessageInfo> autom(new MessageInfo);
+ MessageInfo::shared_ptr m(autom);
+ std::pair<uint64_t, MessageInfo::shared_ptr> p(id, m);
+ {
+ qpid::sys::ScopedWlock<qpid::sys::RWlock> l(lock);
+ messages.insert(p);
+ // If there's only this one message there, move the tail to it.
+ // This prevents the log from continually growing when messages
+ // are added and removed one at a time.
+ if (messages.size() == 1) {
+ CLFS_LSN newTail = idToLsn(id);
+ log.moveTail(newTail);
+ }
+ }
+}
+
+void
+Messages::enqueue(uint64_t msgId, uint64_t queueId, Transaction::shared_ptr& t)
+{
+ MessageInfo::shared_ptr p;
+ {
+ qpid::sys::ScopedRlock<qpid::sys::RWlock> l(lock);
+ MessageMap::const_iterator i = messages.find(msgId);
+ if (i == messages.end())
+ THROW_STORE_EXCEPTION("Message does not exist");
+ p = i->second;
+ }
+ MessageInfo::Location loc(queueId, t, MessageInfo::TRANSACTION_ENQUEUE);
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(p->whereLock);
+ p->where.push_back(loc);
+ uint64_t transactionId = 0;
+ if (t.get() != 0) {
+ transactionId = t->getId();
+ t->enroll(msgId);
+ }
+ try {
+ log.recordEnqueue(msgId, queueId, transactionId);
+ }
+ catch (...) {
+ // Undo the record-keeping if the log wasn't written correctly.
+ if (transactionId != 0)
+ t->unenroll(msgId);
+ p->where.pop_back();
+ throw;
+ }
+ }
+}
+
+void
+Messages::dequeue(uint64_t msgId, uint64_t queueId, Transaction::shared_ptr& t)
+{
+ MessageInfo::shared_ptr p;
+ {
+ qpid::sys::ScopedRlock<qpid::sys::RWlock> l(lock);
+ MessageMap::const_iterator i = messages.find(msgId);
+ if (i == messages.end())
+ THROW_STORE_EXCEPTION("Message does not exist");
+ p = i->second;
+ }
+ {
+ // Locate the 'where' entry for the specified queue. Once this operation
+ // is recorded in the log, update the 'where' entry to reflect it.
+ // Note that an existing entry in 'where' that refers to a transaction
+ // is not eligible for this operation.
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(p->whereLock);
+ std::list<MessageInfo::Location>::iterator i;
+ for (i = p->where.begin(); i != p->where.end(); ++i) {
+ if (i->queueId == queueId && i->transaction.get() == 0)
+ break;
+ }
+ if (i == p->where.end())
+ THROW_STORE_EXCEPTION("Message not on queue");
+ uint64_t transactionId = 0;
+ if (t.get() != 0) {
+ transactionId = t->getId();
+ t->enroll(msgId);
+ }
+ try {
+ log.recordDequeue(msgId, queueId, transactionId);
+ }
+ catch (...) {
+ // Undo the record-keeping if the log wasn't written correctly.
+ if (transactionId != 0)
+ t->unenroll(msgId);
+ throw;
+ }
+ // Ok, logged successfully. If this is a transactional op, note
+ // the transaction. If non-transactional, remove the 'where' entry.
+ if (transactionId != 0) {
+ i->transaction = t;
+ i->disposition = MessageInfo::TRANSACTION_DEQUEUE;
+ }
+ else {
+ p->where.erase(i);
+ // If the message doesn't exist on any other queues, remove it.
+ if (p->where.empty())
+ remove(msgId);
+ }
+ }
+}
+
+// Commit a previous provisional enqueue or dequeue of a particular message
+// actions under a specified transaction. If this results in the message's
+// being removed from all queues, it is deleted.
+void
+Messages::commit(uint64_t msgId, Transaction::shared_ptr& t)
+{
+ MessageInfo::shared_ptr p;
+ {
+ qpid::sys::ScopedRlock<qpid::sys::RWlock> l(lock);
+ MessageMap::const_iterator i = messages.find(msgId);
+ if (i == messages.end())
+ THROW_STORE_EXCEPTION("Message does not exist");
+ p = i->second;
+ }
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(p->whereLock);
+ std::list<MessageInfo::Location>::iterator i;
+ for (i = p->where.begin(); i != p->where.end(); ++i) {
+ if (i->transaction != t)
+ continue;
+ // Transactional dequeues can now remove the item from the
+ // where list; enqueues just clear the transaction reference.
+ if (i->disposition == MessageInfo::TRANSACTION_DEQUEUE)
+ i = p->where.erase(i);
+ else
+ i->transaction.reset();
+ }
+ }
+ // If committing results in this message having no further enqueue
+ // references, delete it. If the delete fails, swallow the exception
+ // and let recovery take care of removing it later.
+ if (p->where.empty()) {
+ try {
+ remove(msgId);
+ }
+ catch(...) {}
+ }
+}
+
+// Abort a previous provisional enqueue or dequeue of a particular message
+// actions under a specified transaction. If this results in the message's
+// being removed from all queues, it is deleted.
+void
+Messages::abort(uint64_t msgId, Transaction::shared_ptr& t)
+{
+ MessageInfo::shared_ptr p;
+ {
+ qpid::sys::ScopedRlock<qpid::sys::RWlock> l(lock);
+ MessageMap::const_iterator i = messages.find(msgId);
+ if (i == messages.end())
+ THROW_STORE_EXCEPTION("Message does not exist");
+ p = i->second;
+ }
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(p->whereLock);
+ std::list<MessageInfo::Location>::iterator i = p->where.begin();
+ while (i != p->where.end()) {
+ if (i->transaction != t) {
+ ++i;
+ continue;
+ }
+ // Aborted transactional dequeues result in the message remaining
+ // enqueued like before the operation; enqueues clear the
+ // message from the where list - like the enqueue never happened.
+ if (i->disposition == MessageInfo::TRANSACTION_ENQUEUE)
+ i = p->where.erase(i);
+ else {
+ i->transaction.reset();
+ ++i;
+ }
+ }
+ }
+ // If aborting results in this message having no further enqueue
+ // references, delete it. If the delete fails, swallow the exception
+ // and let recovery take care of removing it later.
+ if (p->where.empty()) {
+ try {
+ remove(msgId);
+ }
+ catch(...) {}
+ }
+}
+
+// Load part or all of a message's content from previously stored
+// log record(s).
+void
+Messages::loadContent(uint64_t msgId,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+ log.loadContent(msgId, data, offset, length);
+}
+
+// Recover the current set of messages and where they're queued from
+// the log.
+void
+Messages::recover(qpid::broker::RecoveryManager& recoverer,
+ const std::set<uint64_t> &validQueues,
+ const std::map<uint64_t, Transaction::shared_ptr>& transMap,
+ qpid::store::MessageMap& messageMap,
+ qpid::store::MessageQueueMap& messageQueueMap)
+{
+ std::map<uint64_t, std::vector<MessageLog::RecoveredMsgOp> > messageOps;
+ log.recover(recoverer, messageMap, messageOps);
+ // Now read through the messageOps replaying the operations with the
+ // knowledge of which transactions committed, aborted, etc. A transaction
+ // should not be deleted until there are no messages referencing it so
+ // a message operation with a transaction id not found in transMap is
+ // a serious problem.
+ QPID_LOG(debug, "Beginning CLFS-recovered message operation replay");
+ // Keep track of any messages that are recovered from the log but don't
+ // have any place to be. This can happen, for example, if the broker
+ // crashes while logging a message deletion. After all the recovery is
+ // done, delete all the homeless messages.
+ std::vector<uint64_t> homeless;
+ std::map<uint64_t, std::vector<MessageLog::RecoveredMsgOp> >::const_iterator msg;
+ for (msg = messageOps.begin(); msg != messageOps.end(); ++msg) {
+ uint64_t msgId = msg->first;
+ const std::vector<MessageLog::RecoveredMsgOp>& ops = msg->second;
+ QPID_LOG(debug, "Message " << msgId << "; " << ops.size() << " op(s)");
+ MessageInfo::shared_ptr m(new MessageInfo);
+ std::vector<QueueEntry>& entries = messageQueueMap[msgId];
+ std::vector<MessageLog::RecoveredMsgOp>::const_iterator op;
+ for (op = ops.begin(); op != ops.end(); ++op) {
+ QueueEntry entry(op->queueId);
+ MessageInfo::Location loc(op->queueId);
+ std::string dir =
+ op->op == MessageLog::RECOVERED_ENQUEUE ? "enqueue"
+ : "dequeue";
+ if (validQueues.find(op->queueId) == validQueues.end()) {
+ QPID_LOG(info,
+ "Message " << msgId << dir << " on non-existant queue "
+ << op->queueId << "; dropped");
+ continue;
+ }
+ if (op->txnId != 0) {
+ // Be sure to enroll this message in the transaction even if
+ // it has committed or aborted. This ensures that the
+ // transaction isn't removed from the log while finalizing the
+ // recovery. If it were to be removed and the broker failed
+ // again before removing this message during normal operation,
+ // it couldn't be recovered again.
+ //
+ // Recall what is being reconstructed; 2 things:
+ // 1. This class's 'messages' list which keeps track
+ // of the queues each message is on and the transactions
+ // each message is enrolled in. For this, aborted
+ // transactions cause the result of the operation to be
+ // ignored, but the message does need to be enrolled in
+ // the transaction to properly maintain the transaction
+ // references until the message is deleted.
+ // 2. The StorageProvider's MessageQueueMap, which also
+ // has an entry for each queue each message is on and
+ // its TPL status and associated xid.
+ const Transaction::shared_ptr &t =
+ transMap.find(op->txnId)->second;
+ // Prepared transactions cause the operation to be
+ // provisionally acted on, and the message to be enrolled in
+ // the transaction for when it commits/aborts. This is
+ // noted in the QueueEntry for the StorageProvider's map.
+ if (t->getState() == Transaction::TRANS_PREPARED) {
+ QPID_LOG(debug, dir << " for queue " << op->queueId <<
+ ", prepared txn " << op->txnId);
+ TPCTransaction::shared_ptr tpct(boost::dynamic_pointer_cast<TPCTransaction>(t));
+ if (tpct.get() == 0)
+ THROW_STORE_EXCEPTION("Invalid transaction state");
+ t->enroll(msgId);
+ entry.xid = tpct->getXid();
+ loc.transaction = t;
+ if (op->op == MessageLog::RECOVERED_ENQUEUE) {
+ entry.tplStatus = QueueEntry::ADDING;
+ loc.disposition = MessageInfo::TRANSACTION_ENQUEUE;
+ }
+ else {
+ entry.tplStatus = QueueEntry::REMOVING;
+ loc.disposition = MessageInfo::TRANSACTION_DEQUEUE;
+ }
+ }
+ else if (t->getState() != Transaction::TRANS_COMMITTED) {
+ QPID_LOG(debug, dir << " for queue " << op->queueId <<
+ ", txn " << op->txnId << ", rolling back");
+ continue;
+ }
+ }
+ // Here for non-transactional and prepared transactional operations
+ // to set up the messageQueueMap entries. Note that at this point
+ // a committed transactional operation looks like a
+ // non-transactional one as far as the QueueEntry is
+ // concerned - just do it. If this is an entry enqueuing a
+ // message, just add it to the entries list. If it's a dequeue
+ // operation, locate the matching entry for the queue and delete
+ // it if the current op is non-transactional; if it's a prepared
+ // transaction then replace the existing entry with the current
+ // one that notes the message is enqueued but being removed under
+ // a prepared transaction.
+ QPID_LOG(debug, dir + " at queue " << entry.queueId);
+ if (op->op == MessageLog::RECOVERED_ENQUEUE) {
+ entries.push_back(entry);
+ m->where.push_back(loc);
+ }
+ else {
+ std::vector<QueueEntry>::iterator i = entries.begin();
+ while (i != entries.end()) {
+ if (i->queueId == entry.queueId) {
+ if (entry.tplStatus != QueueEntry::NONE)
+ *i = entry;
+ else
+ entries.erase(i);
+ break;
+ }
+ ++i;
+ }
+ std::list<MessageInfo::Location>::iterator w = m->where.begin();
+ while (w != m->where.end()) {
+ if (w->queueId == loc.queueId) {
+ if (loc.transaction.get() != 0) {
+ *w = loc;
+ ++w;
+ }
+ else {
+ w = m->where.erase(w);
+ }
+ }
+ }
+ }
+ }
+ // Now that all the queue entries have been set correctly, see if
+ // there are any entries; they may have all been removed during
+ // recovery. If there are none, add this message to the homeless
+ // list to be deleted from the log after the recovery is done.
+ if (m->where.size() == 0) {
+ homeless.push_back(msgId);
+ messageMap.erase(msgId);
+ messageQueueMap.erase(msgId);
+ }
+ else {
+ std::pair<uint64_t, MessageInfo::shared_ptr> p(msgId, m);
+ messages.insert(p);
+ }
+ }
+
+ QPID_LOG(debug, "Message log recovery done.");
+ // Done! Ok, go back and delete all the homeless messages.
+ BOOST_FOREACH(uint64_t msg, homeless) {
+ QPID_LOG(debug, "Deleting homeless message " << msg);
+ remove(msg);
+ }
+}
+
+// Expunge is called when a queue is deleted. All references to that
+// queue must be expunged from all messages. 'Dequeue' log records are
+// written for each queue entry removed, but any errors are swallowed.
+// On recovery there's a list of valid queues passed in. The deleted
+// queue will not be on that list so if any references to it are
+// recovered they'll get weeded out then.
+void
+Messages::expunge(uint64_t queueId)
+{
+ std::vector<uint64_t> toBeDeleted; // Messages to be deleted later.
+
+ {
+ // Lock everybody out since all messages are possibly in play.
+ // There also may be other threads already working on particular
+ // messages so individual message mutex still must be acquired.
+ qpid::sys::ScopedWlock<qpid::sys::RWlock> l(lock);
+ MessageMap::iterator m;
+ for (m = messages.begin(); m != messages.end(); ++m) {
+ MessageInfo::shared_ptr p = m->second;
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> ml(p->whereLock);
+ std::list<MessageInfo::Location>::iterator i = p->where.begin();
+ while (i != p->where.end()) {
+ if (i->queueId != queueId) {
+ ++i;
+ continue;
+ }
+ // If this entry is involved in a transaction, unenroll it.
+ // Then remove the entry.
+ if (i->transaction.get() != 0)
+ i->transaction->unenroll(m->first);
+ i = p->where.erase(i);
+ try {
+ log.recordDequeue(m->first, queueId, 0);
+ }
+ catch(...) {
+ }
+ }
+ if (p->where.size() == 0)
+ toBeDeleted.push_back(m->first);
+ }
+ }
+ }
+ // Swallow any exceptions during this; don't care. Recover it later
+ // if needed.
+ try {
+ BOOST_FOREACH(uint64_t msg, toBeDeleted)
+ remove(msg);
+ }
+ catch(...) {
+ }
+}
+
+// Remove a specified message from those controlled by this object.
+void
+Messages::remove(uint64_t messageId)
+{
+ uint64_t newFirstId = 0;
+ {
+ qpid::sys::ScopedWlock<qpid::sys::RWlock> l(lock);
+ messages.erase(messageId);
+ // May have deleted the first entry; if so the log can release that.
+ // If this message being deleted results in an empty list of
+ // messages, move the tail up to this message's LSN. This may
+ // result in one or more messages being stranded in the log
+ // until there's more activity. If a restart happens while these
+ // unneeded log records are there, the presence of the MessageDelete
+ // entry will cause the message(s) to be ignored anyway.
+ if (messages.empty())
+ newFirstId = messageId;
+ else if (messages.begin()->first > messageId)
+ newFirstId = messages.begin()->first;
+ }
+ log.deleteMessage(messageId, newFirstId);
+}
+
+}}} // namespace qpid::store::ms_clfs
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Messages.h b/qpid/cpp/src/qpid/store/ms-clfs/Messages.h
new file mode 100644
index 0000000000..93cc8bfe62
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Messages.h
@@ -0,0 +1,144 @@
+#ifndef QPID_STORE_MSCLFS_MESSAGES_H
+#define QPID_STORE_MSCLFS_MESSAGES_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <windows.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <boost/intrusive_ptr.hpp>
+#include <qpid/broker/PersistableMessage.h>
+#include <qpid/sys/Mutex.h>
+
+#include "MessageLog.h"
+#include "Transaction.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+class Messages {
+
+ struct MessageInfo {
+ // How many queues this message is on, whether actually (non-transacted)
+ // or provisionally (included in a non-yet-committed transaction).
+ volatile LONG enqueuedCount;
+
+ // Keep a list of transactional operations this message is
+ // referenced in. When the transaction changes/finalizes these all
+ // need to be acted on.
+ typedef enum { TRANSACTION_NONE = 0,
+ TRANSACTION_ENQUEUE,
+ TRANSACTION_DEQUEUE } TransType;
+#if 0
+ std::map<Transaction::shared_ptr, std::vector<TransType> > transOps;
+ qpid::sys::Mutex transOpsLock;
+#endif
+ // Think what I need is a list of "where is this message" - queue id,
+ // transaction ref, what kind of trans op (enq/deq). Then "remove all
+ // queue refs" can search through all messages looking for queue ids
+ // and undo them. Write "remove from queue" record to log. Also need to
+ // add "remove from queue" to recovery.
+ struct Location {
+ uint64_t queueId;
+ Transaction::shared_ptr transaction;
+ TransType disposition;
+
+ Location(uint64_t q)
+ : queueId(q), transaction(), disposition(TRANSACTION_NONE) {}
+ Location(uint64_t q, Transaction::shared_ptr& t, TransType d)
+ : queueId(q), transaction(t), disposition(d) {}
+ };
+ qpid::sys::Mutex whereLock;
+ std::list<Location> where;
+ // The transactions vector just keeps a shared_ptr to each
+ // Transaction this message was involved in, regardless of the
+ // disposition or transaction state. Keeping a valid shared_ptr
+ // prevents the Transaction from being deleted. As long as there
+ // are any messages that referred to a transaction, that
+ // transaction's state needs to be known so the message disposition
+ // can be correctly recovered if needed.
+ std::vector<Transaction::shared_ptr> transactions;
+
+ typedef boost::shared_ptr<MessageInfo> shared_ptr;
+
+ MessageInfo() : enqueuedCount(0) {}
+ };
+
+ qpid::sys::RWlock lock;
+ typedef std::map<uint64_t, MessageInfo::shared_ptr> MessageMap;
+ MessageMap messages;
+ MessageLog log;
+
+ // Remove a specified message from those controlled by this object.
+ void remove(uint64_t messageId);
+
+public:
+ void openLog(const std::string& path, const Log::TuningParameters& params);
+
+ // Add the specified message to the log and list of known messages.
+ // Upon successful return the message's persistenceId is set.
+ void add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg);
+
+ // Add the specified queue to the message's list of places it is
+ // enqueued.
+ void enqueue(uint64_t msgId, uint64_t queueId, Transaction::shared_ptr& t);
+
+ // Remove the specified queue from the message's list of places it is
+ // enqueued. If there are no other queues holding the message, it is
+ // deleted.
+ void dequeue(uint64_t msgId, uint64_t queueId, Transaction::shared_ptr& t);
+
+ // Commit a previous provisional enqueue or dequeue of a particular message
+ // actions under a specified transaction. If this results in the message's
+ // being removed from all queues, it is deleted.
+ void commit(uint64_t msgId, Transaction::shared_ptr& transaction);
+
+ // Abort a previous provisional enqueue or dequeue of a particular message
+ // actions under a specified transaction. If this results in the message's
+ // being removed from all queues, it is deleted.
+ void abort(uint64_t msgId, Transaction::shared_ptr& transaction);
+
+ // Load part or all of a message's content from previously stored
+ // log record(s).
+ void loadContent(uint64_t msgId,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ // Expunge is called when a queue is deleted. All references to that
+ // queue must be expunged from all messages.
+ void expunge(uint64_t queueId);
+
+ // Recover the current set of messages and where they're queued from
+ // the log.
+ void recover(qpid::broker::RecoveryManager& recoverer,
+ const std::set<uint64_t> &validQueues,
+ const std::map<uint64_t, Transaction::shared_ptr>& transMap,
+ qpid::store::MessageMap& messageMap,
+ qpid::store::MessageQueueMap& messageQueueMap);
+};
+
+}}} // namespace qpid::store::ms_clfs
+
+#endif /* QPID_STORE_MSCLFS_MESSAGES_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Transaction.cpp b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.cpp
new file mode 100644
index 0000000000..f94fef6f84
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.cpp
@@ -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.
+ *
+ */
+
+#include "Transaction.h"
+#include "Messages.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+Transaction::~Transaction()
+{
+ // Transactions that are recovered then found to be deleted get destroyed
+ // but need not be logged.
+ if (state != TRANS_DELETED)
+ log->deleteTransaction(id);
+}
+
+void
+Transaction::enroll(uint64_t msgId)
+{
+ qpid::sys::ScopedWlock<qpid::sys::RWlock> l(enrollLock);
+ enrolledMessages.push_back(msgId);
+}
+
+void
+Transaction::unenroll(uint64_t msgId)
+{
+ qpid::sys::ScopedWlock<qpid::sys::RWlock> l(enrollLock);
+ for (std::vector<uint64_t>::iterator i = enrolledMessages.begin();
+ i < enrolledMessages.end();
+ ++i) {
+ if (*i == msgId) {
+ enrolledMessages.erase(i);
+ break;
+ }
+ }
+}
+
+void
+Transaction::abort(Messages& messages)
+{
+ log->recordAbort(id);
+ for (size_t i = 0; i < enrolledMessages.size(); ++i)
+ messages.abort(enrolledMessages[i], shared_from_this());
+ state = TRANS_ABORTED;
+}
+
+void
+Transaction::commit(Messages& messages)
+{
+ log->recordCommit(id);
+ for (size_t i = 0; i < enrolledMessages.size(); ++i)
+ messages.commit(enrolledMessages[i], shared_from_this());
+ state = TRANS_COMMITTED;
+}
+
+void
+TPCTransaction::prepare(void)
+{
+ log->recordPrepare(id);
+ state = TRANS_PREPARED;
+}
+
+}}} // namespace qpid::store::ms_clfs
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h
new file mode 100644
index 0000000000..fd07f2fb2e
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h
@@ -0,0 +1,146 @@
+#ifndef QPID_STORE_MSCLFS_TRANSACTION_H
+#define QPID_STORE_MSCLFS_TRANSACTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/broker/TransactionalStore.h>
+#include <qpid/sys/Mutex.h>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+#include "TransactionLog.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+class Messages;
+
+/**
+ * @class Transaction
+ *
+ * Class representing an AMQP transaction. This is used around a set of
+ * enqueue and dequeue operations that occur when the broker is acting
+ * on a transaction commit/abort from the client.
+ * This class is what the store uses internally to implement things a
+ * transaction needs; the broker knows about TransactionContext, which
+ * holds a pointer to Transaction.
+ *
+ * NOTE: All references to Transactions (and TPCTransactions, below) are
+ * through Boost shared_ptr instances. All messages enrolled in a transaction
+ * hold a shared_ptr. Thus, a Transaction object will not be deleted until all
+ * messages holding a reference to it are deleted. This fact is also used
+ * during recovery to automatically clean up and delete any Transaction without
+ * messages left referring to it.
+ */
+class Transaction : public boost::enable_shared_from_this<Transaction> {
+private:
+ // TransactionLog has to create all Transaction instances.
+ Transaction() {}
+
+public:
+
+ typedef boost::shared_ptr<Transaction> shared_ptr;
+ typedef enum { TRANS_OPEN = 1,
+ TRANS_PREPARED,
+ TRANS_ABORTED,
+ TRANS_COMMITTED,
+ TRANS_DELETED } State;
+
+ virtual ~Transaction();
+
+ uint64_t getId() { return id; }
+ State getState() { return state; }
+
+ void enroll(uint64_t msgId);
+ void unenroll(uint64_t msgId); // For failed ops, not normal end-of-trans
+
+ void abort(Messages& messages);
+ void commit(Messages& messages);
+
+protected:
+ friend class TransactionLog;
+ Transaction(uint64_t _id, const TransactionLog::shared_ptr& _log)
+ : id(_id), state(TRANS_OPEN), log(_log) {}
+
+ uint64_t id;
+ State state;
+ TransactionLog::shared_ptr log;
+ std::vector<uint64_t> enrolledMessages;
+ qpid::sys::RWlock enrollLock;
+};
+
+class TransactionContext : public qpid::broker::TransactionContext {
+ Transaction::shared_ptr transaction;
+
+public:
+ TransactionContext(const Transaction::shared_ptr& _transaction)
+ : transaction(_transaction) {}
+
+ virtual Transaction::shared_ptr& getTransaction() { return transaction; }
+};
+
+/**
+ * @class TPCTransaction
+ *
+ * Class representing a Two-Phase-Commit (TPC) AMQP transaction. This is
+ * used around a set of enqueue and dequeue operations that occur when the
+ * broker is acting on a transaction prepare/commit/abort from the client.
+ * This class is what the store uses internally to implement things a
+ * transaction needs; the broker knows about TPCTransactionContext, which
+ * holds a pointer to TPCTransaction.
+ */
+class TPCTransaction : public Transaction {
+
+ friend class TransactionLog;
+ TPCTransaction(uint64_t _id,
+ const TransactionLog::shared_ptr& _log,
+ const std::string& _xid)
+ : Transaction(_id, _log), xid(_xid) {}
+
+ std::string xid;
+
+public:
+ typedef boost::shared_ptr<TPCTransaction> shared_ptr;
+
+ virtual ~TPCTransaction() {}
+
+ void prepare();
+ bool isPrepared() const { return state == TRANS_PREPARED; }
+
+ const std::string& getXid(void) const { return xid; }
+};
+
+class TPCTransactionContext : public qpid::broker::TPCTransactionContext {
+ TPCTransaction::shared_ptr transaction;
+
+public:
+ TPCTransactionContext(const TPCTransaction::shared_ptr& _transaction)
+ : transaction(_transaction) {}
+
+ virtual TPCTransaction::shared_ptr& getTransaction() { return transaction; }
+};
+
+}}} // namespace qpid::store::ms_clfs
+
+#endif /* QPID_STORE_MSCLFS_TRANSACTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp
new file mode 100644
index 0000000000..04780e83e8
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp
@@ -0,0 +1,428 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <windows.h>
+#include <clfsw32.h>
+#include <exception>
+#include <malloc.h>
+#include <memory.h>
+#include <qpid/framing/Buffer.h>
+#include <qpid/log/Statement.h>
+#include <qpid/sys/IntegerTypes.h>
+#include <qpid/sys/windows/check.h>
+
+#include "TransactionLog.h"
+#include "Transaction.h"
+#include "Lsn.h"
+
+namespace {
+
+// Structures that hold log records. Each has a type field at the start.
+enum TransactionEntryType {
+ TransactionStartDtxEntry = 1,
+ TransactionStartTxEntry = 2,
+ TransactionPrepareEntry = 3,
+ TransactionCommitEntry = 4,
+ TransactionAbortEntry = 5,
+ TransactionDeleteEntry = 6
+};
+// The only thing that really takes up space in transaction records is the
+// xid. Max xid length is in the neighborhood of 600 bytes. Leave some room.
+static const uint32_t MaxTransactionContentLength = 1024;
+
+// Dtx-Start
+struct TransactionStartDtx {
+ TransactionEntryType type;
+ uint32_t length;
+ char content[MaxTransactionContentLength];
+
+ TransactionStartDtx()
+ : type(TransactionStartDtxEntry), length(0) {}
+};
+// Tx-Start
+struct TransactionStartTx {
+ TransactionEntryType type;
+
+ TransactionStartTx()
+ : type(TransactionStartTxEntry) {}
+};
+// Prepare
+struct TransactionPrepare {
+ TransactionEntryType type;
+
+ TransactionPrepare()
+ : type(TransactionPrepareEntry) {}
+};
+// Commit
+struct TransactionCommit {
+ TransactionEntryType type;
+
+ TransactionCommit()
+ : type(TransactionCommitEntry) {}
+};
+// Abort
+struct TransactionAbort {
+ TransactionEntryType type;
+
+ TransactionAbort()
+ : type(TransactionAbortEntry) {}
+};
+// Delete
+struct TransactionDelete {
+ TransactionEntryType type;
+
+ TransactionDelete()
+ : type(TransactionDeleteEntry) {}
+};
+
+} // namespace
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+void
+TransactionLog::initialize()
+{
+ // Write something to occupy the first record, preventing a real
+ // transaction from being lsn/id 0. Delete of a non-existant id is easily
+ // tossed during recovery if no other transactions have caused the tail
+ // to be moved up past this dummy record by then.
+ deleteTransaction(0);
+}
+
+uint32_t
+TransactionLog::marshallingBufferSize()
+{
+ size_t biggestNeed = sizeof(TransactionStartDtx);
+ uint32_t defSize = static_cast<uint32_t>(biggestNeed);
+ uint32_t minSize = Log::marshallingBufferSize();
+ if (defSize <= minSize)
+ return minSize;
+ // Round up to multiple of minSize
+ return (defSize + minSize) / minSize * minSize;
+}
+
+// Get a new Transaction
+boost::shared_ptr<Transaction>
+TransactionLog::begin()
+{
+ TransactionStartTx entry;
+ CLFS_LSN location;
+ uint64_t id;
+ uint32_t entryLength = static_cast<uint32_t>(sizeof(entry));
+ location = write(&entry, entryLength);
+ try {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ id = lsnToId(location);
+ std::auto_ptr<Transaction> t(new Transaction(id, shared_from_this()));
+ boost::shared_ptr<Transaction> t2(t);
+ boost::weak_ptr<Transaction> weak_t2(t2);
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ validIds[id] = weak_t2;
+ }
+ return t2;
+ }
+ catch(...) {
+ deleteTransaction(id);
+ throw;
+ }
+}
+
+// Get a new TPCTransaction
+boost::shared_ptr<TPCTransaction>
+TransactionLog::begin(const std::string& xid)
+{
+ TransactionStartDtx entry;
+ CLFS_LSN location;
+ uint64_t id;
+ uint32_t entryLength = static_cast<uint32_t>(sizeof(entry));
+ entry.length = static_cast<uint32_t>(xid.length());
+ memcpy_s(entry.content, sizeof(entry.content),
+ xid.c_str(), xid.length());
+ entryLength -= (sizeof(entry.content) - entry.length);
+ location = write(&entry, entryLength);
+ try {
+ id = lsnToId(location);
+ std::auto_ptr<TPCTransaction> t(new TPCTransaction(id,
+ shared_from_this(),
+ xid));
+ boost::shared_ptr<TPCTransaction> t2(t);
+ boost::weak_ptr<Transaction> weak_t2(t2);
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ validIds[id] = weak_t2;
+ }
+ return t2;
+ }
+ catch(...) {
+ deleteTransaction(id);
+ throw;
+ }
+}
+
+void
+TransactionLog::recordPrepare(uint64_t transId)
+{
+ TransactionPrepare entry;
+ CLFS_LSN transLsn = idToLsn(transId);
+ write(&entry, sizeof(entry), &transLsn);
+}
+
+void
+TransactionLog::recordCommit(uint64_t transId)
+{
+ TransactionCommit entry;
+ CLFS_LSN transLsn = idToLsn(transId);
+ write(&entry, sizeof(entry), &transLsn);
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ validIds[transId].reset();
+ }
+}
+
+void
+TransactionLog::recordAbort(uint64_t transId)
+{
+ TransactionAbort entry;
+ CLFS_LSN transLsn = idToLsn(transId);
+ write(&entry, sizeof(entry), &transLsn);
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ validIds[transId].reset();
+ }
+}
+
+void
+TransactionLog::deleteTransaction(uint64_t transId)
+{
+ uint64_t newFirstId = 0;
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ validIds.erase(transId);
+ // May have deleted the first entry; if so the log can release that.
+ // If this deletion results in an empty list of transactions,
+ // move the tail up to this transaction's LSN. This may result in
+ // one or more transactions being stranded in the log until there's
+ // more activity. If a restart happens while these unneeded log
+ // records are there, the presence of the TransactionDelete
+ // entry will cause them to be ignored anyway.
+ if (validIds.empty())
+ newFirstId = transId;
+ else if (validIds.begin()->first > transId)
+ newFirstId = validIds.begin()->first;
+ }
+ TransactionDelete deleteEntry;
+ CLFS_LSN transLsn = idToLsn(transId);
+ write(&deleteEntry, sizeof(deleteEntry), &transLsn);
+ if (newFirstId != 0)
+ moveTail(idToLsn(newFirstId));
+}
+
+void
+TransactionLog::collectPreparedXids(std::map<std::string, TPCTransaction::shared_ptr>& preparedMap)
+{
+ // Go through all the known transactions; if the transaction is still
+ // valid (open or prepared) it will have weak_ptr to the Transaction.
+ // If it can be downcast and has a state of TRANS_PREPARED, add to the map.
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(idsLock);
+ std::map<uint64_t, boost::weak_ptr<Transaction> >::const_iterator i;
+ for (i = validIds.begin(); i != validIds.end(); ++i) {
+ Transaction::shared_ptr t = i->second.lock();
+ if (t.get() == 0)
+ continue;
+ TPCTransaction::shared_ptr tpct(boost::dynamic_pointer_cast<TPCTransaction>(t));
+ if (tpct.get() == 0)
+ continue;
+ if (tpct->state == Transaction::TRANS_PREPARED)
+ preparedMap[tpct->getXid()] = tpct;
+ }
+}
+
+void
+TransactionLog::recover(std::map<uint64_t, Transaction::shared_ptr>& transMap)
+{
+ QPID_LOG(debug, "Recovering transaction log");
+
+ // Note that there may be transaction refs in the log which are deleted,
+ // so be sure to only add transactions at Start records, and ignore those
+ // that don't have an existing message record.
+ // Get the base LSN - that's how to say "start reading at the beginning"
+ CLFS_INFORMATION info;
+ ULONG infoLength = sizeof (info);
+ BOOL ok = ::GetLogFileInformation(handle, &info, &infoLength);
+ QPID_WINDOWS_CHECK_NOT(ok, 0);
+
+ // Pointers for the various record types that can be assigned in the
+ // reading loop below.
+ TransactionStartDtx *startDtxEntry;
+ TransactionStartTx *startTxEntry;
+
+ PVOID recordPointer;
+ ULONG recordLength;
+ CLFS_RECORD_TYPE recordType = ClfsDataRecord;
+ CLFS_LSN transLsn, current, undoNext;
+ PVOID readContext;
+ uint64_t transId;
+ // Note 'current' in case it's needed below; ReadNextLogRecord returns it
+ // via a parameter.
+ current = info.BaseLsn;
+ ok = ::ReadLogRecord(marshal,
+ &info.BaseLsn,
+ ClfsContextForward,
+ &recordPointer,
+ &recordLength,
+ &recordType,
+ &undoNext,
+ &transLsn,
+ &readContext,
+ 0);
+
+ std::auto_ptr<Transaction> tPtr;
+ std::auto_ptr<TPCTransaction> tpcPtr;
+ while (ok) {
+ std::string xid;
+
+ // All the record types this class writes have a TransactionEntryType
+ // in the beginning. Based on that, do what's needed.
+ TransactionEntryType *t =
+ reinterpret_cast<TransactionEntryType *>(recordPointer);
+ switch(*t) {
+ case TransactionStartDtxEntry:
+ startDtxEntry =
+ reinterpret_cast<TransactionStartDtx *>(recordPointer);
+ transId = lsnToId(current);
+ QPID_LOG(debug, "Dtx start, id " << transId);
+ xid.assign(startDtxEntry->content, startDtxEntry->length);
+ tpcPtr.reset(new TPCTransaction(transId, shared_from_this(), xid));
+ transMap[transId] = boost::shared_ptr<TPCTransaction>(tpcPtr);
+ break;
+ case TransactionStartTxEntry:
+ startTxEntry =
+ reinterpret_cast<TransactionStartTx *>(recordPointer);
+ transId = lsnToId(current);
+ QPID_LOG(debug, "Tx start, id " << transId);
+ tPtr.reset(new Transaction(transId, shared_from_this()));
+ transMap[transId] = boost::shared_ptr<Transaction>(tPtr);
+ break;
+ case TransactionPrepareEntry:
+ transId = lsnToId(transLsn);
+ QPID_LOG(debug, "Dtx prepare, id " << transId);
+ if (transMap.find(transId) == transMap.end()) {
+ QPID_LOG(debug,
+ "Dtx " << transId << " doesn't exist; discarded");
+ }
+ else {
+ transMap[transId]->state = Transaction::TRANS_PREPARED;
+ }
+ break;
+ case TransactionCommitEntry:
+ transId = lsnToId(transLsn);
+ QPID_LOG(debug, "Txn commit, id " << transId);
+ if (transMap.find(transId) == transMap.end()) {
+ QPID_LOG(debug,
+ "Txn " << transId << " doesn't exist; discarded");
+ }
+ else {
+ transMap[transId]->state = Transaction::TRANS_COMMITTED;
+ }
+ break;
+ case TransactionAbortEntry:
+ transId = lsnToId(transLsn);
+ QPID_LOG(debug, "Txn abort, id " << transId);
+ if (transMap.find(transId) == transMap.end()) {
+ QPID_LOG(debug,
+ "Txn " << transId << " doesn't exist; discarded");
+ }
+ else {
+ transMap[transId]->state = Transaction::TRANS_ABORTED;
+ }
+ break;
+ case TransactionDeleteEntry:
+ transId = lsnToId(transLsn);
+ QPID_LOG(debug, "Txn delete, id " << transId);
+ if (transMap.find(transId) == transMap.end()) {
+ QPID_LOG(debug,
+ "Txn " << transId << " doesn't exist; discarded");
+ }
+ else {
+ transMap[transId]->state = Transaction::TRANS_DELETED;
+ transMap.erase(transId);
+ }
+ break;
+ default:
+ throw std::runtime_error("Bad transaction log entry type");
+ }
+
+ recordType = ClfsDataRecord;
+ ok = ::ReadNextLogRecord(readContext,
+ &recordPointer,
+ &recordLength,
+ &recordType,
+ 0, // No userLsn
+ &undoNext,
+ &transLsn,
+ &current,
+ 0);
+ }
+ DWORD status = ::GetLastError();
+ ::TerminateReadLog(readContext);
+ if (status != ERROR_HANDLE_EOF) // No more records
+ throw QPID_WINDOWS_ERROR(status);
+
+ QPID_LOG(debug, "Transaction log recovered");
+
+ // At this point we have a list of all the not-deleted transactions that
+ // were in existence when the broker last ran. All transactions of both
+ // Dtx and Tx types that haven't prepared or committed will be aborted.
+ // This will give the proper background against which to decide each
+ // message's disposition when recovering messages that were involved in
+ // transactions.
+ // In addition to recovering and aborting transactions, rebuild the
+ // validIds map now that we know which ids are really valid.
+ std::map<uint64_t, Transaction::shared_ptr>::const_iterator i;
+ for (i = transMap.begin(); i != transMap.end(); ++i) {
+ switch(i->second->state) {
+ case Transaction::TRANS_OPEN:
+ QPID_LOG(debug, "Txn " << i->first << " was open; aborted");
+ i->second->state = Transaction::TRANS_ABORTED;
+ break;
+ case Transaction::TRANS_ABORTED:
+ QPID_LOG(debug, "Txn " << i->first << " was aborted");
+ break;
+ case Transaction::TRANS_COMMITTED:
+ QPID_LOG(debug, "Txn " << i->first << " was committed");
+ break;
+ case Transaction::TRANS_PREPARED:
+ QPID_LOG(debug, "Txn " << i->first << " was prepared");
+ break;
+ case Transaction::TRANS_DELETED:
+ QPID_LOG(error,
+ "Txn " << i->first << " was deleted; shouldn't be here");
+ break;
+ }
+ boost::weak_ptr<Transaction> weak_txn(i->second);
+ validIds[i->first] = weak_txn;
+ }
+}
+
+}}} // namespace qpid::store::ms_clfs
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.h b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.h
new file mode 100644
index 0000000000..7ca27c229e
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.h
@@ -0,0 +1,104 @@
+#ifndef QPID_STORE_MSCLFS_TRANSACTIONLOG_H
+#define QPID_STORE_MSCLFS_TRANSACTIONLOG_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <set>
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <qpid/broker/RecoveryManager.h>
+#include <qpid/sys/IntegerTypes.h>
+#include <qpid/sys/Mutex.h>
+
+#include "Log.h"
+
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
+class Transaction;
+class TPCTransaction;
+
+/**
+ * @class TransactionLog
+ *
+ * Represents a CLFS-housed transaction log.
+ */
+class TransactionLog : public Log,
+ public boost::enable_shared_from_this<TransactionLog> {
+
+ // To know when it's ok to move the log tail the lowest valid Id must
+ // always be known. Keep track of valid Ids here. These are transactions
+ // which have not yet been Deleted in the log. They may be new, in progress,
+ // prepared, committed, or aborted - but not deleted.
+ // Entries corresponding to not-yet-finalized transactions (i.e., open or
+ // prepared) also have a weak_ptr so the Transaction can be accessed.
+ // This is primarily to check its state and get a list of prepared Xids.
+ std::map<uint64_t, boost::weak_ptr<Transaction> > validIds;
+ qpid::sys::Mutex idsLock;
+
+protected:
+ // Transaction log needs to have a no-op first record written in the log
+ // to ensure that no real transaction gets an ID 0; messages think trans
+ // id 0 means "no transaction."
+ virtual void initialize();
+
+public:
+ // Inherited and reimplemented from Log. Figure the minimum marshalling
+ // buffer size needed for the records this class writes.
+ virtual uint32_t marshallingBufferSize();
+
+ typedef boost::shared_ptr<TransactionLog> shared_ptr;
+
+ // Get a new Transaction
+ boost::shared_ptr<Transaction> begin();
+
+ // Get a new TPCTransaction
+ boost::shared_ptr<TPCTransaction> begin(const std::string& xid);
+
+ void recordPrepare(uint64_t transId);
+ void recordCommit(uint64_t transId);
+ void recordAbort(uint64_t transId);
+ void deleteTransaction(uint64_t transId);
+
+ // Fill @arg preparedMap with Xid->TPCTransaction::shared_ptr for all
+ // currently prepared transactions.
+ void collectPreparedXids(std::map<std::string, boost::shared_ptr<TPCTransaction> >& preparedMap);
+
+ // Recover the transactions and their state from the log.
+ // Every non-deleted transaction recovered from the log will be
+ // represented in @arg transMap. The recovering messages can use this
+ // information to tell if a transaction referred to in an enqueue/dequeue
+ // operation should be recovered or dropped by examining transaction state.
+ //
+ // @param recoverer Recovery manager used to recreate broker objects from
+ // entries recovered from the log.
+ // @param transMap This method fills in the map of id -> shared_ptr of
+ // recovered transactions.
+ void recover(std::map<uint64_t, boost::shared_ptr<Transaction> >& transMap);
+};
+
+}}} // namespace qpid::store::ms_clfs
+
+#endif /* QPID_STORE_MSCLFS_TRANSACTIONLOG_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp b/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp
new file mode 100644
index 0000000000..095d1bf331
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp
@@ -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.
+ *
+ */
+
+#include "AmqpTransaction.h"
+#include "DatabaseConnection.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+AmqpTransaction::AmqpTransaction(const boost::shared_ptr<DatabaseConnection>& _db)
+ : db(_db), sqlTrans(_db)
+{
+}
+
+AmqpTransaction::~AmqpTransaction()
+{
+}
+
+void
+AmqpTransaction::sqlBegin()
+{
+ sqlTrans.begin();
+}
+
+void
+AmqpTransaction::sqlCommit()
+{
+ sqlTrans.commit();
+}
+
+void
+AmqpTransaction::sqlAbort()
+{
+ sqlTrans.abort();
+}
+
+
+AmqpTPCTransaction::AmqpTPCTransaction(const boost::shared_ptr<DatabaseConnection>& db,
+ const std::string& _xid)
+ : AmqpTransaction(db), prepared(false), xid(_xid)
+{
+}
+
+AmqpTPCTransaction::~AmqpTPCTransaction()
+{
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.h b/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.h
new file mode 100644
index 0000000000..625fab5595
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/AmqpTransaction.h
@@ -0,0 +1,85 @@
+#ifndef QPID_STORE_MSSQL_AMQPTRANSACTION_H
+#define QPID_STORE_MSSQL_AMQPTRANSACTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/broker/TransactionalStore.h>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+#include "SqlTransaction.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+class DatabaseConnection;
+
+/**
+ * @class AmqpTransaction
+ *
+ * Class representing an AMQP transaction. This is used around a set of
+ * enqueue and dequeue operations that occur when the broker is acting
+ * on a transaction commit/abort from the client.
+ */
+class AmqpTransaction : public qpid::broker::TransactionContext {
+
+ boost::shared_ptr<DatabaseConnection> db;
+ SqlTransaction sqlTrans;
+
+public:
+ AmqpTransaction(const boost::shared_ptr<DatabaseConnection>& _db);
+ virtual ~AmqpTransaction();
+
+ DatabaseConnection *dbConn() { return db.get(); }
+
+ void sqlBegin();
+ void sqlCommit();
+ void sqlAbort();
+};
+
+/**
+ * @class AmqpTPCTransaction
+ *
+ * Class representing a Two-Phase-Commit (TPC) AMQP transaction. This is
+ * used around a set of enqueue and dequeue operations that occur when the
+ * broker is acting on a transaction prepare/commit/abort from the client.
+ */
+class AmqpTPCTransaction : public AmqpTransaction,
+ public qpid::broker::TPCTransactionContext {
+ bool prepared;
+ std::string xid;
+
+public:
+ AmqpTPCTransaction(const boost::shared_ptr<DatabaseConnection>& db,
+ const std::string& _xid);
+ virtual ~AmqpTPCTransaction();
+
+ void setPrepared(void) { prepared = true; }
+ bool isPrepared(void) const { return prepared; }
+
+ const std::string& getXid(void) const { return xid; }
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_AMQPTRANSACTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp
new file mode 100644
index 0000000000..1dc4370312
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp
@@ -0,0 +1,165 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+
+#include "BindingRecordset.h"
+#include "BlobAdapter.h"
+#include "BlobEncoder.h"
+#include "VariantHelper.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+BindingRecordset::removeFilter(const std::string& filter)
+{
+ rs->PutFilter (VariantHelper<std::string>(filter));
+ long recs = rs->GetRecordCount();
+ if (recs == 0)
+ return; // Nothing to do
+ while (recs > 0) {
+ // Deleting adAffectAll doesn't work as documented; go one by one.
+ rs->Delete(adAffectCurrent);
+ if (--recs > 0)
+ rs->MoveNext();
+ }
+ rs->Update();
+}
+
+void
+BindingRecordset::add(uint64_t exchangeId,
+ uint64_t queueId,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable& args)
+{
+ VariantHelper<std::string> routingKeyStr(routingKey);
+ BlobEncoder blob (args); // Marshall field table to a blob
+ rs->AddNew();
+ rs->Fields->GetItem("exchangeId")->Value = exchangeId;
+ rs->Fields->GetItem("queueId")->Value = queueId;
+ rs->Fields->GetItem("routingKey")->Value = routingKeyStr;
+ rs->Fields->GetItem("fieldTableBlob")->AppendChunk(blob);
+ rs->Update();
+}
+
+void
+BindingRecordset::remove(uint64_t exchangeId,
+ uint64_t queueId,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable& /*args*/)
+{
+ // Look up the affected binding.
+ std::ostringstream filter;
+ filter << "exchangeId = " << exchangeId
+ << " AND queueId = " << queueId
+ << " AND routingKey = '" << routingKey << "'" << std::ends;
+ removeFilter(filter.str());
+}
+
+void
+BindingRecordset::removeForExchange(uint64_t exchangeId)
+{
+ // Look up the affected bindings by the exchange ID
+ std::ostringstream filter;
+ filter << "exchangeId = " << exchangeId << std::ends;
+ removeFilter(filter.str());
+}
+
+void
+BindingRecordset::removeForQueue(uint64_t queueId)
+{
+ // Look up the affected bindings by the queue ID
+ std::ostringstream filter;
+ filter << "queueId = " << queueId << std::ends;
+ removeFilter(filter.str());
+}
+
+void
+BindingRecordset::recover(broker::RecoveryManager& recoverer,
+ const store::ExchangeMap& exchMap,
+ const store::QueueMap& queueMap)
+{
+ if (rs->BOF && rs->EndOfFile)
+ return; // Nothing to do
+ rs->MoveFirst();
+ Binding b;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&b);
+ while (!rs->EndOfFile) {
+ long blobSize = rs->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = rs->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ store::ExchangeMap::const_iterator exch = exchMap.find(b.exchangeId);
+ if (exch == exchMap.end()) {
+ std::ostringstream msg;
+ msg << "Error recovering bindings; exchange ID " << b.exchangeId
+ << " not found in exchange map";
+ throw qpid::Exception(msg.str());
+ }
+ broker::RecoverableExchange::shared_ptr exchPtr = exch->second;
+ store::QueueMap::const_iterator q = queueMap.find(b.queueId);
+ if (q == queueMap.end()) {
+ std::ostringstream msg;
+ msg << "Error recovering bindings; queue ID " << b.queueId
+ << " not found in queue map";
+ throw qpid::Exception(msg.str());
+ }
+ broker::RecoverableQueue::shared_ptr qPtr = q->second;
+ // The recovery manager wants the queue name, so get it from the
+ // RecoverableQueue.
+ std::string key(b.routingKey);
+ exchPtr->bind(qPtr->getName(), key, blob);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+void
+BindingRecordset::dump()
+{
+ Recordset::dump();
+ if (rs->EndOfFile && rs->BOF) // No records
+ return;
+ rs->MoveFirst();
+
+ Binding b;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&b);
+
+ while (VARIANT_FALSE == rs->EndOfFile) {
+ QPID_LOG(notice, "exch Id " << b.exchangeId
+ << ", q Id " << b.queueId
+ << ", k: " << b.routingKey);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.h b/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.h
new file mode 100644
index 0000000000..3cb732de75
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BindingRecordset.h
@@ -0,0 +1,88 @@
+#ifndef QPID_STORE_MSSQL_BINDINGRECORDSET_H
+#define QPID_STORE_MSSQL_BINDINGRECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <icrsint.h>
+#include "Recordset.h"
+#include <qpid/store/StorageProvider.h>
+#include <qpid/broker/RecoveryManager.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class BindingRecordset
+ *
+ * Class for the binding records.
+ */
+class BindingRecordset : public Recordset {
+
+ class Binding : public CADORecordBinding {
+ BEGIN_ADO_BINDING(Binding)
+ ADO_FIXED_LENGTH_ENTRY2(1, adBigInt, exchangeId, FALSE)
+ ADO_FIXED_LENGTH_ENTRY2(2, adBigInt, queueId, FALSE)
+ ADO_VARIABLE_LENGTH_ENTRY4(3, adVarChar, routingKey,
+ sizeof(routingKey), FALSE)
+ END_ADO_BINDING()
+
+ public:
+ uint64_t exchangeId;
+ uint64_t queueId;
+ char routingKey[256];
+ };
+
+ // Remove all records matching the specified filter/query.
+ void removeFilter(const std::string& filter);
+
+public:
+ // Add a new binding
+ void add(uint64_t exchangeId,
+ uint64_t queueId,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable& args);
+
+ // Remove a specific binding
+ void remove(uint64_t exchangeId,
+ uint64_t queueId,
+ const std::string& routingKey,
+ const qpid::framing::FieldTable& args);
+
+ // Remove all bindings for the specified exchange
+ void removeForExchange(uint64_t exchangeId);
+
+ // Remove all bindings for the specified queue
+ void removeForQueue(uint64_t queueId);
+
+ // Recover bindings set using exchMap to get from Id to RecoverableExchange.
+ void recover(qpid::broker::RecoveryManager& recoverer,
+ const qpid::store::ExchangeMap& exchMap,
+ const qpid::store::QueueMap& queueMap);
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_BINDINGRECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp b/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp
new file mode 100644
index 0000000000..1889f34e41
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp
@@ -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.
+ *
+ */
+
+#include "BlobAdapter.h"
+#include <qpid/Exception.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+BlobAdapter::extractBuff()
+{
+ // To give a valid Buffer back, lock the safearray, obtaining a pointer to
+ // the actual data. Record the pointer in the Buffer so the destructor
+ // knows to unlock the safearray.
+ if (buff.getPointer() == 0) {
+ char *blob;
+ SafeArrayAccessData(this->parray, (void **)&blob);
+ qpid::framing::Buffer lockedBuff(blob, buff.getSize());
+ buff = lockedBuff;
+ }
+}
+
+
+BlobAdapter::~BlobAdapter()
+{
+ // If buff's pointer is set, the safearray is locked, so unlock it
+ if (buff.getPointer() != 0)
+ SafeArrayUnaccessData(this->parray);
+}
+
+BlobAdapter::operator qpid::framing::Buffer& ()
+{
+ extractBuff();
+ return buff;
+}
+
+BlobAdapter::operator qpid::framing::FieldTable& ()
+{
+ extractBuff();
+ fields.decode(buff);
+ return fields;
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.h b/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.h
new file mode 100644
index 0000000000..1c666392bc
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobAdapter.h
@@ -0,0 +1,62 @@
+#ifndef QPID_STORE_MSSQL_BLOBADAPTER_H
+#define QPID_STORE_MSSQL_BLOBADAPTER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <comutil.h>
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/FieldTable.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class BlobAdapter
+ *
+ * Adapter for accessing a blob (varbinary SQL field) as a qpid::framing::Buffer
+ * in an exception-safe way.
+ */
+class BlobAdapter : public _variant_t {
+private:
+ // This Buffer's pointer indicates whether or not a safearray has
+ // been locked; if it's 0, no locking was done.
+ qpid::framing::Buffer buff;
+ qpid::framing::FieldTable fields;
+
+ void extractBuff();
+
+public:
+ // Initialize with the known length of the data that will come.
+ // Assigning a _variant_t to this object will set up the array to be
+ // accessed with the operator Buffer&()
+ BlobAdapter(long blobSize) : _variant_t(), buff(0, blobSize) {}
+ ~BlobAdapter();
+ BlobAdapter& operator=(_variant_t& var_t_Src)
+ { _variant_t::operator=(var_t_Src); return *this; }
+ operator qpid::framing::Buffer& ();
+ operator qpid::framing::FieldTable& ();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_BLOBADAPTER_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp b/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp
new file mode 100644
index 0000000000..75d3dc2d86
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp
@@ -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.
+ *
+ */
+
+#include "BlobEncoder.h"
+#include <qpid/Exception.h>
+#include <qpid/broker/Persistable.h>
+#include <qpid/broker/PersistableMessage.h>
+#include <boost/intrusive_ptr.hpp>
+#include <memory.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+template <class ITEM> void
+BlobEncoder::encode(const ITEM &item)
+{
+ SAFEARRAYBOUND bound[1] = {0, 0};
+ bound[0].cElements = item.encodedSize();
+ blob = SafeArrayCreate(VT_UI1, 1, bound);
+ if (S_OK != SafeArrayLock(blob)) {
+ SafeArrayDestroy(blob);
+ blob = 0;
+ throw qpid::Exception("Error locking blob area for persistable item");
+ }
+ try {
+ qpid::framing::Buffer buff((char *)blob->pvData, bound[0].cElements);
+ item.encode(buff);
+ }
+ catch(...) {
+ SafeArrayUnlock(blob);
+ SafeArrayDestroy(blob);
+ blob = 0;
+ throw;
+ }
+ this->vt = VT_ARRAY | VT_UI1;
+ this->parray = blob;
+ SafeArrayUnlock(blob);
+}
+
+template <> void
+BlobEncoder::encode(const boost::intrusive_ptr<qpid::broker::PersistableMessage> &item)
+{
+ // NOTE! If this code changes, verify the recovery code in MessageRecordset
+ SAFEARRAYBOUND bound[1] = {0, 0};
+ bound[0].cElements = item->encodedSize() + sizeof(uint32_t);
+ blob = SafeArrayCreate(VT_UI1, 1, bound);
+ if (S_OK != SafeArrayLock(blob)) {
+ SafeArrayDestroy(blob);
+ blob = 0;
+ throw qpid::Exception("Error locking blob area for message");
+ }
+ try {
+ uint32_t headerSize = item->encodedHeaderSize();
+ qpid::framing::Buffer buff((char *)blob->pvData, bound[0].cElements);
+ buff.putLong(headerSize);
+ item->encode(buff);
+ }
+ catch(...) {
+ SafeArrayUnlock(blob);
+ SafeArrayDestroy(blob);
+ blob = 0;
+ throw;
+ }
+ this->vt = VT_ARRAY | VT_UI1;
+ this->parray = blob;
+ SafeArrayUnlock(blob);
+}
+
+template <> void
+BlobEncoder::encode(const std::string &item)
+{
+ SAFEARRAYBOUND bound[1] = {0, 0};
+ bound[0].cElements = item.size();
+ blob = SafeArrayCreate(VT_UI1, 1, bound);
+ if (S_OK != SafeArrayLock(blob)) {
+ SafeArrayDestroy(blob);
+ blob = 0;
+ throw qpid::Exception("Error locking blob area for string");
+ }
+ memcpy_s(blob->pvData, item.size(), item.data(), item.size());
+ this->vt = VT_ARRAY | VT_UI1;
+ this->parray = blob;
+ SafeArrayUnlock(blob);
+}
+
+BlobEncoder::BlobEncoder(const qpid::broker::Persistable &item) : blob(0)
+{
+ encode(item);
+}
+
+BlobEncoder::BlobEncoder(const boost::intrusive_ptr<qpid::broker::PersistableMessage> &msg) : blob(0)
+{
+ encode(msg);
+}
+
+BlobEncoder::BlobEncoder(const qpid::framing::FieldTable &fields) : blob(0)
+{
+ encode(fields);
+}
+
+BlobEncoder::BlobEncoder(const std::string &data) : blob(0)
+{
+ encode(data);
+}
+
+BlobEncoder::~BlobEncoder()
+{
+ if (blob)
+ SafeArrayDestroy(blob);
+ blob = 0;
+ this->parray = 0;
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.h b/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.h
new file mode 100644
index 0000000000..d2b56223c1
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobEncoder.h
@@ -0,0 +1,61 @@
+#ifndef QPID_STORE_MSSQL_BLOBENCODER_H
+#define QPID_STORE_MSSQL_BLOBENCODER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <comutil.h>
+#include <string>
+#include <boost/intrusive_ptr.hpp>
+#include <qpid/broker/Persistable.h>
+#include <qpid/broker/PersistableMessage.h>
+#include <qpid/framing/Buffer.h>
+#include <qpid/framing/FieldTable.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class BlobEncoder
+ *
+ * Encodes a blob (varbinary) field from a qpid::broker::Persistable or a
+ * qpid::framing::FieldTable (both of which can be encoded to
+ * qpid::framing::Buffer) so it can be passed to ADO methods for writing
+ * to the database.
+ */
+class BlobEncoder : public _variant_t {
+private:
+ SAFEARRAY *blob;
+
+ template <class ITEM> void encode(const ITEM &item);
+
+public:
+ BlobEncoder(const qpid::broker::Persistable &item);
+ BlobEncoder(const boost::intrusive_ptr<qpid::broker::PersistableMessage> &msg);
+ BlobEncoder(const qpid::framing::FieldTable &fields);
+ BlobEncoder(const std::string& data);
+ ~BlobEncoder();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_BLOBENCODER_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp
new file mode 100644
index 0000000000..ef1757dbad
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+
+#include "BlobRecordset.h"
+#include "BlobEncoder.h"
+#include "VariantHelper.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+BlobRecordset::add(const qpid::broker::Persistable& item)
+{
+ BlobEncoder blob (item); // Marshall item info to a blob
+ rs->AddNew();
+ rs->Fields->GetItem("fieldTableBlob")->AppendChunk(blob);
+ rs->Update();
+ uint64_t id = rs->Fields->Item["persistenceId"]->Value;
+ item.setPersistenceId(id);
+}
+
+void
+BlobRecordset::remove(uint64_t id)
+{
+ // Look up the item by its persistenceId
+ std::ostringstream filter;
+ filter << "persistenceId = " << id << std::ends;
+ rs->PutFilter (VariantHelper<std::string>(filter.str()));
+ if (!rs->EndOfFile) {
+ // Delete the record
+ rs->Delete(adAffectCurrent);
+ rs->Update();
+ }
+}
+
+void
+BlobRecordset::remove(const qpid::broker::Persistable& item)
+{
+ remove(item.getPersistenceId());
+}
+
+void
+BlobRecordset::dump()
+{
+ Recordset::dump();
+#if 1
+ if (rs->EndOfFile && rs->BOF) // No records
+ return;
+
+ rs->MoveFirst();
+ while (!rs->EndOfFile) {
+ uint64_t id = rs->Fields->Item["persistenceId"]->Value;
+ QPID_LOG(notice, " -> " << id);
+ rs->MoveNext();
+ }
+#else
+ for (Iterator iter = begin(); iter != end(); ++iter) {
+ uint64_t id = *iter.first;
+ QPID_LOG(notice, " -> " << id);
+ }
+#endif
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.h b/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.h
new file mode 100644
index 0000000000..4d1c338746
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/BlobRecordset.h
@@ -0,0 +1,54 @@
+#ifndef QPID_STORE_MSSQL_BLOBRECORDSET_H
+#define QPID_STORE_MSSQL_BLOBRECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "Recordset.h"
+#include <qpid/broker/Persistable.h>
+#include <string>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class BlobRecordset
+ *
+ * Class for the "blob" records that record an id, varbinary(max) pair.
+ */
+class BlobRecordset : public Recordset {
+protected:
+
+public:
+ void add(const qpid::broker::Persistable& item);
+
+ // Remove a record given its Id.
+ void remove(uint64_t id);
+ void remove(const qpid::broker::Persistable& item);
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_BLOBRECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp b/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp
new file mode 100644
index 0000000000..3219ea526a
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp
@@ -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.
+ *
+ */
+
+#include "DatabaseConnection.h"
+#include "Exception.h"
+#include <comdef.h>
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+}
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+DatabaseConnection::DatabaseConnection() : conn(0)
+{
+}
+
+DatabaseConnection::~DatabaseConnection()
+{
+ close();
+}
+
+void
+DatabaseConnection::open(const std::string& connectString,
+ const std::string& dbName)
+{
+ if (conn && conn->State == adStateOpen)
+ return;
+ std::string adoConnect = "Provider=SQLOLEDB;" + connectString;
+ try {
+ TESTHR(conn.CreateInstance(__uuidof(Connection)));
+ conn->ConnectionString = adoConnect.c_str();
+ conn->Open("", "", "", adConnectUnspecified);
+ if (dbName.length() > 0)
+ conn->DefaultDatabase = dbName.c_str();
+ }
+ catch(_com_error &e) {
+ close();
+ throw ADOException("MSSQL can't open " + dbName + " at " + adoConnect, e);
+ }
+}
+
+void
+DatabaseConnection::close()
+{
+ if (conn && conn->State == adStateOpen)
+ conn->Close();
+ conn = 0;
+}
+
+std::string
+DatabaseConnection::getErrors()
+{
+ long errCount = conn->Errors->Count;
+ if (errCount <= 0)
+ return "";
+ // Collection ranges from 0 to nCount -1.
+ std::ostringstream messages;
+ ErrorPtr pErr = NULL;
+ for (long i = 0 ; i < errCount ; i++ ) {
+ if (i > 0)
+ messages << "\n";
+ messages << "[" << i << "] ";
+ pErr = conn->Errors->GetItem(i);
+ messages << "Error " << pErr->Number << ": "
+ << (LPCSTR)pErr->Description;
+ }
+ messages << std::ends;
+ return messages.str();
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.h b/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.h
new file mode 100644
index 0000000000..785d1587c5
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/DatabaseConnection.h
@@ -0,0 +1,64 @@
+#ifndef QPID_STORE_MSSQL_DATABASECONNECTION_H
+#define QPID_STORE_MSSQL_DATABASECONNECTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Bring in ADO 2.8 (yes, I know it says "15", but that's it...)
+#import "C:\Program Files\Common Files\System\ado\msado15.dll" \
+ no_namespace rename("EOF", "EndOfFile")
+
+#include <string>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class DatabaseConnection
+ *
+ * Represents a connection to the SQL database. This class wraps the
+ * needed _ConnectionPtr for ADO as well as the needed COM initialization
+ * and cleanup that each thread requires. It is expected that this class
+ * will be maintained in thread-specific storage so it has no locks.
+ */
+class DatabaseConnection {
+protected:
+ _ConnectionPtr conn;
+
+public:
+ DatabaseConnection();
+ ~DatabaseConnection();
+ void open(const std::string& connectString,
+ const std::string& dbName = "");
+ void close();
+ operator _ConnectionPtr () { return conn; }
+
+ void beginTransaction() { conn->BeginTrans(); }
+ void commitTransaction() {conn->CommitTrans(); }
+ void rollbackTransaction() { conn->RollbackTrans(); }
+
+ std::string getErrors();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_DATABASECONNECTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/Exception.h b/qpid/cpp/src/qpid/store/ms-sql/Exception.h
new file mode 100644
index 0000000000..65ec3388ff
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/Exception.h
@@ -0,0 +1,66 @@
+#ifndef QPID_STORE_MSSQL_EXCEPTION_H
+#define QPID_STORE_MSSQL_EXCEPTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+#include <comdef.h>
+#include <qpid/store/StorageProvider.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+class Exception : public qpid::store::StorageProvider::Exception
+{
+protected:
+ std::string text;
+public:
+ Exception(const std::string& _text) : text(_text) {}
+ virtual ~Exception() {}
+ virtual const char* what() const throw() { return text.c_str(); }
+};
+
+class ADOException : public Exception
+{
+public:
+ ADOException(const std::string& _text,
+ _com_error &e,
+ const std::string& providerErrors = "")
+ : Exception(_text) {
+ text += ": ";
+ text += e.ErrorMessage();
+ IErrorInfo *i = e.ErrorInfo();
+ if (i != 0) {
+ text += ": ";
+ _bstr_t wmsg = e.Description();
+ text += (const char *)wmsg;
+ i->Release();
+ }
+ if (providerErrors.length() > 0)
+ text += providerErrors;
+ }
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_EXCEPTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp b/qpid/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
new file mode 100644
index 0000000000..7f22db3d02
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
@@ -0,0 +1,1305 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string>
+#include <windows.h>
+#include <qpid/broker/RecoverableQueue.h>
+#include <qpid/log/Statement.h>
+#include <qpid/store/MessageStorePlugin.h>
+#include <qpid/store/StorageProvider.h>
+#include "AmqpTransaction.h"
+#include "BlobAdapter.h"
+#include "BlobRecordset.h"
+#include "BindingRecordset.h"
+#include "MessageMapRecordset.h"
+#include "MessageRecordset.h"
+#include "TplRecordset.h"
+#include "DatabaseConnection.h"
+#include "Exception.h"
+#include "State.h"
+#include "VariantHelper.h"
+
+// Bring in ADO 2.8 (yes, I know it says "15", but that's it...)
+#import "C:\Program Files\Common Files\System\ado\msado15.dll" \
+ no_namespace rename("EOF", "EndOfFile")
+#include <comdef.h>
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+
+// Table names
+const std::string TblBinding("tblBinding");
+const std::string TblConfig("tblConfig");
+const std::string TblExchange("tblExchange");
+const std::string TblMessage("tblMessage");
+const std::string TblMessageMap("tblMessageMap");
+const std::string TblQueue("tblQueue");
+const std::string TblTpl("tblTPL");
+}
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class MSSqlProvider
+ *
+ * Implements a qpid::store::StorageProvider that uses Microsoft SQL Server as
+ * the backend data store for Qpid.
+ */
+class MSSqlProvider : public qpid::store::StorageProvider
+{
+protected:
+ void finalizeMe();
+
+ void dump();
+
+public:
+ MSSqlProvider();
+ ~MSSqlProvider();
+
+ virtual qpid::Options* getOptions() { return &options; }
+
+ virtual void earlyInitialize (Plugin::Target& target);
+ virtual void initialize(Plugin::Target& target);
+
+ /**
+ * Receive notification that this provider is the one that will actively
+ * handle provider storage for the target. If the provider is to be used,
+ * this method will be called after earlyInitialize() and before any
+ * recovery operations (recovery, in turn, precedes call to initialize()).
+ */
+ virtual void activate(MessageStorePlugin &store);
+
+ /**
+ * @name Methods inherited from qpid::broker::MessageStore
+ */
+ //@{
+ /**
+ * If called after init() but before recovery, will discard the database
+ * and reinitialize using an empty store dir. If @a pushDownStoreFiles
+ * is true, the content of the store dir will be moved to a backup dir
+ * inside the store dir. This is used when cluster nodes recover and must
+ * get thier content from a cluster sync rather than directly fromt the
+ * store.
+ *
+ * @param pushDownStoreFiles If true, will move content of the store dir
+ * into a subdir, leaving the store dir
+ * otherwise empty.
+ */
+ virtual void truncateInit(const bool pushDownStoreFiles = false);
+
+ /**
+ * Record the existence of a durable queue
+ */
+ virtual void create(PersistableQueue& queue,
+ const qpid::framing::FieldTable& args);
+ /**
+ * Destroy a durable queue
+ */
+ virtual void destroy(PersistableQueue& queue);
+
+ /**
+ * Record the existence of a durable exchange
+ */
+ virtual void create(const PersistableExchange& exchange,
+ const qpid::framing::FieldTable& args);
+ /**
+ * Destroy a durable exchange
+ */
+ virtual void destroy(const PersistableExchange& exchange);
+
+ /**
+ * Record a binding
+ */
+ virtual void bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ /**
+ * Forget a binding
+ */
+ virtual void unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args);
+
+ /**
+ * Record generic durable configuration
+ */
+ virtual void create(const PersistableConfig& config);
+
+ /**
+ * Destroy generic durable configuration
+ */
+ virtual void destroy(const PersistableConfig& config);
+
+ /**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+ virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg);
+
+ /**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+ virtual void destroy(PersistableMessage& msg);
+
+ /**
+ * Appends content to a previously staged message
+ */
+ virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data);
+
+ /**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+ virtual void loadContent(const qpid::broker::PersistableQueue& queue,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ /**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+
+ /**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * Note: that this is async so the return of the function does
+ * not mean the opperation is complete.
+ *
+ * @param msg the message to dequeue
+ * @param queue the name of the queue from which it is to be dequeued
+ * @param xid (a pointer to) an identifier of the
+ * distributed transaction in which the operation takes
+ * place or null for 'local' transactions
+ */
+ virtual void dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue);
+
+ /**
+ * Flushes all async messages to disk for the specified queue
+ *
+ * Note: this is a no-op for this provider.
+ *
+ * @param queue the name of the queue from which it is to be dequeued
+ */
+ virtual void flush(const PersistableQueue& queue) {};
+
+ /**
+ * Returns the number of outstanding AIO's for a given queue
+ *
+ * If 0, than all the enqueue / dequeues have been stored
+ * to disk
+ *
+ * @param queue the name of the queue to check for outstanding AIO
+ */
+ virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue)
+ {return 0;}
+ //@}
+
+ /**
+ * @name Methods inherited from qpid::broker::TransactionalStore
+ */
+ //@{
+ virtual std::auto_ptr<qpid::broker::TransactionContext> begin();
+ virtual std::auto_ptr<qpid::broker::TPCTransactionContext> begin(const std::string& xid);
+ virtual void prepare(qpid::broker::TPCTransactionContext& txn);
+ virtual void commit(qpid::broker::TransactionContext& txn);
+ virtual void abort(qpid::broker::TransactionContext& txn);
+ virtual void collectPreparedXids(std::set<std::string>& xids);
+ //@}
+
+ virtual void recoverConfigs(qpid::broker::RecoveryManager& recoverer);
+ virtual void recoverExchanges(qpid::broker::RecoveryManager& recoverer,
+ ExchangeMap& exchangeMap);
+ virtual void recoverQueues(qpid::broker::RecoveryManager& recoverer,
+ QueueMap& queueMap);
+ virtual void recoverBindings(qpid::broker::RecoveryManager& recoverer,
+ const ExchangeMap& exchangeMap,
+ const QueueMap& queueMap);
+ virtual void recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap);
+ virtual void recoverTransactions(qpid::broker::RecoveryManager& recoverer,
+ PreparedTransactionMap& dtxMap);
+
+private:
+ struct ProviderOptions : public qpid::Options
+ {
+ std::string connectString;
+ std::string catalogName;
+
+ ProviderOptions(const std::string &name)
+ : qpid::Options(name),
+ catalogName("QpidStore")
+ {
+ const enum { NAMELEN = MAX_COMPUTERNAME_LENGTH + 1 };
+ TCHAR myName[NAMELEN];
+ DWORD myNameLen = NAMELEN;
+ GetComputerName(myName, &myNameLen);
+ connectString = "Data Source=";
+ connectString += myName;
+ connectString += "\\SQLEXPRESS;Integrated Security=SSPI";
+ addOptions()
+ ("connect",
+ qpid::optValue(connectString, "STRING"),
+ "Connection string for the database to use. Will prepend "
+ "Provider=SQLOLEDB;")
+ ("catalog",
+ qpid::optValue(catalogName, "DB NAME"),
+ "Catalog (database) name")
+ ;
+ }
+ };
+ ProviderOptions options;
+
+ // Each thread has a separate connection to the database and also needs
+ // to manage its COM initialize/finalize individually. This is done by
+ // keeping a thread-specific State.
+ boost::thread_specific_ptr<State> dbState;
+
+ State *initState();
+ DatabaseConnection *initConnection(void);
+ void createDb(DatabaseConnection *db, const std::string &name);
+};
+
+static MSSqlProvider static_instance_registers_plugin;
+
+void
+MSSqlProvider::finalizeMe()
+{
+ dbState.reset();
+}
+
+MSSqlProvider::MSSqlProvider()
+ : options("MS SQL Provider options")
+{
+}
+
+MSSqlProvider::~MSSqlProvider()
+{
+}
+
+void
+MSSqlProvider::earlyInitialize(Plugin::Target &target)
+{
+ MessageStorePlugin *store = dynamic_cast<MessageStorePlugin *>(&target);
+ if (store) {
+ // If the database init fails, report it and don't register; give
+ // the rest of the broker a chance to run.
+ //
+ // Don't try to initConnection() since that will fail if the
+ // database doesn't exist. Instead, try to open a connection without
+ // a database name, then search for the database. There's still a
+ // chance this provider won't be selected for the store too, so be
+ // be sure to close the database connection before return to avoid
+ // leaving a connection up that will not be used.
+ try {
+ initState(); // This initializes COM
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection());
+ db->open(options.connectString, "");
+ _ConnectionPtr conn(*db);
+ _RecordsetPtr pCatalogs = NULL;
+ VariantHelper<std::string> catalogName(options.catalogName);
+ pCatalogs = conn->OpenSchema(adSchemaCatalogs, catalogName);
+ if (pCatalogs->EndOfFile) {
+ // Database doesn't exist; create it
+ QPID_LOG(notice,
+ "MSSQL: Creating database " + options.catalogName);
+ createDb(db.get(), options.catalogName);
+ }
+ else {
+ QPID_LOG(notice,
+ "MSSQL: Database located: " + options.catalogName);
+ }
+ if (pCatalogs) {
+ if (pCatalogs->State == adStateOpen)
+ pCatalogs->Close();
+ pCatalogs = 0;
+ }
+ db->close();
+ store->providerAvailable("MSSQL", this);
+ }
+ catch (qpid::Exception &e) {
+ QPID_LOG(error, e.what());
+ return;
+ }
+ store->addFinalizer(boost::bind(&MSSqlProvider::finalizeMe, this));
+ }
+}
+
+void
+MSSqlProvider::initialize(Plugin::Target& target)
+{
+}
+
+void
+MSSqlProvider::activate(MessageStorePlugin &store)
+{
+ QPID_LOG(info, "MS SQL Provider is up");
+}
+
+void
+MSSqlProvider::truncateInit(const bool pushDownStoreFiles)
+{
+}
+
+void
+MSSqlProvider::create(PersistableQueue& queue,
+ const qpid::framing::FieldTable& /*args needed for jrnl*/)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ try {
+ db->beginTransaction();
+ rsQueues.open(db, TblQueue);
+ rsQueues.add(queue);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating queue " + queue.getName(), e, errs);
+ }
+}
+
+/**
+ * Destroy a durable queue
+ */
+void
+MSSqlProvider::destroy(PersistableQueue& queue)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsQueues;
+ BindingRecordset rsBindings;
+ MessageRecordset rsMessages;
+ MessageMapRecordset rsMessageMaps;
+ try {
+ db->beginTransaction();
+ rsQueues.open(db, TblQueue);
+ rsBindings.open(db, TblBinding);
+ rsMessages.open(db, TblMessage);
+ rsMessageMaps.open(db, TblMessageMap);
+ // Remove bindings first; the queue IDs can't be ripped out from
+ // under the references in the bindings table. Then remove the
+ // message->queue entries for the queue, also because the queue can't
+ // be deleted while there are references to it. If there are messages
+ // orphaned by removing the queue references, they're deleted by
+ // a trigger on the tblMessageMap table. Lastly, the queue record
+ // can be removed.
+ rsBindings.removeForQueue(queue.getPersistenceId());
+ rsMessageMaps.removeForQueue(queue.getPersistenceId());
+ rsQueues.remove(queue);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting queue " + queue.getName(), e, errs);
+ }
+}
+
+/**
+ * Record the existence of a durable exchange
+ */
+void
+MSSqlProvider::create(const PersistableExchange& exchange,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsExchanges;
+ try {
+ db->beginTransaction();
+ rsExchanges.open(db, TblExchange);
+ rsExchanges.add(exchange);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating exchange " + exchange.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Destroy a durable exchange
+ */
+void
+MSSqlProvider::destroy(const PersistableExchange& exchange)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsExchanges;
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsExchanges.open(db, TblExchange);
+ rsBindings.open(db, TblBinding);
+ // Remove bindings first; the exchange IDs can't be ripped out from
+ // under the references in the bindings table.
+ rsBindings.removeForExchange(exchange.getPersistenceId());
+ rsExchanges.remove(exchange);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting exchange " + exchange.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Record a binding
+ */
+void
+MSSqlProvider::bind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsBindings.open(db, TblBinding);
+ rsBindings.add(exchange.getPersistenceId(),
+ queue.getPersistenceId(),
+ key,
+ args);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error binding exchange " + exchange.getName() +
+ " to queue " + queue.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Forget a binding
+ */
+void
+MSSqlProvider::unbind(const PersistableExchange& exchange,
+ const PersistableQueue& queue,
+ const std::string& key,
+ const qpid::framing::FieldTable& args)
+{
+ DatabaseConnection *db = initConnection();
+ BindingRecordset rsBindings;
+ try {
+ db->beginTransaction();
+ rsBindings.open(db, TblBinding);
+ rsBindings.remove(exchange.getPersistenceId(),
+ queue.getPersistenceId(),
+ key,
+ args);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error unbinding exchange " + exchange.getName() +
+ " from queue " + queue.getName(),
+ e,
+ errs);
+ }
+}
+
+/**
+ * Record generic durable configuration
+ */
+void
+MSSqlProvider::create(const PersistableConfig& config)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsConfigs;
+ try {
+ db->beginTransaction();
+ rsConfigs.open(db, TblConfig);
+ rsConfigs.add(config);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error creating config " + config.getName(), e, errs);
+ }
+}
+
+/**
+ * Destroy generic durable configuration
+ */
+void
+MSSqlProvider::destroy(const PersistableConfig& config)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsConfigs;
+ try {
+ db->beginTransaction();
+ rsConfigs.open(db, TblConfig);
+ rsConfigs.remove(config);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting config " + config.getName(), e, errs);
+ }
+}
+
+/**
+ * Stores a messages before it has been enqueued
+ * (enqueueing automatically stores the message so this is
+ * only required if storage is required prior to that
+ * point). If the message has not yet been stored it will
+ * store the headers as well as any content passed in. A
+ * persistence id will be set on the message which can be
+ * used to load the content or to append to it.
+ */
+void
+MSSqlProvider::stage(const boost::intrusive_ptr<PersistableMessage>& msg)
+{
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.add(msg);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error staging message", e, errs);
+ }
+}
+
+/**
+ * Destroys a previously staged message. This only needs
+ * to be called if the message is never enqueued. (Once
+ * enqueued, deletion will be automatic when the message
+ * is dequeued from all queues it was enqueued onto).
+ */
+void
+MSSqlProvider::destroy(PersistableMessage& msg)
+{
+ DatabaseConnection *db = initConnection();
+ BlobRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.remove(msg);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error deleting message", e, errs);
+ }
+}
+
+/**
+ * Appends content to a previously staged message
+ */
+void
+MSSqlProvider::appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const std::string& data)
+{
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ try {
+ db->beginTransaction();
+ rsMessages.open(db, TblMessage);
+ rsMessages.append(msg, data);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error appending to message", e, errs);
+ }
+}
+
+/**
+ * Loads (a section) of content data for the specified
+ * message (previously stored through a call to stage or
+ * enqueue) into data. The offset refers to the content
+ * only (i.e. an offset of 0 implies that the start of the
+ * content should be loaded, not the headers or related
+ * meta-data).
+ */
+void
+MSSqlProvider::loadContent(const qpid::broker::PersistableQueue& /*queue*/,
+ const boost::intrusive_ptr<const PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+ // SQL store keeps all messages in one table, so we don't need the
+ // queue reference.
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ try {
+ rsMessages.open(db, TblMessage);
+ rsMessages.loadContent(msg, data, offset, length);
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ throw ADOException("Error loading message content", e, errs);
+ }
+}
+
+/**
+ * Enqueues a message, storing the message if it has not
+ * been previously stored and recording that the given
+ * message is on the given queue.
+ *
+ * @param ctxt The transaction context under which this enqueue happens.
+ * @param msg The message to enqueue
+ * @param queue the name of the queue onto which it is to be enqueued
+ */
+void
+MSSqlProvider::enqueue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ // If this enqueue is in the context of a transaction, use the specified
+ // transaction to nest a new transaction for this operation. However, if
+ // this is not in the context of a transaction, then just use the thread's
+ // DatabaseConnection with a ADO transaction.
+ DatabaseConnection *db = 0;
+ std::string xid;
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (ctxt);
+ if (atxn == 0) {
+ db = initConnection();
+ db->beginTransaction();
+ }
+ else {
+ (void)initState(); // Ensure this thread is initialized
+ // It's a transactional enqueue; if it's TPC, grab the xid.
+ AmqpTPCTransaction *tpcTxn = dynamic_cast<AmqpTPCTransaction*> (ctxt);
+ if (tpcTxn)
+ xid = tpcTxn->getXid();
+ db = atxn->dbConn();
+ try {
+ atxn->sqlBegin();
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error queuing message", e, db->getErrors());
+ }
+ }
+
+ MessageRecordset rsMessages;
+ MessageMapRecordset rsMap;
+ try {
+ if (msg->getPersistenceId() == 0) { // Message itself not yet saved
+ rsMessages.open(db, TblMessage);
+ rsMessages.add(msg);
+ }
+ rsMap.open(db, TblMessageMap);
+ rsMap.add(msg->getPersistenceId(), queue.getPersistenceId(), xid);
+ if (atxn)
+ atxn->sqlCommit();
+ else
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ if (atxn)
+ atxn->sqlAbort();
+ else
+ db->rollbackTransaction();
+ throw ADOException("Error queuing message", e, errs);
+ }
+ msg->enqueueComplete();
+}
+
+/**
+ * Dequeues a message, recording that the given message is
+ * no longer on the given queue and deleting the message
+ * if it is no longer on any other queue.
+ *
+ * @param ctxt The transaction context under which this dequeue happens.
+ * @param msg The message to dequeue
+ * @param queue The queue from which it is to be dequeued
+ */
+void
+MSSqlProvider::dequeue(qpid::broker::TransactionContext* ctxt,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+{
+ // If this dequeue is in the context of a transaction, use the specified
+ // transaction to nest a new transaction for this operation. However, if
+ // this is not in the context of a transaction, then just use the thread's
+ // DatabaseConnection with a ADO transaction.
+ DatabaseConnection *db = 0;
+ std::string xid;
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (ctxt);
+ if (atxn == 0) {
+ db = initConnection();
+ db->beginTransaction();
+ }
+ else {
+ (void)initState(); // Ensure this thread is initialized
+ // It's a transactional dequeue; if it's TPC, grab the xid.
+ AmqpTPCTransaction *tpcTxn = dynamic_cast<AmqpTPCTransaction*> (ctxt);
+ if (tpcTxn)
+ xid = tpcTxn->getXid();
+ db = atxn->dbConn();
+ try {
+ atxn->sqlBegin();
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error queuing message", e, db->getErrors());
+ }
+ }
+
+ MessageMapRecordset rsMap;
+ try {
+ rsMap.open(db, TblMessageMap);
+ // TPC dequeues are just marked pending and will actually be removed
+ // when the transaction commits; Single-phase dequeues are removed
+ // now, relying on the SQL transaction to put it back if the
+ // transaction doesn't commit.
+ if (!xid.empty()) {
+ rsMap.pendingRemove(msg->getPersistenceId(),
+ queue.getPersistenceId(),
+ xid);
+ }
+ else {
+ rsMap.remove(msg->getPersistenceId(),
+ queue.getPersistenceId());
+ }
+ if (atxn)
+ atxn->sqlCommit();
+ else
+ db->commitTransaction();
+ }
+ catch(ms_sql::Exception&) {
+ if (atxn)
+ atxn->sqlAbort();
+ else
+ db->rollbackTransaction();
+ throw;
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ if (atxn)
+ atxn->sqlAbort();
+ else
+ db->rollbackTransaction();
+ throw ADOException("Error dequeuing message", e, errs);
+ }
+ msg->dequeueComplete();
+}
+
+std::auto_ptr<qpid::broker::TransactionContext>
+MSSqlProvider::begin()
+{
+ (void)initState(); // Ensure this thread is initialized
+
+ // Transactions are associated with the Connection, so this transaction
+ // context needs its own connection. At the time of writing, single-phase
+ // transactions are dealt with completely on one thread, so we really
+ // could just use the thread-specific DatabaseConnection for this.
+ // However, that would introduce an ugly, hidden coupling, so play
+ // it safe and handle this just like a TPC transaction, which actually
+ // can be prepared and committed/aborted from different threads,
+ // making it a bad idea to try using the thread-local DatabaseConnection.
+ boost::shared_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ std::auto_ptr<AmqpTransaction> tx(new AmqpTransaction(db));
+ tx->sqlBegin();
+ std::auto_ptr<qpid::broker::TransactionContext> tc(tx);
+ return tc;
+}
+
+std::auto_ptr<qpid::broker::TPCTransactionContext>
+MSSqlProvider::begin(const std::string& xid)
+{
+ (void)initState(); // Ensure this thread is initialized
+ boost::shared_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ std::auto_ptr<AmqpTPCTransaction> tx(new AmqpTPCTransaction(db, xid));
+ tx->sqlBegin();
+
+ TplRecordset rsTpl;
+ try {
+ tx->sqlBegin();
+ rsTpl.open(db.get(), TblTpl);
+ rsTpl.add(xid);
+ tx->sqlCommit();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ tx->sqlAbort();
+ throw ADOException("Error adding TPL record", e, errs);
+ }
+
+ std::auto_ptr<qpid::broker::TPCTransactionContext> tc(tx);
+ return tc;
+}
+
+void
+MSSqlProvider::prepare(qpid::broker::TPCTransactionContext& txn)
+{
+ // Commit all the marked-up enqueue/dequeue ops and the TPL record.
+ // On commit/rollback the TPL will be removed and the TPL markups
+ // on the message map will be cleaned up as well.
+ (void)initState(); // Ensure this thread is initialized
+ AmqpTPCTransaction *atxn = dynamic_cast<AmqpTPCTransaction*> (&txn);
+ if (atxn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ try {
+ atxn->sqlCommit();
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error preparing", e, atxn->dbConn()->getErrors());
+ }
+ atxn->setPrepared();
+}
+
+void
+MSSqlProvider::commit(qpid::broker::TransactionContext& txn)
+{
+ (void)initState(); // Ensure this thread is initialized
+ /*
+ * One-phase transactions simply commit the outer SQL transaction
+ * that was begun on begin(). Two-phase transactions are different -
+ * the SQL transaction started on begin() was committed on prepare()
+ * so all the SQL records reflecting the enqueue/dequeue actions for
+ * the transaction are recorded but with xid markups on them to reflect
+ * that they are prepared but not committed. Now go back and remove
+ * the markups, deleting those marked for removal.
+ */
+ AmqpTPCTransaction *p2txn = dynamic_cast<AmqpTPCTransaction*> (&txn);
+ if (p2txn == 0) {
+ AmqpTransaction *p1txn = dynamic_cast<AmqpTransaction*> (&txn);
+ if (p1txn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ p1txn->sqlCommit();
+ return;
+ }
+
+ DatabaseConnection *db(p2txn->dbConn());
+ TplRecordset rsTpl;
+ MessageMapRecordset rsMessageMap;
+ try {
+ db->beginTransaction();
+ rsTpl.open(db, TblTpl);
+ rsMessageMap.open(db, TblMessageMap);
+ rsMessageMap.commitPrepared(p2txn->getXid());
+ rsTpl.remove(p2txn->getXid());
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error committing transaction", e, errs);
+ }
+}
+
+void
+MSSqlProvider::abort(qpid::broker::TransactionContext& txn)
+{
+ (void)initState(); // Ensure this thread is initialized
+ /*
+ * One-phase and non-prepared two-phase transactions simply abort
+ * the outer SQL transaction that was begun on begin(). However, prepared
+ * two-phase transactions are different - the SQL transaction started
+ * on begin() was committed on prepare() so all the SQL records
+ * reflecting the enqueue/dequeue actions for the transaction are
+ * recorded but with xid markups on them to reflect that they are
+ * prepared but not committed. Now go back and remove the markups,
+ * deleting those marked for addition.
+ */
+ AmqpTPCTransaction *p2txn = dynamic_cast<AmqpTPCTransaction*> (&txn);
+ if (p2txn == 0 || !p2txn->isPrepared()) {
+ AmqpTransaction *p1txn = dynamic_cast<AmqpTransaction*> (&txn);
+ if (p1txn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ p1txn->sqlAbort();
+ return;
+ }
+
+ DatabaseConnection *db(p2txn->dbConn());
+ TplRecordset rsTpl;
+ MessageMapRecordset rsMessageMap;
+ try {
+ db->beginTransaction();
+ rsTpl.open(db, TblTpl);
+ rsMessageMap.open(db, TblMessageMap);
+ rsMessageMap.abortPrepared(p2txn->getXid());
+ rsTpl.remove(p2txn->getXid());
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ std::string errs = db->getErrors();
+ db->rollbackTransaction();
+ throw ADOException("Error committing transaction", e, errs);
+ }
+
+
+ (void)initState(); // Ensure this thread is initialized
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (&txn);
+ if (atxn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ atxn->sqlAbort();
+}
+
+void
+MSSqlProvider::collectPreparedXids(std::set<std::string>& xids)
+{
+ DatabaseConnection *db = initConnection();
+ try {
+ TplRecordset rsTpl;
+ rsTpl.open(db, TblTpl);
+ rsTpl.recover(xids);
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error reading TPL", e, db->getErrors());
+ }
+}
+
+// @TODO Much of this recovery code is way too similar... refactor to
+// a recover template method on BlobRecordset.
+
+void
+MSSqlProvider::recoverConfigs(qpid::broker::RecoveryManager& recoverer)
+{
+ DatabaseConnection *db = 0;
+ try {
+ db = initConnection();
+ BlobRecordset rsConfigs;
+ rsConfigs.open(db, TblConfig);
+ _RecordsetPtr p = (_RecordsetPtr)rsConfigs;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Config instance and reset its ID.
+ broker::RecoverableConfig::shared_ptr config =
+ recoverer.recoverConfig(blob);
+ config->setPersistenceId(id);
+ p->MoveNext();
+ }
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering configs",
+ e,
+ db ? db->getErrors() : "");
+ }
+}
+
+void
+MSSqlProvider::recoverExchanges(qpid::broker::RecoveryManager& recoverer,
+ ExchangeMap& exchangeMap)
+{
+ DatabaseConnection *db = 0;
+ try {
+ db = initConnection();
+ BlobRecordset rsExchanges;
+ rsExchanges.open(db, TblExchange);
+ _RecordsetPtr p = (_RecordsetPtr)rsExchanges;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Exchange instance, reset its ID, and remember the
+ // ones restored for matching up when recovering bindings.
+ broker::RecoverableExchange::shared_ptr exchange =
+ recoverer.recoverExchange(blob);
+ exchange->setPersistenceId(id);
+ exchangeMap[id] = exchange;
+ p->MoveNext();
+ }
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering exchanges",
+ e,
+ db ? db->getErrors() : "");
+ }
+}
+
+void
+MSSqlProvider::recoverQueues(qpid::broker::RecoveryManager& recoverer,
+ QueueMap& queueMap)
+{
+ DatabaseConnection *db = 0;
+ try {
+ db = initConnection();
+ BlobRecordset rsQueues;
+ rsQueues.open(db, TblQueue);
+ _RecordsetPtr p = (_RecordsetPtr)rsQueues;
+ if (p->BOF && p->EndOfFile)
+ return; // Nothing to do
+ p->MoveFirst();
+ while (!p->EndOfFile) {
+ uint64_t id = p->Fields->Item["persistenceId"]->Value;
+ long blobSize = p->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = p->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ // Recreate the Queue instance and reset its ID.
+ broker::RecoverableQueue::shared_ptr queue =
+ recoverer.recoverQueue(blob);
+ queue->setPersistenceId(id);
+ queueMap[id] = queue;
+ p->MoveNext();
+ }
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering queues",
+ e,
+ db ? db->getErrors() : "");
+ }
+}
+
+void
+MSSqlProvider::recoverBindings(qpid::broker::RecoveryManager& recoverer,
+ const ExchangeMap& exchangeMap,
+ const QueueMap& queueMap)
+{
+ DatabaseConnection *db = 0;
+ try {
+ db = initConnection();
+ BindingRecordset rsBindings;
+ rsBindings.open(db, TblBinding);
+ rsBindings.recover(recoverer, exchangeMap, queueMap);
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering bindings",
+ e,
+ db ? db->getErrors() : "");
+ }
+}
+
+void
+MSSqlProvider::recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap)
+{
+ DatabaseConnection *db = 0;
+ try {
+ db = initConnection();
+ MessageRecordset rsMessages;
+ rsMessages.open(db, TblMessage);
+ rsMessages.recover(recoverer, messageMap);
+
+ MessageMapRecordset rsMessageMaps;
+ rsMessageMaps.open(db, TblMessageMap);
+ rsMessageMaps.recover(messageQueueMap);
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering messages",
+ e,
+ db ? db->getErrors() : "");
+ }
+}
+
+void
+MSSqlProvider::recoverTransactions(qpid::broker::RecoveryManager& recoverer,
+ PreparedTransactionMap& dtxMap)
+{
+ DatabaseConnection *db = initConnection();
+ std::set<std::string> xids;
+ try {
+ TplRecordset rsTpl;
+ rsTpl.open(db, TblTpl);
+ rsTpl.recover(xids);
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recovering TPL records", e, db->getErrors());
+ }
+
+ try {
+ // Rebuild the needed RecoverableTransactions.
+ for (std::set<std::string>::const_iterator iXid = xids.begin();
+ iXid != xids.end();
+ ++iXid) {
+ boost::shared_ptr<DatabaseConnection> dbX(new DatabaseConnection);
+ dbX->open(options.connectString, options.catalogName);
+ std::auto_ptr<AmqpTPCTransaction> tx(new AmqpTPCTransaction(dbX,
+ *iXid));
+ tx->setPrepared();
+ std::auto_ptr<qpid::broker::TPCTransactionContext> tc(tx);
+ dtxMap[*iXid] = recoverer.recoverTransaction(*iXid, tc);
+ }
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error recreating dtx connection", e);
+ }
+}
+
+////////////// Internal Methods
+
+State *
+MSSqlProvider::initState()
+{
+ State *state = dbState.get(); // See if thread has initialized
+ if (!state) {
+ state = new State;
+ dbState.reset(state);
+ }
+ return state;
+}
+
+DatabaseConnection *
+MSSqlProvider::initConnection(void)
+{
+ State *state = initState();
+ if (state->dbConn != 0)
+ return state->dbConn; // And the DatabaseConnection is set up too
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ state->dbConn = db.release();
+ return state->dbConn;
+}
+
+void
+MSSqlProvider::createDb(DatabaseConnection *db, const std::string &name)
+{
+ const std::string dbCmd = "CREATE DATABASE " + name;
+ const std::string useCmd = "USE " + name;
+ const std::string tableCmd = "CREATE TABLE ";
+ const std::string colSpecs =
+ " (persistenceId bigint PRIMARY KEY NOT NULL IDENTITY(1,1),"
+ " fieldTableBlob varbinary(MAX) NOT NULL)";
+ const std::string bindingSpecs =
+ " (exchangeId bigint REFERENCES tblExchange(persistenceId) NOT NULL,"
+ " queueId bigint REFERENCES tblQueue(persistenceId) NOT NULL,"
+ " routingKey varchar(255),"
+ " fieldTableBlob varbinary(MAX))";
+ const std::string messageMapSpecs =
+ " (messageId bigint REFERENCES tblMessage(persistenceId) NOT NULL,"
+ " queueId bigint REFERENCES tblQueue(persistenceId) NOT NULL,"
+ " prepareStatus tinyint CHECK (prepareStatus IS NULL OR "
+ " prepareStatus IN (1, 2)),"
+ " xid varbinary(512) REFERENCES tblTPL(xid)"
+ " CONSTRAINT CK_NoDups UNIQUE NONCLUSTERED (messageId, queueId) )";
+ const std::string tplSpecs = " (xid varbinary(512) PRIMARY KEY NOT NULL)";
+ // SET NOCOUNT ON added to prevent extra result sets from
+ // interfering with SELECT statements. (Added by SQL Management)
+ const std::string removeUnrefMsgsTrigger =
+ "CREATE TRIGGER dbo.RemoveUnreferencedMessages "
+ "ON tblMessageMap AFTER DELETE AS BEGIN "
+ "SET NOCOUNT ON; "
+ "DELETE FROM tblMessage "
+ "WHERE tblMessage.persistenceId IN "
+ " (SELECT messageId FROM deleted) AND"
+ " NOT EXISTS(SELECT * FROM tblMessageMap"
+ " WHERE tblMessageMap.messageId IN"
+ " (SELECT messageId FROM deleted)) "
+ "END";
+
+ _variant_t unused;
+ _bstr_t dbStr = dbCmd.c_str();
+ _ConnectionPtr conn(*db);
+ try {
+ conn->Execute(dbStr, &unused, adExecuteNoRecords);
+ _bstr_t useStr = useCmd.c_str();
+ conn->Execute(useStr, &unused, adExecuteNoRecords);
+ std::string makeTable = tableCmd + TblQueue + colSpecs;
+ _bstr_t makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblExchange + colSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblConfig + colSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblMessage + colSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblBinding + bindingSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblTpl + tplSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ makeTable = tableCmd + TblMessageMap + messageMapSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ _bstr_t addTriggerStr = removeUnrefMsgsTrigger.c_str();
+ conn->Execute(addTriggerStr, &unused, adExecuteNoRecords);
+ }
+ catch(_com_error &e) {
+ throw ADOException("MSSQL can't create " + name, e, db->getErrors());
+ }
+}
+
+void
+MSSqlProvider::dump()
+{
+ // dump all db records to qpid_log
+ QPID_LOG(notice, "DB Dump: (not dumping anything)");
+ // rsQueues.dump();
+}
+
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp
new file mode 100644
index 0000000000..ce9fa61010
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp
@@ -0,0 +1,267 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+#include <qpid/store/StorageProvider.h>
+
+#include "MessageMapRecordset.h"
+#include "BlobEncoder.h"
+#include "DatabaseConnection.h"
+#include "Exception.h"
+#include "VariantHelper.h"
+
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+}
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+MessageMapRecordset::open(DatabaseConnection* conn, const std::string& table)
+{
+ init(conn, table);
+}
+
+void
+MessageMapRecordset::add(uint64_t messageId,
+ uint64_t queueId,
+ const std::string& xid)
+{
+ std::ostringstream command;
+ command << "INSERT INTO " << tableName
+ << " (messageId, queueId";
+ if (!xid.empty())
+ command << ", prepareStatus, xid";
+ command << ") VALUES (" << messageId << "," << queueId;
+ if (!xid.empty())
+ command << "," << PREPARE_ADD << ",?";
+ command << ")" << std::ends;
+
+ _CommandPtr cmd = NULL;
+ _ParameterPtr xidVal = NULL;
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.str().c_str();
+ cmd->CommandType = adCmdText;
+ if (!xid.empty()) {
+ TESTHR(xidVal.CreateInstance(__uuidof(Parameter)));
+ xidVal->Name = "@xid";
+ xidVal->Type = adVarBinary;
+ xidVal->Size = xid.length();
+ xidVal->Direction = adParamInput;
+ xidVal->Value = BlobEncoder(xid);
+ cmd->Parameters->Append(xidVal);
+ }
+ cmd->Execute(NULL, NULL, adCmdText | adExecuteNoRecords);
+}
+
+void
+MessageMapRecordset::remove(uint64_t messageId, uint64_t queueId)
+{
+ std::ostringstream command;
+ command << "DELETE FROM " << tableName
+ << " WHERE queueId = " << queueId
+ << " AND messageId = " << messageId << std::ends;
+ _CommandPtr cmd = NULL;
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.str().c_str();
+ cmd->CommandType = adCmdText;
+ _variant_t deletedRecords;
+ cmd->Execute(&deletedRecords, NULL, adCmdText | adExecuteNoRecords);
+ if ((long)deletedRecords == 0)
+ throw ms_sql::Exception("Message does not exist in queue mapping");
+ // Trigger on deleting the mapping takes care of deleting orphaned
+ // message record from tblMessage.
+}
+
+void
+MessageMapRecordset::pendingRemove(uint64_t messageId,
+ uint64_t queueId,
+ const std::string& xid)
+{
+ // Look up the mapping for the specified message and queue. There
+ // should be only one because of the uniqueness constraint in the
+ // SQL table. Update it to reflect it's pending delete with
+ // the specified xid.
+ std::ostringstream command;
+ command << "UPDATE " << tableName
+ << " SET prepareStatus=" << PREPARE_REMOVE
+ << " , xid=?"
+ << " WHERE queueId = " << queueId
+ << " AND messageId = " << messageId << std::ends;
+
+ _CommandPtr cmd = NULL;
+ _ParameterPtr xidVal = NULL;
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ TESTHR(xidVal.CreateInstance(__uuidof(Parameter)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.str().c_str();
+ cmd->CommandType = adCmdText;
+ xidVal->Name = "@xid";
+ xidVal->Type = adVarBinary;
+ xidVal->Size = xid.length();
+ xidVal->Direction = adParamInput;
+ xidVal->Value = BlobEncoder(xid);
+ cmd->Parameters->Append(xidVal);
+ cmd->Execute(NULL, NULL, adCmdText | adExecuteNoRecords);
+}
+
+void
+MessageMapRecordset::removeForQueue(uint64_t queueId)
+{
+ std::ostringstream command;
+ command << "DELETE FROM " << tableName
+ << " WHERE queueId = " << queueId << std::ends;
+ _CommandPtr cmd = NULL;
+
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.str().c_str();
+ cmd->CommandType = adCmdText;
+ cmd->Execute(NULL, NULL, adCmdText | adExecuteNoRecords);
+}
+
+void
+MessageMapRecordset::commitPrepared(const std::string& xid)
+{
+ // Find all the records for the specified xid. Records marked as adding
+ // are now permanent so remove the xid and prepareStatus. Records marked
+ // as removing are removed entirely.
+ openRs();
+ MessageMap m;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&m);
+ for (; !rs->EndOfFile; rs->MoveNext()) {
+ if (m.xidStatus != adFldOK)
+ continue;
+ const std::string x(m.xid, m.xidLength);
+ if (x != xid)
+ continue;
+ if (m.prepareStatus == PREPARE_REMOVE) {
+ rs->Delete(adAffectCurrent);
+ }
+ else {
+ _variant_t dbNull;
+ dbNull.ChangeType(VT_NULL);
+ rs->Fields->GetItem("prepareStatus")->Value = dbNull;
+ rs->Fields->GetItem("xid")->Value = dbNull;
+ }
+ rs->Update();
+ }
+ piAdoRecordBinding->Release();
+}
+
+void
+MessageMapRecordset::abortPrepared(const std::string& xid)
+{
+ // Find all the records for the specified xid. Records marked as adding
+ // need to be removed while records marked as removing are put back to
+ // no xid and no prepareStatus.
+ openRs();
+ MessageMap m;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&m);
+ for (; !rs->EndOfFile; rs->MoveNext()) {
+ if (m.xidStatus != adFldOK)
+ continue;
+ const std::string x(m.xid, m.xidLength);
+ if (x != xid)
+ continue;
+ if (m.prepareStatus == PREPARE_ADD) {
+ rs->Delete(adAffectCurrent);
+ }
+ else {
+ _variant_t dbNull;
+ dbNull.ChangeType(VT_NULL);
+ rs->Fields->GetItem("prepareStatus")->Value = dbNull;
+ rs->Fields->GetItem("xid")->Value = dbNull;
+ }
+ rs->Update();
+ }
+ piAdoRecordBinding->Release();
+}
+
+void
+MessageMapRecordset::recover(MessageQueueMap& msgMap)
+{
+ openRs();
+ if (rs->BOF && rs->EndOfFile)
+ return; // Nothing to do
+ rs->MoveFirst();
+ MessageMap b;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&b);
+ while (!rs->EndOfFile) {
+ qpid::store::QueueEntry entry(b.queueId);
+ if (b.xidStatus == adFldOK && b.xidLength > 0) {
+ entry.xid.assign(b.xid, b.xidLength);
+ entry.tplStatus =
+ b.prepareStatus == PREPARE_ADD ? QueueEntry::ADDING
+ : QueueEntry::REMOVING;
+ }
+ else {
+ entry.tplStatus = QueueEntry::NONE;
+ }
+ msgMap[b.messageId].push_back(entry);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+void
+MessageMapRecordset::dump()
+{
+ openRs();
+ Recordset::dump();
+ if (rs->EndOfFile && rs->BOF) // No records
+ return;
+ rs->MoveFirst();
+
+ MessageMap m;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&m);
+
+ while (!rs->EndOfFile) {
+ QPID_LOG(notice, "msg " << m.messageId << " on queue " << m.queueId);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h b/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h
new file mode 100644
index 0000000000..1b0c2f073e
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h
@@ -0,0 +1,100 @@
+#ifndef QPID_STORE_MSSQL_MESSAGEMAPRECORDSET_H
+#define QPID_STORE_MSSQL_MESSAGEMAPRECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <icrsint.h>
+#include "Recordset.h"
+#include <qpid/broker/RecoveryManager.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class MessageMapRecordset
+ *
+ * Class for the message map (message -> queue) records.
+ */
+class MessageMapRecordset : public Recordset {
+
+ // These values are defined in a constraint on the tblMessageMap table.
+ // the prepareStatus column can only be null, 1, or 2.
+ enum { PREPARE_ADD=1, PREPARE_REMOVE=2 };
+
+ class MessageMap : public CADORecordBinding {
+ BEGIN_ADO_BINDING(MessageMap)
+ ADO_FIXED_LENGTH_ENTRY2(1, adBigInt, messageId, FALSE)
+ ADO_FIXED_LENGTH_ENTRY2(2, adBigInt, queueId, FALSE)
+ ADO_FIXED_LENGTH_ENTRY2(3, adTinyInt, prepareStatus, FALSE)
+ ADO_VARIABLE_LENGTH_ENTRY(4, adVarBinary, xid, sizeof(xid),
+ xidStatus, xidLength, FALSE)
+ END_ADO_BINDING()
+
+ public:
+ uint64_t messageId;
+ uint64_t queueId;
+ uint8_t prepareStatus;
+ char xid[512];
+ int xidStatus;
+ uint32_t xidLength;
+ };
+
+ void selectOnXid(const std::string& xid);
+
+public:
+ virtual void open(DatabaseConnection* conn, const std::string& table);
+
+ // Add a new mapping
+ void add(uint64_t messageId,
+ uint64_t queueId,
+ const std::string& xid = "");
+
+ // Remove a specific mapping.
+ void remove(uint64_t messageId, uint64_t queueId);
+
+ // Mark the indicated message->queue entry pending removal. The entry
+ // for the mapping is updated to indicate pending removal with the
+ // specified xid.
+ void pendingRemove(uint64_t messageId,
+ uint64_t queueId,
+ const std::string& xid);
+
+ // Remove mappings for all messages on a specified queue.
+ void removeForQueue(uint64_t queueId);
+
+ // Commit records recorded as prepared.
+ void commitPrepared(const std::string& xid);
+
+ // Abort prepared changes.
+ void abortPrepared(const std::string& xid);
+
+ // Recover the mappings of message ID -> vector<queue ID>.
+ void recover(MessageQueueMap& msgMap);
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_MESSAGEMAPRECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
new file mode 100644
index 0000000000..b62a333df6
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+
+#include "MessageRecordset.h"
+#include "BlobAdapter.h"
+#include "BlobEncoder.h"
+#include "VariantHelper.h"
+
+#include <boost/intrusive_ptr.hpp>
+
+class qpid::broker::PersistableMessage;
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+MessageRecordset::add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg)
+{
+ BlobEncoder blob (msg); // Marshall headers and content to a blob
+ rs->AddNew();
+ rs->Fields->GetItem("fieldTableBlob")->AppendChunk(blob);
+ rs->Update();
+ uint64_t id = rs->Fields->Item["persistenceId"]->Value;
+ msg->setPersistenceId(id);
+}
+
+void
+MessageRecordset::append(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ const std::string& data)
+{
+ // Look up the message by its Id
+ std::ostringstream filter;
+ filter << "persistenceId = " << msg->getPersistenceId() << std::ends;
+ rs->PutFilter (VariantHelper<std::string>(filter.str()));
+ if (rs->RecordCount == 0) {
+ throw Exception("Can't append to message not stored in database");
+ }
+ BlobEncoder blob (data); // Marshall string data to a blob
+ rs->Fields->GetItem("fieldTableBlob")->AppendChunk(blob);
+ rs->Update();
+}
+
+void
+MessageRecordset::remove(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg)
+{
+ BlobRecordset::remove(msg->getPersistenceId());
+}
+
+void
+MessageRecordset::loadContent(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length)
+{
+ // Look up the message by its Id
+ std::ostringstream filter;
+ filter << "persistenceId = " << msg->getPersistenceId() << std::ends;
+ rs->PutFilter (VariantHelper<std::string>(filter.str()));
+ if (rs->RecordCount == 0) {
+ throw Exception("Can't load message not stored in database");
+ }
+
+ // NOTE! If this code needs to change, please verify the encoding
+ // code in BlobEncoder.
+ long blobSize = rs->Fields->Item["fieldTableBlob"]->ActualSize;
+ uint32_t headerSize;
+ const size_t headerFieldLength = sizeof(headerSize);
+ BlobAdapter blob(headerFieldLength);
+ blob =
+ rs->Fields->Item["fieldTableBlob"]->GetChunk((long)headerFieldLength);
+ headerSize = ((qpid::framing::Buffer&)blob).getLong();
+
+ // GetChunk always begins reading where the previous GetChunk left off,
+ // so we can't just tell it to ignore the header and read the data.
+ // So, read the header plus the offset, plus the desired data, then
+ // copy the desired data to the supplied string. If this ends up asking
+ // for more than is available in the field, reduce it to what's there.
+ long getSize = headerSize + offset + length;
+ if (getSize + (long)headerFieldLength > blobSize) {
+ size_t reduce = (getSize + headerFieldLength) - blobSize;
+ getSize -= reduce;
+ length -= reduce;
+ }
+ BlobAdapter header_plus(getSize);
+ header_plus = rs->Fields->Item["fieldTableBlob"]->GetChunk(getSize);
+ uint8_t *throw_away = new uint8_t[headerSize + offset];
+ ((qpid::framing::Buffer&)header_plus).getRawData(throw_away, headerSize + offset);
+ delete throw_away;
+ ((qpid::framing::Buffer&)header_plus).getRawData(data, length);
+}
+
+void
+MessageRecordset::recover(qpid::broker::RecoveryManager& recoverer,
+ std::map<uint64_t, broker::RecoverableMessage::shared_ptr>& messageMap)
+{
+ if (rs->BOF && rs->EndOfFile)
+ return; // Nothing to do
+ rs->MoveFirst();
+ Binding b;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&b);
+ while (!rs->EndOfFile) {
+ // The blob was written as normal, but with the header length
+ // prepended in a uint32_t. Due to message staging threshold
+ // limits, the header may be all that's read in; get it first,
+ // recover that message header, then see if the rest is needed.
+ //
+ // NOTE! If this code needs to change, please verify the encoding
+ // code in BlobEncoder.
+ long blobSize = rs->Fields->Item["fieldTableBlob"]->ActualSize;
+ uint32_t headerSize;
+ const size_t headerFieldLength = sizeof(headerSize);
+ BlobAdapter blob(headerFieldLength);
+ blob =
+ rs->Fields->Item["fieldTableBlob"]->GetChunk((long)headerFieldLength);
+ headerSize = ((qpid::framing::Buffer&)blob).getLong();
+ BlobAdapter header(headerSize);
+ header = rs->Fields->Item["fieldTableBlob"]->GetChunk(headerSize);
+ broker::RecoverableMessage::shared_ptr msg;
+ msg = recoverer.recoverMessage(header);
+ msg->setPersistenceId(b.messageId);
+ messageMap[b.messageId] = msg;
+
+ // Now, do we need the rest of the content?
+ long contentLength = blobSize - headerFieldLength - headerSize;
+ if (msg->loadContent(contentLength)) {
+ BlobAdapter content(contentLength);
+ content =
+ rs->Fields->Item["fieldTableBlob"]->GetChunk(contentLength);
+ msg->decodeContent(content);
+ }
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+void
+MessageRecordset::dump()
+{
+ Recordset::dump();
+ if (rs->EndOfFile && rs->BOF) // No records
+ return;
+ rs->MoveFirst();
+
+ Binding b;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&b);
+
+ while (VARIANT_FALSE == rs->EndOfFile) {
+ QPID_LOG(notice, "Msg " << b.messageId);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.h b/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.h
new file mode 100644
index 0000000000..698b2561fe
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/MessageRecordset.h
@@ -0,0 +1,85 @@
+#ifndef QPID_STORE_MSSQL_MESSAGERECORDSET_H
+#define QPID_STORE_MSSQL_MESSAGERECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <icrsint.h>
+#include "BlobRecordset.h"
+#include <qpid/broker/PersistableMessage.h>
+#include <qpid/broker/RecoveryManager.h>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class MessageRecordset
+ *
+ * Class for storing and recovering messages. Messages are primarily blobs
+ * and handled similarly. However, messages larger than the staging threshold
+ * are not contained completely in memory; they're left mostly in the store
+ * and the header is held in memory. So when the message "blob" is saved,
+ * an additional size-of-the-header field is prepended to the blob.
+ * On recovery, the size-of-the-header is used to get only what's needed
+ * until it's determined if the entire message is to be recovered to memory.
+ */
+class MessageRecordset : public BlobRecordset {
+ class Binding : public CADORecordBinding {
+ BEGIN_ADO_BINDING(Binding)
+ ADO_FIXED_LENGTH_ENTRY2(1, adBigInt, messageId, FALSE)
+ END_ADO_BINDING()
+
+ public:
+ uint64_t messageId;
+ };
+
+public:
+ // Store a message. Store the header size (4 bytes) then the regular
+ // blob comprising the message.
+ void add(const boost::intrusive_ptr<qpid::broker::PersistableMessage>& msg);
+
+ // Append additional content to an existing message.
+ void append(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ const std::string& data);
+
+ // Remove an existing message
+ void remove(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg);
+
+ // Load all or part of a stored message. This skips the header parts and
+ // loads content.
+ void loadContent(const boost::intrusive_ptr<const qpid::broker::PersistableMessage>& msg,
+ std::string& data,
+ uint64_t offset,
+ uint32_t length);
+
+ // Recover messages and save a map of those recovered.
+ void recover(qpid::broker::RecoveryManager& recoverer,
+ std::map<uint64_t, broker::RecoverableMessage::shared_ptr>& messageMap);
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_MESSAGERECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/Recordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/Recordset.cpp
new file mode 100644
index 0000000000..e706799951
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/Recordset.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+
+#include "Recordset.h"
+#include "BlobEncoder.h"
+#include "DatabaseConnection.h"
+#include "VariantHelper.h"
+
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+}
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+
+void
+Recordset::init(DatabaseConnection* conn, const std::string& table)
+{
+ dbConn = conn;
+ TESTHR(rs.CreateInstance(__uuidof(::Recordset)));
+ tableName = table;
+}
+
+void
+Recordset::openRs()
+{
+ // Client-side cursors needed to get access to newly added
+ // identity column immediately. Recordsets need this to get the
+ // persistence ID for the broker objects.
+ rs->CursorLocation = adUseClient;
+ _ConnectionPtr p = *dbConn;
+ rs->Open(tableName.c_str(),
+ _variant_t((IDispatch *)p, true),
+ adOpenStatic,
+ adLockOptimistic,
+ adCmdTable);
+}
+
+void
+Recordset::open(DatabaseConnection* conn, const std::string& table)
+{
+ init(conn, table);
+ openRs();
+}
+
+void
+Recordset::close()
+{
+ if (rs && rs->State == adStateOpen)
+ rs->Close();
+}
+
+void
+Recordset::requery()
+{
+ // Restore the recordset to reflect all current records.
+ rs->Filter = "";
+ rs->Requery(-1);
+}
+
+void
+Recordset::dump()
+{
+ long count = rs->RecordCount;
+ QPID_LOG(notice, "DB Dump: " + tableName <<
+ ": " << count << " records");
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/Recordset.h b/qpid/cpp/src/qpid/store/ms-sql/Recordset.h
new file mode 100644
index 0000000000..032b2bd434
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/Recordset.h
@@ -0,0 +1,75 @@
+#ifndef QPID_STORE_MSSQL_RECORDSET_H
+#define QPID_STORE_MSSQL_RECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// Bring in ADO 2.8 (yes, I know it says "15", but that's it...)
+#import "C:\Program Files\Common Files\System\ado\msado15.dll" \
+ no_namespace rename("EOF", "EndOfFile")
+#include <comdef.h>
+#include <comutil.h>
+#include <string>
+#if 0
+#include <utility>
+#endif
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+class DatabaseConnection;
+
+/**
+ * @class Recordset
+ *
+ * Represents an ADO Recordset, abstracting out the common operations needed
+ * on the common tables used that have 2 fields, persistence ID and blob.
+ */
+class Recordset {
+protected:
+ _RecordsetPtr rs;
+ DatabaseConnection* dbConn;
+ std::string tableName;
+
+ void init(DatabaseConnection* conn, const std::string& table);
+ void openRs();
+
+public:
+ Recordset() : rs(0), dbConn(0) {}
+ virtual ~Recordset() { close(); rs = 0; dbConn = 0; }
+
+ /**
+ * Default open() reads all records into the recordset.
+ */
+ virtual void open(DatabaseConnection* conn, const std::string& table);
+ void close();
+ void requery();
+ operator _RecordsetPtr () { return rs; }
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_RECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.cpp b/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.cpp
new file mode 100644
index 0000000000..6ad7725570
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.cpp
@@ -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.
+ *
+ */
+
+#include "SqlTransaction.h"
+#include "DatabaseConnection.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+SqlTransaction::SqlTransaction(const boost::shared_ptr<DatabaseConnection>& _db)
+ : db(_db), transDepth(0)
+{
+}
+
+SqlTransaction::~SqlTransaction()
+{
+ if (transDepth > 0)
+ this->abort();
+}
+
+void
+SqlTransaction::begin()
+{
+ _bstr_t beginCmd("BEGIN TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(beginCmd, NULL, adExecuteNoRecords);
+ ++transDepth;
+}
+
+void
+SqlTransaction::commit()
+{
+ if (transDepth > 0) {
+ _bstr_t commitCmd("COMMIT TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(commitCmd, NULL, adExecuteNoRecords);
+ --transDepth;
+ }
+}
+
+void
+SqlTransaction::abort()
+{
+ if (transDepth > 0) {
+ _bstr_t rollbackCmd("ROLLBACK TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(rollbackCmd, NULL, adExecuteNoRecords);
+ transDepth = 0;
+ }
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.h b/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.h
new file mode 100644
index 0000000000..8b5239b786
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/SqlTransaction.h
@@ -0,0 +1,67 @@
+#ifndef QPID_STORE_MSSQL_SQLTRANSACTION_H
+#define QPID_STORE_MSSQL_SQLTRANSACTION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+class DatabaseConnection;
+
+/**
+ * @class SqlTransaction
+ *
+ * Class representing an SQL transaction.
+ * Since ADO w/ SQLOLEDB can't do nested transaction via its BeginTrans(),
+ * et al, nested transactions are carried out with direct SQL commands.
+ * To ensure the state of this is known, keep track of how deeply the
+ * transactions are nested. This is more of a safety/sanity check since
+ * AMQP doesn't provide nested transactions.
+ */
+class SqlTransaction {
+
+ boost::shared_ptr<DatabaseConnection> db;
+
+ // Since ADO w/ SQLOLEDB can't do nested transaction via its BeginTrans(),
+ // et al, nested transactions are carried out with direct SQL commands.
+ // To ensure the state of this is known, keep track of how deeply the
+ // transactions are nested.
+ unsigned int transDepth;
+
+public:
+ SqlTransaction(const boost::shared_ptr<DatabaseConnection>& _db);
+ ~SqlTransaction();
+
+ DatabaseConnection *dbConn() { return db.get(); }
+
+ void begin();
+ void commit();
+ void abort();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_SQLTRANSACTION_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/State.cpp b/qpid/cpp/src/qpid/store/ms-sql/State.cpp
new file mode 100644
index 0000000000..720603dd11
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/State.cpp
@@ -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.
+ *
+ */
+
+#include "State.h"
+#include "DatabaseConnection.h"
+#include "Exception.h"
+#include <comdef.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+State::State() : dbConn(0)
+{
+ HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (hr != S_OK && hr != S_FALSE)
+ throw Exception("Error initializing COM");
+}
+
+State::~State()
+{
+ if (dbConn)
+ delete dbConn;
+ ::CoUninitialize();
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/State.h b/qpid/cpp/src/qpid/store/ms-sql/State.h
new file mode 100644
index 0000000000..6350bc5bd2
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/State.h
@@ -0,0 +1,52 @@
+#ifndef QPID_STORE_MSSQL_STATE_H
+#define QPID_STORE_MSSQL_STATE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+class DatabaseConnection;
+
+/**
+ * @struct State
+ *
+ * Represents a thread's state for accessing ADO and the database.
+ * Creating an instance of State initializes COM for this thread, and
+ * destroying it uninitializes COM. There's also a DatabaseConnection
+ * for this thread's default access to the database. More DatabaseConnections
+ * can always be created, but State has one that can always be used by
+ * the thread whose state is represented.
+ *
+ * This class is intended to be one-per-thread, so it should be accessed
+ * via thread-specific storage.
+ */
+struct State {
+ State();
+ ~State();
+ DatabaseConnection *dbConn;
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_STATE_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.cpp b/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.cpp
new file mode 100644
index 0000000000..1309d921a9
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.cpp
@@ -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.
+ *
+ */
+
+#include <string>
+#include <qpid/Exception.h>
+#include <qpid/log/Statement.h>
+
+#include "TplRecordset.h"
+#include "BlobEncoder.h"
+#include "DatabaseConnection.h"
+#include "VariantHelper.h"
+
+namespace {
+inline void TESTHR(HRESULT x) {if FAILED(x) _com_issue_error(x);};
+}
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+TplRecordset::open(DatabaseConnection* conn, const std::string& table)
+{
+ init(conn, table);
+ // Don't actually open until we know what to do. It's far easier and more
+ // efficient to simply do most of these TPL/xid ops in a single statement.
+}
+
+void
+TplRecordset::add(const std::string& xid)
+{
+ const std::string command =
+ "INSERT INTO " + tableName + " ( xid ) VALUES ( ? )";
+ _CommandPtr cmd = NULL;
+ _ParameterPtr xidVal = NULL;
+
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ TESTHR(xidVal.CreateInstance(__uuidof(Parameter)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.c_str();
+ cmd->CommandType = adCmdText;
+ xidVal->Name = "@xid";
+ xidVal->Type = adVarBinary;
+ xidVal->Size = xid.length();
+ xidVal->Direction = adParamInput;
+ xidVal->Value = BlobEncoder(xid);
+ cmd->Parameters->Append(xidVal);
+ cmd->Execute(NULL, NULL, adCmdText | adExecuteNoRecords);
+}
+
+void
+TplRecordset::remove(const std::string& xid)
+{
+ // Look up the item by its xid
+ const std::string command =
+ "DELETE FROM " + tableName + " WHERE xid = ?";
+ _CommandPtr cmd = NULL;
+ _ParameterPtr xidVal = NULL;
+
+ TESTHR(cmd.CreateInstance(__uuidof(Command)));
+ TESTHR(xidVal.CreateInstance(__uuidof(Parameter)));
+ _ConnectionPtr p = *dbConn;
+ cmd->ActiveConnection = p;
+ cmd->CommandText = command.c_str();
+ cmd->CommandType = adCmdText;
+ xidVal->Name = "@xid";
+ xidVal->Type = adVarBinary;
+ xidVal->Size = xid.length();
+ xidVal->Direction = adParamInput;
+ xidVal->Value = BlobEncoder(xid);
+ cmd->Parameters->Append(xidVal);
+ _variant_t deletedRecords;
+ cmd->Execute(&deletedRecords, NULL, adCmdText | adExecuteNoRecords);
+}
+
+void
+TplRecordset::recover(std::set<std::string>& xids)
+{
+ openRs();
+ if (rs->BOF && rs->EndOfFile)
+ return; // Nothing to do
+ rs->MoveFirst();
+ while (!rs->EndOfFile) {
+ _variant_t wxid = rs->Fields->Item["xid"]->Value;
+ char *xidBytes;
+ SafeArrayAccessData(wxid.parray, (void **)&xidBytes);
+ std::string xid(xidBytes, rs->Fields->Item["xid"]->ActualSize);
+ xids.insert(xid);
+ SafeArrayUnaccessData(wxid.parray);
+ rs->MoveNext();
+ }
+}
+
+void
+TplRecordset::dump()
+{
+ Recordset::dump();
+ if (rs->EndOfFile && rs->BOF) // No records
+ return;
+
+ rs->MoveFirst();
+ while (!rs->EndOfFile) {
+ _bstr_t wxid = rs->Fields->Item["xid"]->Value;
+ QPID_LOG(notice, " -> " << (const char *)wxid);
+ rs->MoveNext();
+ }
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.h b/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.h
new file mode 100644
index 0000000000..fbde51738c
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/TplRecordset.h
@@ -0,0 +1,58 @@
+#ifndef QPID_STORE_MSSQL_TPLRECORDSET_H
+#define QPID_STORE_MSSQL_TPLRECORDSET_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "Recordset.h"
+#include <string>
+#include <set>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class TplRecordset
+ *
+ * Class for the TPL (Transaction Prepared List) records.
+ */
+class TplRecordset : public Recordset {
+protected:
+
+public:
+ virtual void open(DatabaseConnection* conn, const std::string& table);
+
+ void add(const std::string& xid);
+
+ // Remove a record given its xid.
+ void remove(const std::string& xid);
+
+ // Recover prepared transaction XIDs.
+ void recover(std::set<std::string>& xids);
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_TPLRECORDSET_H */
diff --git a/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.cpp b/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.cpp
new file mode 100644
index 0000000000..acec95c1f9
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.cpp
@@ -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.
+ *
+ */
+
+#include <string>
+#include "VariantHelper.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+template <class Wrapped>
+VariantHelper<Wrapped>::VariantHelper()
+{
+ var.vt = VT_EMPTY;
+}
+
+template <class Wrapped>
+VariantHelper<Wrapped>::operator const _variant_t& () const
+{
+ return var;
+}
+
+// Specialization for using _variant_t to wrap a std::string
+VariantHelper<std::string>::VariantHelper(const std::string &init)
+{
+ if (init.empty() || init.length() == 0) {
+ var.vt = VT_BSTR;
+ var.bstrVal = NULL;
+ }
+ else {
+ var.SetString(init.c_str());
+ }
+}
+
+VariantHelper<std::string>&
+VariantHelper<std::string>::operator=(const std::string &rhs)
+{
+ if (rhs.empty() || rhs.length() == 0) {
+ var.vt = VT_BSTR;
+ var.bstrVal = NULL;
+ }
+ else {
+ var.SetString(rhs.c_str());
+ }
+ return *this;
+}
+
+VariantHelper<std::string>::operator const _variant_t& () const
+{
+ return var;
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.h b/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.h
new file mode 100644
index 0000000000..723dbc3b76
--- /dev/null
+++ b/qpid/cpp/src/qpid/store/ms-sql/VariantHelper.h
@@ -0,0 +1,61 @@
+#ifndef QPID_STORE_MSSQL_VARIANTHELPER_H
+#define QPID_STORE_MSSQL_VARIANTHELPER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <comutil.h>
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+/**
+ * @class VariantHelper
+ *
+ * Class template to wrap the details of working with _variant_t objects.
+ */
+template <class Wrapped> class VariantHelper {
+private:
+ _variant_t var;
+
+public:
+ VariantHelper();
+ VariantHelper(const Wrapped &init);
+
+ VariantHelper& operator =(const Wrapped& rhs);
+ operator const _variant_t& () const;
+};
+
+// Specialization for using _variant_t to wrap a std::string
+template<> class VariantHelper<std::string> {
+private:
+ _variant_t var;
+
+public:
+ VariantHelper(const std::string &init);
+ VariantHelper& operator =(const std::string& rhs);
+ operator const _variant_t& () const;
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_VARIANTHELPER_H */
diff --git a/qpid/cpp/src/qpid/sys/AggregateOutput.cpp b/qpid/cpp/src/qpid/sys/AggregateOutput.cpp
new file mode 100644
index 0000000000..fc95f46fb9
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AggregateOutput.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/AggregateOutput.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
+
+namespace qpid {
+namespace sys {
+
+AggregateOutput::AggregateOutput(OutputControl& c) : busy(false), control(c) {}
+
+void AggregateOutput::abort() { control.abort(); }
+
+void AggregateOutput::activateOutput() { control.activateOutput(); }
+
+void AggregateOutput::giveReadCredit(int32_t credit) { control.giveReadCredit(credit); }
+
+namespace {
+// Clear the busy flag and notify waiting threads in destructor.
+struct ScopedBusy {
+ bool& flag;
+ Monitor& monitor;
+ ScopedBusy(bool& f, Monitor& m) : flag(f), monitor(m) { f = true; }
+ ~ScopedBusy() { flag = false; monitor.notifyAll(); }
+};
+}
+
+bool AggregateOutput::doOutput() {
+ Mutex::ScopedLock l(lock);
+ ScopedBusy sb(busy, lock);
+
+ while (!tasks.empty()) {
+ OutputTask* t=tasks.front();
+ tasks.pop_front();
+ bool didOutput;
+ {
+ // Allow concurrent call to addOutputTask.
+ // removeOutputTask will wait till !busy before removing a task.
+ Mutex::ScopedUnlock u(lock);
+ didOutput = t->doOutput();
+ }
+ if (didOutput) {
+ tasks.push_back(t);
+ return true;
+ }
+ }
+ return false;
+}
+
+void AggregateOutput::addOutputTask(OutputTask* task) {
+ Mutex::ScopedLock l(lock);
+ tasks.push_back(task);
+}
+
+void AggregateOutput::removeOutputTask(OutputTask* task) {
+ Mutex::ScopedLock l(lock);
+ while (busy) lock.wait();
+ tasks.erase(std::remove(tasks.begin(), tasks.end(), task), tasks.end());
+}
+
+void AggregateOutput::removeAll()
+{
+ Mutex::ScopedLock l(lock);
+ while (busy) lock.wait();
+ tasks.clear();
+}
+
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/AggregateOutput.h b/qpid/cpp/src/qpid/sys/AggregateOutput.h
new file mode 100644
index 0000000000..d7c0ff29e3
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AggregateOutput.h
@@ -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.
+ *
+ */
+#ifndef _AggregateOutput_
+#define _AggregateOutput_
+
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/sys/OutputTask.h"
+#include "qpid/CommonImportExport.h"
+
+#include <algorithm>
+#include <deque>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Holds a collection of output tasks, doOutput picks the next one to execute.
+ *
+ * Tasks are automatically removed if their doOutput() or hasOutput() returns false.
+ *
+ * Thread safe. addOutputTask may be called in one connection thread while
+ * doOutput is called in another.
+ */
+
+class QPID_COMMON_CLASS_EXTERN AggregateOutput : public OutputTask, public OutputControl
+{
+ typedef std::deque<OutputTask*> TaskList;
+
+ Monitor lock;
+ TaskList tasks;
+ bool busy;
+ OutputControl& control;
+
+ public:
+ QPID_COMMON_EXTERN AggregateOutput(OutputControl& c);
+
+ // These may be called concurrently with any function.
+ QPID_COMMON_EXTERN void abort();
+ QPID_COMMON_EXTERN void activateOutput();
+ QPID_COMMON_EXTERN void giveReadCredit(int32_t);
+ QPID_COMMON_EXTERN void addOutputTask(OutputTask* t);
+
+ // These functions must not be called concurrently with each other.
+ QPID_COMMON_EXTERN bool doOutput();
+ QPID_COMMON_EXTERN void removeOutputTask(OutputTask* t);
+ QPID_COMMON_EXTERN void removeAll();
+
+ /** Apply f to each OutputTask* in the tasks list */
+ template <class F> void eachOutput(F f) {
+ Mutex::ScopedLock l(lock);
+ std::for_each(tasks.begin(), tasks.end(), f);
+ }
+};
+
+}} // namespace qpid::sys
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/AsynchIO.h b/qpid/cpp/src/qpid/sys/AsynchIO.h
new file mode 100644
index 0000000000..41f74f7ed0
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AsynchIO.h
@@ -0,0 +1,160 @@
+#ifndef _sys_AsynchIO
+#define _sys_AsynchIO
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+
+#include <string.h>
+
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+class Socket;
+class Poller;
+
+/*
+ * Asynchronous acceptor: accepts connections then does a callback with the
+ * accepted fd
+ */
+class AsynchAcceptor {
+public:
+ typedef boost::function1<void, const Socket&> Callback;
+
+ QPID_COMMON_EXTERN static AsynchAcceptor* create(const Socket& s, Callback callback);
+ virtual ~AsynchAcceptor() {};
+ virtual void start(boost::shared_ptr<Poller> poller) = 0;
+};
+
+/*
+ * Asynchronous connector: starts the process of initiating a connection and
+ * invokes a callback when completed or failed.
+ */
+class AsynchConnector {
+public:
+ typedef boost::function1<void, const Socket&> ConnectedCallback;
+ typedef boost::function3<void, const Socket&, int, const std::string&> FailedCallback;
+
+ // Call create() to allocate a new AsynchConnector object with the
+ // specified poller, addressing, and callbacks.
+ // This method is implemented in platform-specific code to
+ // create a correctly typed object. The platform code also manages
+ // deletes. To correctly manage heaps when needed, the allocate and
+ // delete should both be done from the same class/library.
+ QPID_COMMON_EXTERN static AsynchConnector* create(const Socket& s,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb);
+ virtual void start(boost::shared_ptr<Poller> poller) = 0;
+ virtual void stop() {};
+protected:
+ AsynchConnector() {}
+ virtual ~AsynchConnector() {}
+};
+
+struct AsynchIOBufferBase {
+ char* const bytes;
+ const int32_t byteCount;
+ int32_t dataStart;
+ int32_t dataCount;
+
+ AsynchIOBufferBase(char* const b, const int32_t s) :
+ bytes(b),
+ byteCount(s),
+ dataStart(0),
+ dataCount(0)
+ {}
+
+ virtual ~AsynchIOBufferBase()
+ {}
+
+ void squish() {
+ if (dataStart != 0) {
+ ::memmove(bytes, bytes + dataStart, dataCount);
+ dataStart = 0;
+ }
+ }
+};
+
+/*
+ * Asychronous reader/writer:
+ * Reader accepts buffers to read into; reads into the provided buffers
+ * and then does a callback with the buffer and amount read. Optionally it
+ * can callback when there is something to read but no buffer to read it into.
+ *
+ * Writer accepts a buffer and queues it for writing; can also be given
+ * a callback for when writing is "idle" (ie fd is writable, but nothing
+ * to write).
+ */
+class AsynchIO {
+public:
+ typedef AsynchIOBufferBase BufferBase;
+
+ typedef boost::function2<void, AsynchIO&, BufferBase*> ReadCallback;
+ typedef boost::function1<void, AsynchIO&> EofCallback;
+ typedef boost::function1<void, AsynchIO&> DisconnectCallback;
+ typedef boost::function2<void, AsynchIO&, const Socket&> ClosedCallback;
+ typedef boost::function1<void, AsynchIO&> BuffersEmptyCallback;
+ typedef boost::function1<void, AsynchIO&> IdleCallback;
+ typedef boost::function1<void, AsynchIO&> RequestCallback;
+
+ // Call create() to allocate a new AsynchIO object with the specified
+ // callbacks. This method is implemented in platform-specific code to
+ // create a correctly typed object. The platform code also manages
+ // deletes. To correctly manage heaps when needed, the allocate and
+ // delete should both be done from the same class/library.
+ QPID_COMMON_EXTERN static AsynchIO* create(const Socket& s,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0);
+public:
+ virtual void queueForDeletion() = 0;
+
+ virtual void start(boost::shared_ptr<Poller> poller) = 0;
+ virtual void queueReadBuffer(BufferBase* buff) = 0;
+ virtual void unread(BufferBase* buff) = 0;
+ virtual void queueWrite(BufferBase* buff) = 0;
+ virtual void notifyPendingWrite() = 0;
+ virtual void queueWriteClose() = 0;
+ virtual bool writeQueueEmpty() = 0;
+ virtual void startReading() = 0;
+ virtual void stopReading() = 0;
+ virtual void requestCallback(RequestCallback) = 0;
+ virtual BufferBase* getQueuedBuffer() = 0;
+
+protected:
+ // Derived class manages lifetime; must be constructed using the
+ // static create() method. Deletes not allowed from outside.
+ AsynchIO() {}
+ virtual ~AsynchIO() {}
+};
+
+}}
+
+#endif // _sys_AsynchIO
diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp
new file mode 100644
index 0000000000..30a87d9d44
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SecuritySettings.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace sys {
+
+// Buffer definition
+struct Buff : public AsynchIO::BufferBase {
+ Buff() :
+ AsynchIO::BufferBase(new char[65536], 65536)
+ {}
+ ~Buff()
+ { delete [] bytes;}
+};
+
+AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) :
+ identifier(id),
+ aio(0),
+ factory(f),
+ codec(0),
+ readError(false),
+ isClient(false),
+ readCredit(InfiniteCredit)
+{}
+
+AsynchIOHandler::~AsynchIOHandler() {
+ if (codec)
+ codec->closed();
+ delete codec;
+}
+
+void AsynchIOHandler::init(AsynchIO* a, int numBuffs) {
+ aio = a;
+
+ // Give connection some buffers to use
+ for (int i = 0; i < numBuffs; i++) {
+ aio->queueReadBuffer(new Buff);
+ }
+}
+
+void AsynchIOHandler::write(const framing::ProtocolInitiation& data)
+{
+ QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")");
+ AsynchIO::BufferBase* buff = aio->getQueuedBuffer();
+ if (!buff)
+ buff = new Buff;
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void AsynchIOHandler::abort() {
+ // Don't disconnect if we're already disconnecting
+ if (!readError) {
+ aio->requestCallback(boost::bind(&AsynchIOHandler::eof, this, _1));
+ }
+}
+
+void AsynchIOHandler::activateOutput() {
+ aio->notifyPendingWrite();
+}
+
+// Input side
+void AsynchIOHandler::giveReadCredit(int32_t credit) {
+ // Check whether we started in the don't about credit state
+ if (readCredit.boolCompareAndSwap(InfiniteCredit, credit))
+ return;
+ // TODO In theory should be able to use an atomic operation before taking the lock
+ // but in practice there seems to be an unexplained race in that case
+ ScopedLock<Mutex> l(creditLock);
+ if (readCredit.fetchAndAdd(credit) != 0)
+ return;
+ assert(readCredit.get() >= 0);
+ if (readCredit.get() != 0)
+ aio->startReading();
+}
+
+void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
+ if (readError) {
+ return;
+ }
+
+ // Check here for read credit
+ if (readCredit.get() != InfiniteCredit) {
+ if (readCredit.get() == 0) {
+ // FIXME aconway 2009-10-01: Workaround to avoid "false wakeups".
+ // readbuff is sometimes called with no credit.
+ // This should be fixed somewhere else to avoid such calls.
+ aio->unread(buff);
+ return;
+ }
+ // TODO In theory should be able to use an atomic operation before taking the lock
+ // but in practice there seems to be an unexplained race in that case
+ ScopedLock<Mutex> l(creditLock);
+ if (--readCredit == 0) {
+ assert(readCredit.get() >= 0);
+ if (readCredit.get() == 0) {
+ aio->stopReading();
+ }
+ }
+ }
+
+ size_t decoded = 0;
+ if (codec) { // Already initiated
+ try {
+ decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount);
+ }catch(const std::exception& e){
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+ }else{
+ framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount);
+ framing::ProtocolInitiation protocolInit;
+ if (protocolInit.decode(in)) {
+ decoded = in.getPosition();
+ QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")");
+ try {
+ codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
+ if (!codec) {
+ //TODO: may still want to revise this...
+ //send valid version header & close connection.
+ write(framing::ProtocolInitiation(framing::highestProtocolVersion));
+ readError = true;
+ aio->queueWriteClose();
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+ }
+ }
+ // TODO: unreading needs to go away, and when we can cope
+ // with multiple sub-buffers in the general buffer scheme, it will
+ if (decoded != size_t(buff->dataCount)) {
+ // Adjust buffer for used bytes and then "unread them"
+ buff->dataStart += decoded;
+ buff->dataCount -= decoded;
+ aio->unread(buff);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio->queueReadBuffer(buff);
+ }
+}
+
+void AsynchIOHandler::eof(AsynchIO& a) {
+ disconnect(a);
+ readError = true;
+ aio->queueWriteClose();
+}
+
+void AsynchIOHandler::closedSocket(AsynchIO&, const Socket& s) {
+ // If we closed with data still to send log a warning
+ if (!aio->writeQueueEmpty()) {
+ QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)");
+ }
+ delete &s;
+ aio->queueForDeletion();
+ delete this;
+}
+
+void AsynchIOHandler::disconnect(AsynchIO&) {
+ QPID_LOG(debug, "DISCONNECTED [" << identifier << "]");
+ if (codec) codec->closed();
+}
+
+// Notifications
+void AsynchIOHandler::nobuffs(AsynchIO&) {
+}
+
+void AsynchIOHandler::idle(AsynchIO&){
+ if (isClient && codec == 0) {
+ codec = factory->create(*this, identifier, SecuritySettings());
+ write(framing::ProtocolInitiation(codec->getVersion()));
+ return;
+ }
+ if (codec == 0) return;
+ try {
+ if (codec->canEncode()) {
+ // Try and get a queued buffer if not then construct new one
+ AsynchIO::BufferBase* buff = aio->getQueuedBuffer();
+ if (!buff) buff = new Buff;
+ size_t encoded=codec->encode(buff->bytes, buff->byteCount);
+ buff->dataCount = encoded;
+ aio->queueWrite(buff);
+ }
+ if (codec->isClosed()) {
+ readError = true;
+ aio->queueWriteClose();
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.h b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h
new file mode 100644
index 0000000000..b9867606c4
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h
@@ -0,0 +1,80 @@
+#ifndef _sys_AsynchIOHandler_h
+#define _sys_AsynchIOHandler_h
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/OutputControl.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+
+namespace framing {
+ class ProtocolInitiation;
+}
+
+namespace sys {
+
+class AsynchIO;
+struct AsynchIOBufferBase;
+class Socket;
+
+class AsynchIOHandler : public OutputControl {
+ std::string identifier;
+ AsynchIO* aio;
+ ConnectionCodec::Factory* factory;
+ ConnectionCodec* codec;
+ bool readError;
+ bool isClient;
+ AtomicValue<int32_t> readCredit;
+ static const int32_t InfiniteCredit = -1;
+ Mutex creditLock;
+
+ void write(const framing::ProtocolInitiation&);
+
+ public:
+ QPID_COMMON_EXTERN AsynchIOHandler(std::string id, ConnectionCodec::Factory* f);
+ QPID_COMMON_EXTERN ~AsynchIOHandler();
+ QPID_COMMON_EXTERN void init(AsynchIO* a, int numBuffs);
+
+ QPID_COMMON_INLINE_EXTERN void setClient() { isClient = true; }
+
+ // Output side
+ QPID_COMMON_EXTERN void abort();
+ QPID_COMMON_EXTERN void activateOutput();
+ QPID_COMMON_EXTERN void giveReadCredit(int32_t credit);
+
+ // Input side
+ QPID_COMMON_EXTERN void readbuff(AsynchIO& aio, AsynchIOBufferBase* buff);
+ QPID_COMMON_EXTERN void eof(AsynchIO& aio);
+ QPID_COMMON_EXTERN void disconnect(AsynchIO& aio);
+
+ // Notifications
+ QPID_COMMON_EXTERN void nobuffs(AsynchIO& aio);
+ QPID_COMMON_EXTERN void idle(AsynchIO& aio);
+ QPID_COMMON_EXTERN void closedSocket(AsynchIO& aio, const Socket& s);
+};
+
+}} // namespace qpid::sys
+
+#endif // _sys_AsynchIOHandler_h
diff --git a/qpid/cpp/src/qpid/sys/AtomicCount.h b/qpid/cpp/src/qpid/sys/AtomicCount.h
new file mode 100644
index 0000000000..94580c61f3
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AtomicCount.h
@@ -0,0 +1,52 @@
+#ifndef _posix_AtomicCount_h
+#define _posix_AtomicCount_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <boost/detail/atomic_count.hpp>
+#include "qpid/sys/ScopedIncrement.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Atomic counter.
+ */
+class AtomicCount {
+ public:
+ typedef ::qpid::sys::ScopedDecrement<AtomicCount> ScopedDecrement;
+ typedef ::qpid::sys::ScopedIncrement<AtomicCount> ScopedIncrement;
+
+ AtomicCount(long value = 0) : count(value) {}
+
+ void operator++() { ++count ; }
+
+ long operator--() { return --count; }
+
+ operator long() const { return count; }
+
+ private:
+ boost::detail::atomic_count count;
+};
+
+
+}}
+
+
+#endif // _posix_AtomicCount_h
diff --git a/qpid/cpp/src/qpid/sys/AtomicValue.h b/qpid/cpp/src/qpid/sys/AtomicValue.h
new file mode 100644
index 0000000000..bf995f991e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AtomicValue.h
@@ -0,0 +1,39 @@
+#ifndef QPID_SYS_ATOMICVALUE_H
+#define QPID_SYS_ATOMICVALUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Have to check for clang before gcc as clang pretends to be gcc too
+#if defined( __clang__ )
+// Use the clang doesn't support atomic builtins for 64 bit values, so use the slow versions
+#include "qpid/sys/AtomicValue_mutex.h"
+
+#elif defined( __GNUC__ ) && __GNUC__ >= 4 && ( defined( __i686__ ) || defined( __x86_64__ ) )
+// Use the Gnu C built-in atomic operations if compiling with gcc on a suitable platform.
+#include "qpid/sys/AtomicValue_gcc.h"
+
+#else
+// Fall-back to mutex locked operations if we don't have atomic ops.
+#include "qpid/sys/AtomicValue_mutex.h"
+#endif
+
+#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/
diff --git a/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h b/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h
new file mode 100644
index 0000000000..d022b07c1d
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AtomicValue_gcc.h
@@ -0,0 +1,68 @@
+#ifndef QPID_SYS_ATOMICVALUE_GCC_H
+#define QPID_SYS_ATOMICVALUE_GCC_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 !defined(QPID_SYS_ATOMICVALUE_H)
+#error "This file should only be included via AtomicValue.h."
+#endif
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes.
+ * All operations are atomic and preform a full memory barrier unless otherwise noted.
+ */
+template <class T>
+class AtomicValue
+{
+ public:
+ AtomicValue(T init=0) : value(init) {}
+
+ // Update and return new value.
+ inline T operator+=(T n) { return __sync_add_and_fetch(&value, n); }
+ inline T operator-=(T n) { return __sync_sub_and_fetch(&value, n); }
+ inline T operator++() { return *this += 1; }
+ inline T operator--() { return *this -= 1; }
+
+ // Update and return old value.
+ inline T fetchAndAdd(T n) { return __sync_fetch_and_add(&value, n); }
+ inline T fetchAndSub(T n) { return __sync_fetch_and_sub(&value, n); }
+ inline T operator++(int) { return fetchAndAdd(1); }
+ inline T operator--(int) { return fetchAndSub(1); }
+
+ /** If current value == testval then set to newval. Returns the old value. */
+ T valueCompareAndSwap(T testval, T newval) { return __sync_val_compare_and_swap(&value, testval, newval); }
+
+ /** If current value == testval then set to newval. Returns true if the swap was performed. */
+ bool boolCompareAndSwap(T testval, T newval) { return __sync_bool_compare_and_swap(&value, testval, newval); }
+
+ T get() const { return const_cast<AtomicValue<T>*>(this)->fetchAndAdd(static_cast<T>(0)); }
+
+ private:
+ T value;
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/
diff --git a/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h b/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h
new file mode 100644
index 0000000000..e4d433e7f5
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/AtomicValue_mutex.h
@@ -0,0 +1,83 @@
+#ifndef QPID_SYS_ATOMICVALUE_MUTEX_H
+#define QPID_SYS_ATOMICVALUE_MUTEX_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 !defined(QPID_SYS_ATOMICVALUE_H)
+#error "This file should only be included via AtomicValue.h."
+#endif
+
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes.
+ * All operations are atomic and preform a full memory barrier unless otherwise noted.
+ */
+template <class T>
+class AtomicValue
+{
+ public:
+ AtomicValue(T init=0) : value(init) {}
+
+ // Update and return new value.
+ inline T operator+=(T n) { Lock l(lock); return value += n; }
+ inline T operator-=(T n) { Lock l(lock); return value -= n; }
+ inline T operator++() { return *this += 1; }
+ inline T operator--() { return *this -= 1; }
+
+ // Update and return old value.
+ inline T fetchAndAdd(T n) { Lock l(lock); T old=value; value += n; return old; }
+ inline T fetchAndSub(T n) { Lock l(lock); T old=value; value -= n; return old; }
+ inline T operator++(int) { return fetchAndAdd(1); }
+ inline T operator--(int) { return fetchAndSub(1); }
+
+ AtomicValue& operator=(T newval) { Lock l(lock); value = newval; return *this; }
+
+ /** If current value == testval then set to newval. Returns the old value. */
+ T valueCompareAndSwap(T testval, T newval) {
+ Lock l(lock);
+ T old=value;
+ if (value == testval) value = newval;
+ return old;
+ }
+
+ /** If current value == testval then set to newval. Returns true if the swap was performed. */
+ bool boolCompareAndSwap(T testval, T newval) {
+ Lock l(lock);
+ if (value == testval) { value = newval; return true; }
+ return false;
+ }
+
+ T get() const { Lock l(lock); return value; }
+
+ private:
+ typedef Mutex::ScopedLock Lock;
+ T value;
+ mutable Mutex lock;
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_ATOMICVALUE_MUTEX_H*/
diff --git a/qpid/cpp/src/qpid/sys/BlockingQueue.h b/qpid/cpp/src/qpid/sys/BlockingQueue.h
new file mode 100644
index 0000000000..ca6b529930
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/BlockingQueue.h
@@ -0,0 +1,129 @@
+#ifndef QPID_SYS_BLOCKINGQUEUE_H
+#define QPID_SYS_BLOCKINGQUEUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Waitable.h"
+
+#include <queue>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A simple blocking queue template
+ */
+template <class T>
+class BlockingQueue
+{
+ mutable sys::Waitable waitable;
+ std::queue<T> queue;
+
+public:
+ BlockingQueue() {}
+ ~BlockingQueue() { close(); }
+
+ /** Pop from the queue, block up to timeout if empty.
+ *@param result Set to value popped from queue.
+ *@param timeout Defaults to infinite.
+ *@return true if result was set, false if queue empty after timeout.
+ */
+ bool pop(T& result, Duration timeout=TIME_INFINITE) {
+ Mutex::ScopedLock l(waitable);
+ {
+ Waitable::ScopedWait w(waitable);
+ if (timeout == TIME_INFINITE) {
+ while (queue.empty()) waitable.wait();
+ } else if (timeout) {
+ AbsTime deadline(now(),timeout);
+ while (queue.empty() && deadline > now()) waitable.wait(deadline);
+ } else {
+ //ensure zero timeout pop does not miss the fact that
+ //queue is closed
+ waitable.checkException();
+ }
+ }
+ if (queue.empty()) return false;
+ result = queue.front();
+ queue.pop();
+ if (!queue.empty())
+ waitable.notify(); // Notify another waiter.
+ return true;
+ }
+
+ T pop(Duration timeout=TIME_INFINITE) {
+ T result;
+ bool ok = pop(result, timeout);
+ if (!ok)
+ throw Exception("Timed out waiting on a blocking queue");
+ return result;
+ }
+
+ /** Push a value onto the queue.
+ * Note it is not an error to push onto a closed queue.
+ */
+ void push(const T& t) {
+ Mutex::ScopedLock l(waitable);
+ queue.push(t);
+ waitable.notify(); // Notify a waiter.
+ }
+
+ /**
+ * Close the queue.
+ *@ex exception to throw to waiting threads. ClosedException by default.
+ */
+ void close(const ExceptionHolder& ex=ExceptionHolder(new ClosedException()))
+ {
+ Mutex::ScopedLock l(waitable);
+ if (!waitable.hasException()) {
+ waitable.setException(ex);
+ waitable.notifyAll();
+ waitable.waitWaiters(); // Ensure no threads are still waiting.
+ }
+ }
+
+ /** Open a closed queue. */
+ void open() {
+ Mutex::ScopedLock l(waitable);
+ waitable.resetException();
+ }
+
+ bool isClosed() const {
+ Mutex::ScopedLock l(waitable);
+ return waitable.hasException();
+ }
+
+ bool empty() const {
+ Mutex::ScopedLock l(waitable);
+ return queue.empty();
+ }
+ size_t size() const {
+ Mutex::ScopedLock l(waitable);
+ return queue.size();
+ }
+};
+
+}}
+
+
+
+#endif /*!QPID_SYS_BLOCKINGQUEUE_H*/
diff --git a/qpid/cpp/src/qpid/sys/ClusterSafe.cpp b/qpid/cpp/src/qpid/sys/ClusterSafe.cpp
new file mode 100644
index 0000000000..dd37615145
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ClusterSafe.cpp
@@ -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.
+ *
+ */
+
+#include "ClusterSafe.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Thread.h"
+#include <stdlib.h>
+
+namespace qpid {
+namespace sys {
+
+namespace {
+bool inCluster = false;
+QPID_TSS bool inContext = false;
+}
+
+bool isClusterSafe() { return !inCluster || inContext; }
+
+void assertClusterSafe() {
+ if (!isClusterSafe()) {
+ QPID_LOG(critical, "Modified cluster state outside of cluster context");
+ ::abort();
+ }
+}
+
+ClusterSafeScope::ClusterSafeScope() {
+ save = inContext;
+ inContext = true;
+}
+
+ClusterSafeScope::~ClusterSafeScope() {
+ assert(inContext);
+ inContext = save;
+}
+
+ClusterUnsafeScope::ClusterUnsafeScope() {
+ save = inContext;
+ inContext = false;
+}
+
+ClusterUnsafeScope::~ClusterUnsafeScope() {
+ assert(!inContext);
+ inContext = save;
+}
+
+void enableClusterSafe() { inCluster = true; }
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/ClusterSafe.h b/qpid/cpp/src/qpid/sys/ClusterSafe.h
new file mode 100644
index 0000000000..27e4eb46a5
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ClusterSafe.h
@@ -0,0 +1,87 @@
+#ifndef QPID_SYS_CLUSTERSAFE_H
+#define QPID_SYS_CLUSTERSAFE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Assertion to add to code that modifies clustered state.
+ *
+ * In a non-clustered broker this is a no-op.
+ *
+ * In a clustered broker, checks that it is being called
+ * in a context where it is safe to modify clustered state.
+ * If not it aborts the process as this is a serious bug.
+ *
+ * This function is in the common library rather than the cluster
+ * library because it is called by code in the broker library.
+ */
+QPID_COMMON_EXTERN void assertClusterSafe();
+
+/**
+ * In a non-clustered broker, returns true.
+ *
+ * In a clustered broker returns true if we are in a context where it
+ * is safe to modify cluster state.
+ *
+ * This function is in the common library rather than the cluster
+ * library because it is called by code in the broker library.
+ */
+QPID_COMMON_EXTERN bool isClusterSafe();
+
+/**
+ * Mark a scope as cluster safe. Sets isClusterSafe in constructor and resets
+ * to previous value in destructor.
+ */
+class ClusterSafeScope {
+ public:
+ ClusterSafeScope();
+ ~ClusterSafeScope();
+ private:
+ bool save;
+};
+
+/**
+ * Mark a scope as cluster unsafe. Clears isClusterSafe in constructor and resets
+ * to previous value in destructor.
+ */
+class ClusterUnsafeScope {
+ public:
+ QPID_COMMON_EXTERN ClusterUnsafeScope();
+ QPID_COMMON_EXTERN ~ClusterUnsafeScope();
+ private:
+ bool save;
+};
+
+/**
+ * Enable cluster-safe assertions. By default they are no-ops.
+ * Called by cluster code.
+ */
+void enableClusterSafe();
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_CLUSTERSAFE_H*/
diff --git a/qpid/cpp/src/qpid/sys/Codec.h b/qpid/cpp/src/qpid/sys/Codec.h
new file mode 100644
index 0000000000..ace721fbcc
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Codec.h
@@ -0,0 +1,52 @@
+#ifndef QPID_SYS_CODEC_H
+#define QPID_SYS_CODEC_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <cstddef>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Generic codec interface
+ */
+class Codec
+{
+ public:
+ virtual ~Codec() {}
+
+ /** Decode from buffer, return number of bytes decoded.
+ * @return may be less than size if there was incomplete
+ * data at the end of the buffer.
+ */
+ virtual std::size_t decode(const char* buffer, std::size_t size) = 0;
+
+
+ /** Encode into buffer, return number of bytes encoded */
+ virtual std::size_t encode(const char* buffer, std::size_t size) = 0;
+
+ /** Return true if we have data to encode */
+ virtual bool canEncode() = 0;
+};
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_CODEC_H*/
diff --git a/qpid/cpp/src/qpid/sys/ConnectionCodec.h b/qpid/cpp/src/qpid/sys/ConnectionCodec.h
new file mode 100644
index 0000000000..c2890f06dc
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ConnectionCodec.h
@@ -0,0 +1,68 @@
+#ifndef QPID_SYS_CONNECTION_CODEC_H
+#define QPID_SYS_CONNECTION_CODEC_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Codec.h"
+#include "qpid/framing/ProtocolVersion.h"
+
+namespace qpid {
+
+namespace sys {
+
+class InputHandlerFactory;
+class OutputControl;
+struct SecuritySettings;
+
+/**
+ * Interface of coder/decoder for a connection of a specific protocol
+ * version.
+ */
+class ConnectionCodec : public Codec {
+ public:
+ virtual ~ConnectionCodec() {}
+
+ /** Network connection was closed from other end. */
+ virtual void closed() = 0;
+
+ virtual bool isClosed() const = 0;
+
+ virtual framing::ProtocolVersion getVersion() const = 0;
+
+ struct Factory {
+ virtual ~Factory() {}
+
+ /** Return 0 if version unknown */
+ virtual ConnectionCodec* create(
+ framing::ProtocolVersion, OutputControl&, const std::string& id,
+ const SecuritySettings&
+ ) = 0;
+
+ /** Return "preferred" codec for outbound connections. */
+ virtual ConnectionCodec* create(
+ OutputControl&, const std::string& id, const SecuritySettings&
+ ) = 0;
+ };
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_CONNECTION_CODEC_H*/
diff --git a/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h b/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h
new file mode 100644
index 0000000000..92de808308
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionInputHandler_
+#define _ConnectionInputHandler_
+
+#include "qpid/framing/InputHandler.h"
+#include "qpid/sys/OutputTask.h"
+#include "qpid/sys/TimeoutHandler.h"
+
+namespace qpid {
+namespace sys {
+
+
+/**
+ * ConnectionInputHandler provides methods to process incoming frames
+ * using InputHandler::receive() and to generate outgoing messages in
+ * OutputTask::doOutput()
+ *
+ */
+
+ class ConnectionInputHandler :
+ public qpid::framing::InputHandler,
+ public TimeoutHandler, public OutputTask
+ {
+ public:
+
+ virtual void closed() = 0;
+ };
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h b/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h
new file mode 100644
index 0000000000..9bb7e13686
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionInputHandlerFactory_
+#define _ConnectionInputHandlerFactory_
+
+#include <boost/noncopyable.hpp>
+#include <string>
+
+namespace qpid {
+namespace sys {
+
+class ConnectionOutputHandler;
+class ConnectionInputHandler;
+
+/**
+ * Callback interface used by the Acceptor to
+ * create a ConnectionInputHandler for each new connection.
+ */
+class ConnectionInputHandlerFactory : private boost::noncopyable
+{
+ public:
+ /**
+ *@param out handler for connection output.
+ *@param id identify the connection for management purposes.
+ */
+ virtual ConnectionInputHandler* create(ConnectionOutputHandler* out,
+ const std::string& id,
+ bool isClient) = 0;
+
+ virtual ~ConnectionInputHandlerFactory(){}
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h b/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h
new file mode 100644
index 0000000000..421dd7c269
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h
@@ -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.
+ *
+ */
+#ifndef _ConnectionOutputHandler_
+#define _ConnectionOutputHandler_
+
+#include "qpid/framing/OutputHandler.h"
+#include "qpid/sys/OutputControl.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Provides the output handler associated with a connection.
+ */
+class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler, public OutputControl
+{
+ public:
+ virtual void close() = 0;
+ virtual size_t getBuffered() const { return 0; }
+};
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h b/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
new file mode 100644
index 0000000000..95a08d15ae
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
@@ -0,0 +1,56 @@
+#ifndef QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H
+#define QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ConnectionOutputHandler.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A ConnectionOutputHandler that delegates to another
+ * ConnectionOutputHandler. Allows the "real" ConnectionOutputHandler
+ * to be changed without updating all the pointers/references
+ * using the ConnectionOutputHandlerPtr
+ */
+class ConnectionOutputHandlerPtr : public ConnectionOutputHandler
+{
+ public:
+ ConnectionOutputHandlerPtr(ConnectionOutputHandler* p) : next(p) { assert(next); }
+ void set(ConnectionOutputHandler* p) { next = p; assert(next); }
+ ConnectionOutputHandler* get() { return next; }
+ const ConnectionOutputHandler* get() const { return next; }
+
+ void close() { next->close(); }
+ size_t getBuffered() const { return next->getBuffered(); }
+ void abort() { next->abort(); }
+ void activateOutput() { next->activateOutput(); }
+ void giveReadCredit(int32_t credit) { next->giveReadCredit(credit); }
+ void send(framing::AMQFrame& f) { next->send(f); }
+
+ private:
+ ConnectionOutputHandler* next;
+};
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H*/
diff --git a/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h b/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h
new file mode 100644
index 0000000000..45a231dfd8
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/CopyOnWriteArray.h
@@ -0,0 +1,156 @@
+#ifndef QPID_SYS_COPYONWRITEARRAY_H
+#define QPID_SYS_COPYONWRITEARRAY_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Mutex.h"
+#include <algorithm>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * An array that copies on adding/removing element allowing lock-free
+ * iteration.
+ */
+template <class T>
+class CopyOnWriteArray
+{
+public:
+ typedef boost::shared_ptr<const std::vector<T> > ConstPtr;
+
+ CopyOnWriteArray() {}
+ CopyOnWriteArray(const CopyOnWriteArray& c) : array(c.array) {}
+
+ void add(T& t)
+ {
+ Mutex::ScopedLock l(lock);
+ ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>());
+ copy->push_back(t);
+ array = copy;
+ }
+
+ bool remove(T& t)
+ {
+ Mutex::ScopedLock l(lock);
+ if (array && std::find(array->begin(), array->end(), t) != array->end()) {
+ ArrayPtr copy(new std::vector<T>(*array));
+ copy->erase(std::find(copy->begin(), copy->end(), t));
+ array = copy;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool clear()
+ {
+ Mutex::ScopedLock l(lock);
+ if (array && !array->empty()) {
+ ArrayPtr copy;
+ array = copy;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ template <class F>
+ bool add_unless(T& t, F f)
+ {
+ Mutex::ScopedLock l(lock);
+ if (array && std::find_if(array->begin(), array->end(), f) != array->end()) {
+ return false;
+ } else {
+ ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>());
+ copy->push_back(t);
+ array = copy;
+ return true;
+ }
+ }
+
+ template <class F>
+ bool remove_if(F f)
+ {
+ Mutex::ScopedLock l(lock);
+ if (array && std::find_if(array->begin(), array->end(), f) != array->end()) {
+ ArrayPtr copy(new std::vector<T>(*array));
+ copy->erase(std::remove_if(copy->begin(), copy->end(), f), copy->end());
+ array = copy;
+ return true;
+ }
+ return false;
+ }
+
+ template <class TestFn, class ModifierFn>
+ bool modify_if(TestFn f, ModifierFn & m)
+ {
+ if (!array)
+ return false;
+ {
+ Mutex::ScopedLock l(lock);
+ if (std::find_if(array->begin(), array->end(), f) != array->end())
+ {
+ ArrayPtr copy(new std::vector<T>(*array));
+ m(*std::find_if(copy->begin(), copy->end(), f));
+ array = copy;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template <class F>
+ F for_each(F f)
+ {
+ ArrayPtr a;
+ {
+ Mutex::ScopedLock l(lock);
+ a = array;
+ }
+ if (!a) return f;
+ return std::for_each(a->begin(), a->end(), f);
+ }
+
+ ConstPtr snapshot()
+ {
+ ConstPtr a;
+ {
+ Mutex::ScopedLock l(lock);
+ a = array;
+ }
+ return a;
+ }
+
+private:
+ typedef boost::shared_ptr< std::vector<T> > ArrayPtr;
+ Mutex lock;
+ ArrayPtr array;
+};
+
+}}
+
+
+
+#endif /*!QPID_SYS_COPYONWRITEARRAY_H*/
diff --git a/qpid/cpp/src/qpid/sys/DeletionManager.h b/qpid/cpp/src/qpid/sys/DeletionManager.h
new file mode 100644
index 0000000000..c1fea19f30
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/DeletionManager.h
@@ -0,0 +1,162 @@
+#ifndef _sys_DeletionManager_h
+#define _sys_DeletionManager_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <vector>
+#include <algorithm>
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+struct deleter
+{
+ template <typename T>
+ void operator()(T* ptr){ delete ptr;}
+};
+
+/**
+ * DeletionManager keeps track of handles that need to be deleted but may still be
+ * in use by one of the threads concurrently.
+ *
+ * The mode of operation is like this:
+ * - When we want to delete but we might still be using the handle we
+ * * Transfer ownership of the handle to this class
+ * * Mark the handle as (potentially) in use by every thread
+ * - Then subsequently at points where the thread code knows it isn't
+ * using any handles it declares that it is using no handles
+ * - When the last thread declares no use of a handle it automatically
+ * gets deleted by the shared_ptr implementation
+ *
+ * The class only has static members and data and so can only be used once for
+ * any particular handle type
+ */
+template <typename H>
+class DeletionManager
+{
+ struct ThreadStatus;
+
+public:
+ // Mark every thread as using the handle - it will be deleted
+ // below after every thread marks the handle as unused
+ static void markForDeletion(H* handle) {
+ allThreadsStatuses.addHandle(shared_ptr(handle));
+ }
+
+ // Mark this thread is not using any handle -
+ // handles get deleted here when no one else
+ // is using them either
+ static void markAllUnusedInThisThread() {
+ ThreadStatus* threadStatus = getThreadStatus();
+ ScopedLock<Mutex> l(threadStatus->lock);
+
+ // The actual deletions will happen here when all the shared_ptr
+ // ref counts hit 0 (that is when every thread marks the handle unused)
+ threadStatus->handles.clear();
+ }
+
+ static void destroyThreadState() {
+ ThreadStatus* threadStatus = getThreadStatus();
+ allThreadsStatuses.delThreadStatus(threadStatus);
+ delete threadStatus;
+ threadStatus = 0;
+ }
+
+private:
+
+ static ThreadStatus*& getThreadStatus() {
+ static __thread ThreadStatus* threadStatus = 0;
+
+ // Thread local vars can't be dynamically constructed so we need
+ // to check whether we've made it yet and construct it if not
+ // (no locking necessary for the check as it's thread local!)
+ if (!threadStatus) {
+ threadStatus = new ThreadStatus;
+ allThreadsStatuses.addThreadStatus(threadStatus);
+ }
+
+ return threadStatus;
+ }
+
+ typedef boost::shared_ptr<H> shared_ptr;
+
+ // In theory we know that we never need more handles than the number of
+ // threads runnning so we could use a fixed size array. However at this point
+ // in the code we don't have easy access to this information.
+ struct ThreadStatus
+ {
+ Mutex lock;
+ std::vector<shared_ptr> handles;
+ };
+
+ class AllThreadsStatuses
+ {
+ Mutex lock;
+ std::vector<ThreadStatus*> statuses;
+
+ struct handleAdder
+ {
+ shared_ptr handle;
+
+ handleAdder(shared_ptr h): handle(h) {}
+
+ void operator()(ThreadStatus* ptr) {
+ ScopedLock<Mutex> l(ptr->lock);
+ ptr->handles.push_back(handle);
+ }
+ };
+
+ public:
+ // Need this to be able to do static initialisation
+ explicit AllThreadsStatuses(int) {}
+
+ ~AllThreadsStatuses() {
+ ScopedLock<Mutex> l(lock);
+ std::for_each(statuses.begin(), statuses.end(), deleter());
+ }
+
+ void addThreadStatus(ThreadStatus* t) {
+ ScopedLock<Mutex> l(lock);
+ statuses.push_back(t);
+ }
+
+ void delThreadStatus(ThreadStatus* t) {
+ ScopedLock<Mutex> l(lock);
+ typename std::vector<ThreadStatus*>::iterator it =
+ std::find(statuses.begin(),statuses.end(), t);
+ if (it != statuses.end()) {
+ statuses.erase(it);
+ }
+ }
+
+ void addHandle(shared_ptr h) {
+ ScopedLock<Mutex> l(lock);
+ std::for_each(statuses.begin(), statuses.end(), handleAdder(h));
+ }
+ };
+
+ static AllThreadsStatuses allThreadsStatuses;
+};
+
+}}
+#endif // _sys_DeletionManager_h
diff --git a/qpid/cpp/src/qpid/sys/DispatchHandle.cpp b/qpid/cpp/src/qpid/sys/DispatchHandle.cpp
new file mode 100644
index 0000000000..5d6fc4e72f
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/DispatchHandle.cpp
@@ -0,0 +1,352 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/log/Statement.h"
+
+#include <algorithm>
+
+#include <boost/cast.hpp>
+
+#include <assert.h>
+
+namespace qpid {
+namespace sys {
+
+DispatchHandle::DispatchHandle(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) :
+ PollerHandle(h),
+ readableCallback(rCb),
+ writableCallback(wCb),
+ disconnectedCallback(dCb),
+ state(IDLE)
+{
+}
+
+
+DispatchHandle::~DispatchHandle() {
+}
+
+void DispatchHandle::startWatch(Poller::shared_ptr poller0) {
+ bool r = readableCallback;
+ bool w = writableCallback;
+
+ ScopedLock<Mutex> lock(stateLock);
+ assert(state == IDLE);
+
+ poller = poller0;
+ poller->registerHandle(*this);
+ state = WAITING;
+ Poller::Direction dir = r ?
+ ( w ? Poller::INOUT : Poller::INPUT ) :
+ ( w ? Poller::OUTPUT : Poller::NONE );
+ poller->monitorHandle(*this, dir);
+}
+
+void DispatchHandle::rewatch() {
+ bool r = readableCallback;
+ bool w = writableCallback;
+ if (!r && !w) {
+ return;
+ }
+ Poller::Direction dir = r ?
+ ( w ? Poller::INOUT : Poller::INPUT ) :
+ ( w ? Poller::OUTPUT : Poller::NONE );
+
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->monitorHandle(*this, dir);
+}
+
+void DispatchHandle::rewatchRead() {
+ if (!readableCallback) {
+ return;
+ }
+
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->monitorHandle(*this, Poller::INPUT);
+}
+
+void DispatchHandle::rewatchWrite() {
+ if (!writableCallback) {
+ return;
+ }
+
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->monitorHandle(*this, Poller::OUTPUT);
+}
+
+void DispatchHandle::unwatchRead() {
+ if (!readableCallback) {
+ return;
+ }
+
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->unmonitorHandle(*this, Poller::INPUT);
+}
+
+void DispatchHandle::unwatchWrite() {
+ if (!writableCallback) {
+ return;
+ }
+
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->unmonitorHandle(*this, Poller::OUTPUT);
+}
+
+void DispatchHandle::unwatch() {
+ ScopedLock<Mutex> lock(stateLock);
+ switch(state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ break;
+ }
+ assert(poller);
+ poller->unmonitorHandle(*this, Poller::INOUT);
+}
+
+void DispatchHandle::stopWatch() {
+ ScopedLock<Mutex> lock(stateLock);
+ switch (state) {
+ case IDLE:
+ assert(state != IDLE);
+ return;
+ case STOPPING:
+ assert(state != STOPPING);
+ return;
+ case CALLING:
+ state = STOPPING;
+ break;
+ case WAITING:
+ state = IDLE;
+ break;
+ case DELETING:
+ return;
+ }
+ assert(poller);
+ poller->unregisterHandle(*this);
+ poller.reset();
+}
+
+// If we are in the IDLE/STOPPING state we can't do the callback as we've
+// not/no longer got the fd registered in any poller
+void DispatchHandle::call(Callback iCb) {
+ assert(iCb);
+ ScopedLock<Mutex> lock(stateLock);
+ switch (state) {
+ case IDLE:
+ case STOPPING:
+ case DELETING:
+ return;
+ default:
+ interruptedCallbacks.push(iCb);
+ assert(poller);
+ (void) poller->interrupt(*this);
+ }
+}
+
+// The slightly strange switch structure
+// is to ensure that the lock is released before
+// we do the delete
+void DispatchHandle::doDelete() {
+ {
+ ScopedLock<Mutex> lock(stateLock);
+ // Ensure that we're no longer watching anything
+ switch (state) {
+ case IDLE:
+ state = DELETING;
+ break;
+ case STOPPING:
+ state = DELETING;
+ return;
+ case WAITING:
+ state = DELETING;
+ assert(poller);
+ (void) poller->interrupt(*this);
+ poller->unregisterHandle(*this);
+ return;
+ case CALLING:
+ state = DELETING;
+ assert(poller);
+ poller->unregisterHandle(*this);
+ return;
+ case DELETING:
+ return;
+ }
+ }
+ // If we're IDLE we can do this right away
+ delete this;
+}
+
+void DispatchHandle::processEvent(Poller::EventType type) {
+
+ // Phase I
+ {
+ ScopedLock<Mutex> lock(stateLock);
+
+ switch(state) {
+ case IDLE:
+ // Can get here if a non connection thread stops watching
+ // whilst we were stuck in the above lock
+ return;
+ case WAITING:
+ state = CALLING;
+ break;
+ case CALLING:
+ assert(state!=CALLING);
+ return;
+ case STOPPING:
+ assert(state!=STOPPING);
+ return;
+ case DELETING:
+ // Need to make sure we clean up any pending callbacks in this case
+ std::swap(callbacks, interruptedCallbacks);
+ goto saybyebye;
+ }
+
+ std::swap(callbacks, interruptedCallbacks);
+ }
+
+ // Do callbacks - whilst we are doing the callbacks we are prevented from processing
+ // the same handle until we re-enable it. To avoid rentering the callbacks for a single
+ // handle re-enabling in the callbacks is actually deferred until they are complete.
+ try {
+ switch (type) {
+ case Poller::READABLE:
+ readableCallback(*this);
+ break;
+ case Poller::WRITABLE:
+ writableCallback(*this);
+ break;
+ case Poller::READ_WRITABLE:
+ readableCallback(*this);
+ writableCallback(*this);
+ break;
+ case Poller::DISCONNECTED:
+ if (disconnectedCallback) {
+ disconnectedCallback(*this);
+ }
+ break;
+ case Poller::INTERRUPTED:
+ {
+ // We'll actually do the interrupt below
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ // If we have any callbacks do them now -
+ // (because we use a copy from before the previous callbacks we won't
+ // do anything yet that was just added)
+ while (callbacks.size() > 0) {
+ {
+ ScopedLock<Mutex> lock(stateLock);
+ switch (state) {
+ case DELETING:
+ goto finishcallbacks;
+ default:
+ break;
+ }
+ }
+ Callback cb = callbacks.front();
+ assert(cb);
+ cb(*this);
+ callbacks.pop();
+ }
+ } catch (std::exception& e) {
+ // One of the callbacks threw an exception - that's not allowed
+ QPID_LOG(error, "Caught exception in state: " << state << " with event: " << type << ": " << e.what());
+ // It would be nice to clean up and delete ourselves here, but we can't
+ }
+
+finishcallbacks:
+ {
+ ScopedLock<Mutex> lock(stateLock);
+ switch (state) {
+ case IDLE:
+ assert(state!=IDLE);
+ return;
+ case STOPPING:
+ state = IDLE;
+ return;
+ case WAITING:
+ assert(state!=WAITING);
+ return;
+ case CALLING:
+ state = WAITING;
+ return;
+ case DELETING:
+ break;
+ }
+ }
+
+saybyebye:
+ delete this;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/DispatchHandle.h b/qpid/cpp/src/qpid/sys/DispatchHandle.h
new file mode 100644
index 0000000000..115a3c44f7
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/DispatchHandle.h
@@ -0,0 +1,150 @@
+#ifndef _sys_DispatchHandle_h
+#define _sys_DispatchHandle_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/function.hpp>
+
+#include <queue>
+
+namespace qpid {
+namespace sys {
+
+class DispatchHandleRef;
+/**
+ * In order to have your own handle (file descriptor on Unix) watched by the poller
+ * you need to:
+ *
+ * - Subclass IOHandle, in the constructor supply an appropriate
+ * IOHandlerPrivate object for the platform.
+ *
+ * - Construct a DispatchHandle passing it your IOHandle and
+ * callback functions for read, write and disconnect events.
+ *
+ * - Ensure the DispatchHandle is not deleted until the poller is no longer using it.
+ * TODO: astitcher document DispatchHandleRef to simplify this.
+ *
+ * When an event occurs on the handle, the poller calls the relevant callback and
+ * stops watching that handle. Your callback can call rewatch() or related functions
+ * to re-enable polling.
+ */
+class DispatchHandle : public PollerHandle {
+ friend class DispatchHandleRef;
+public:
+ typedef boost::function1<void, DispatchHandle&> Callback;
+ typedef std::queue<Callback> CallbackQueue;
+
+private:
+ Callback readableCallback;
+ Callback writableCallback;
+ Callback disconnectedCallback;
+ CallbackQueue interruptedCallbacks;
+ CallbackQueue callbacks; // Double buffer
+ Poller::shared_ptr poller;
+ Mutex stateLock;
+ enum {
+ IDLE,
+ STOPPING,
+ WAITING,
+ CALLING,
+ DELETING
+ } state;
+
+public:
+ /**
+ * Provide a handle to poll and a set of callbacks. Note
+ * callbacks can be 0, meaning you are not interested in that
+ * event.
+ *
+ *@param h: the handle to watch. The IOHandle encapsulates a
+ * platfrom-specific handle to an IO object (e.g. a file descriptor
+ * on Unix.)
+ *@param rCb Callback called when the handle is readable.
+ *@param wCb Callback called when the handle is writable.
+ *@param dCb Callback called when the handle is disconnected.
+ */
+ QPID_COMMON_EXTERN DispatchHandle(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb);
+ QPID_COMMON_EXTERN ~DispatchHandle();
+
+ /** Add this DispatchHandle to the poller to be watched. */
+ QPID_COMMON_EXTERN void startWatch(Poller::shared_ptr poller);
+
+ /** Resume watching for all non-0 callbacks. */
+ QPID_COMMON_EXTERN void rewatch();
+ /** Resume watching for read only. */
+ QPID_COMMON_EXTERN void rewatchRead();
+
+ /** Resume watching for write only. */
+ QPID_COMMON_EXTERN void rewatchWrite();
+
+ /** Stop watching temporarily. The DispatchHandle remains
+ associated with the poller and can be re-activated using
+ rewatch. */
+ QPID_COMMON_EXTERN void unwatch();
+ /** Stop watching for read */
+ QPID_COMMON_EXTERN void unwatchRead();
+ /** Stop watching for write */
+ QPID_COMMON_EXTERN void unwatchWrite();
+
+ /** Stop watching permanently. Disassociates from the poller. */
+ QPID_COMMON_EXTERN void stopWatch();
+
+ /** Interrupt watching this handle and make a serialised callback that respects the
+ * same exclusivity guarantees as the other callbacks
+ */
+ QPID_COMMON_EXTERN void call(Callback iCb);
+
+protected:
+ QPID_COMMON_EXTERN void doDelete();
+
+private:
+ QPID_COMMON_EXTERN void processEvent(Poller::EventType dir);
+};
+
+class DispatchHandleRef {
+ DispatchHandle* ref;
+
+public:
+ typedef boost::function1<void, DispatchHandle&> Callback;
+ DispatchHandleRef(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) :
+ ref(new DispatchHandle(h, rCb, wCb, dCb))
+ {}
+
+ ~DispatchHandleRef() { ref->doDelete(); }
+
+ void startWatch(Poller::shared_ptr poller) { ref->startWatch(poller); }
+ void rewatch() { ref->rewatch(); }
+ void rewatchRead() { ref->rewatchRead(); }
+ void rewatchWrite() { ref->rewatchWrite(); }
+ void unwatch() { ref->unwatch(); }
+ void unwatchRead() { ref->unwatchRead(); }
+ void unwatchWrite() { ref->unwatchWrite(); }
+ void stopWatch() { ref->stopWatch(); }
+ void call(Callback iCb) { ref->call(iCb); }
+};
+
+}}
+
+#endif // _sys_DispatchHandle_h
diff --git a/qpid/cpp/src/qpid/sys/Dispatcher.cpp b/qpid/cpp/src/qpid/sys/Dispatcher.cpp
new file mode 100644
index 0000000000..5f52dcd990
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Dispatcher.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Dispatcher.h"
+
+#include <assert.h>
+
+namespace qpid {
+namespace sys {
+
+Dispatcher::Dispatcher(Poller::shared_ptr poller0) :
+ poller(poller0) {
+}
+
+Dispatcher::~Dispatcher() {
+}
+
+void Dispatcher::run() {
+ poller->run();
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/Dispatcher.h b/qpid/cpp/src/qpid/sys/Dispatcher.h
new file mode 100644
index 0000000000..e8213d0579
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Dispatcher.h
@@ -0,0 +1,44 @@
+#ifndef _sys_Dispatcher_h
+#define _sys_Dispatcher_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace sys {
+
+class Dispatcher : public Runnable {
+ const Poller::shared_ptr poller;
+
+public:
+ QPID_COMMON_EXTERN Dispatcher(Poller::shared_ptr poller);
+ QPID_COMMON_EXTERN ~Dispatcher();
+
+ QPID_COMMON_EXTERN void run();
+};
+
+}}
+
+#endif // _sys_Dispatcher_h
diff --git a/qpid/cpp/src/qpid/sys/FileSysDir.h b/qpid/cpp/src/qpid/sys/FileSysDir.h
new file mode 100755
index 0000000000..ffe7823f0a
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/FileSysDir.h
@@ -0,0 +1,62 @@
+#ifndef QPID_SYS_FILESYSDIR_H
+#define QPID_SYS_FILESYSDIR_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * @class FileSysDir
+ *
+ * Represents a filesystem directory accessible from the local host.
+ * This class simply checks existence of, and creates, a directory. It could
+ * be added to later to list contents, etc.
+ */
+class FileSysDir
+{
+ const std::string dirPath;
+
+ public:
+
+ FileSysDir (std::string path) : dirPath(path) {}
+ ~FileSysDir () {}
+
+ /**
+ * Check to see if the directory exists and is a directory. Throws an
+ * exception if there is an error checking existence or if the path
+ * exists but is not a directory.
+ *
+ * @retval true if the path exists and is a directory.
+ * @retval false if the path does not exist.
+ */
+ bool exists (void) const;
+
+ void mkdir(void);
+
+ std::string getPath () { return dirPath; }
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_FILESYSDIR_H*/
diff --git a/qpid/cpp/src/qpid/sys/Fork.h b/qpid/cpp/src/qpid/sys/Fork.h
new file mode 100644
index 0000000000..4ec061f7bc
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Fork.h
@@ -0,0 +1,24 @@
+#ifndef QPID_SYS_FORK_H
+#define QPID_SYS_FORK_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "posix/Fork.h"
+
+#endif /*!QPID_SYS_FORK_H*/
diff --git a/qpid/cpp/src/qpid/sys/LockFile.h b/qpid/cpp/src/qpid/sys/LockFile.h
new file mode 100644
index 0000000000..14a76cbf3e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/LockFile.h
@@ -0,0 +1,64 @@
+#ifndef _sys_LockFile_h
+#define _sys_LockFile_h
+
+/*
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+#include "qpid/CommonImportExport.h"
+#include "qpid/sys/IntegerTypes.h"
+
+namespace qpid {
+namespace sys {
+
+class LockFilePrivate;
+
+/**
+ * @class LockFile
+ *
+ * LockFile represents a locked file suitable for a coarse-grain system
+ * lock. For example, the broker uses this to ensure that only one broker
+ * runs. A common usage idiom is to store the current "owner" process ID
+ * in the lock file - if the lock file exists, but the stored process ID
+ * doesn't, the old owner has probably died without cleaning up the lock
+ * file.
+ */
+class LockFile : private boost::noncopyable
+{
+ std::string path;
+ bool created;
+ boost::shared_ptr<LockFilePrivate> impl;
+
+protected:
+ int read(void*, size_t) const;
+ int write(void*, size_t) const;
+
+public:
+ QPID_COMMON_EXTERN LockFile(const std::string& path_, bool create);
+ QPID_COMMON_EXTERN ~LockFile();
+};
+
+}} /* namespace qpid::sys */
+
+#endif /*!_sys_LockFile_h*/
+
+
+
diff --git a/qpid/cpp/src/qpid/sys/LockPtr.h b/qpid/cpp/src/qpid/sys/LockPtr.h
new file mode 100644
index 0000000000..738a864317
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/LockPtr.h
@@ -0,0 +1,89 @@
+#ifndef QPID_SYS_LOCKPTR_H
+#define QPID_SYS_LOCKPTR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Mutex.h"
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+namespace sys {
+
+class Mutex;
+
+/**
+ * LockPtr is a smart pointer to T. It is constructed from a volatile
+ * T* and a Lock (by default a Mutex). It const_casts away the
+ * volatile qualifier and locks the Lock for the duration of its
+ *
+ * Used in conjuntion with the "volatile" keyword to get the compiler
+ * to help enforce correct concurrent use of mutli-threaded objects.
+ * See ochttp://www.ddj.com/cpp/184403766 for a detailed discussion.
+ *
+ * To summarize the convention:
+ * - Declare thread-safe member functions as volatile.
+ * - Declare instances of the class that may be called concurrently as volatile.
+ * - Use LockPtr to cast away the volatile qualifier while taking a lock.
+ *
+ * This means that code calling on a concurrently-used object
+ * (declared volatile) can only call thread-safe (volatile) member
+ * functions. Code that needs to use thread-unsafe members must use a
+ * LockPtr, thereby acquiring the lock and making it safe to do so.
+ *
+ * A good type-safe pattern is the internally-locked object:
+ * - It has it's own private lock member.
+ * - All public functions are thread safe and declared volatile.
+ * - Any thread-unsafe, non-volatile functions are private.
+ * - Only member function implementations use LockPtr to access private functions.
+ *
+ * This encapsulates all the locking logic inside the class.
+ *
+ * One nice feature of this convention is the common case where you
+ * need a public, locked version of some function foo() and also a
+ * private unlocked version to avoid recursive locks. They can be declared as
+ * volatile and non-volatile overloads of the same function:
+ *
+ * // public
+ * void Thing::foo() volatile { LockPtr<Thing>(this, myLock)->foo(); }
+ * // private
+ * void Thing::foo() { ... do stuff ...}
+ */
+
+template <class T, class Lock> class LockPtr : public boost::noncopyable {
+ public:
+ LockPtr(volatile T* p, Lock& l) : ptr(const_cast<T*>(p)), lock(l) { lock.lock(); }
+ LockPtr(volatile T* p, volatile Lock& l) : ptr(const_cast<T*>(p)), lock(const_cast<Lock&>(l)) { lock.lock(); }
+ ~LockPtr() { lock.unlock(); }
+
+ T& operator*() { return *ptr; }
+ T* operator->() { return ptr; }
+
+ private:
+ T* ptr;
+ Lock& lock;
+};
+
+
+}} // namespace qpid::sys
+
+
+#endif /*!QPID_SYS_LOCKPTR_H*/
diff --git a/qpid/cpp/src/qpid/sys/OutputControl.h b/qpid/cpp/src/qpid/sys/OutputControl.h
new file mode 100644
index 0000000000..eae99beb0f
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/OutputControl.h
@@ -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.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+
+#ifndef _OutputControl_
+#define _OutputControl_
+
+namespace qpid {
+namespace sys {
+
+ class OutputControl
+ {
+ public:
+ virtual ~OutputControl() {}
+ virtual void abort() = 0;
+ virtual void activateOutput() = 0;
+ virtual void giveReadCredit(int32_t credit) = 0;
+ };
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/OutputTask.h b/qpid/cpp/src/qpid/sys/OutputTask.h
new file mode 100644
index 0000000000..fb08a63cd0
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/OutputTask.h
@@ -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.
+ *
+ */
+#ifndef _OutputTask_
+#define _OutputTask_
+
+namespace qpid {
+namespace sys {
+
+ class OutputTask
+ {
+ public:
+ virtual ~OutputTask() {}
+ /** Generate some output.
+ *@return true if output was generated, false if there is no work to do.
+ */
+ virtual bool doOutput() = 0;
+ };
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/PipeHandle.h b/qpid/cpp/src/qpid/sys/PipeHandle.h
new file mode 100755
index 0000000000..8aac76996b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/PipeHandle.h
@@ -0,0 +1,51 @@
+#ifndef _sys_PipeHandle_h
+#define _sys_PipeHandle_h
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <string>
+
+// This class is a portability wrapper around pipe fds.
+// It currently exists primarily and solely for the purpose of
+// integration with single-threaded components that require QMF
+// integration through a signalling fd.
+
+namespace qpid {
+namespace sys {
+
+ class PipeHandle {
+ private:
+ int writeFd;
+ int readFd;
+ public:
+ QPID_COMMON_EXTERN PipeHandle(bool nonBlocking=true);
+ QPID_COMMON_EXTERN ~PipeHandle();
+ QPID_COMMON_EXTERN int read(void* buf, size_t bufSize);
+ QPID_COMMON_EXTERN int write(const void* buf, size_t bufSize);
+ QPID_COMMON_EXTERN int getReadHandle();
+ };
+
+}}
+
+#endif /*!_sys_PipeHandle_h*/
diff --git a/qpid/cpp/src/qpid/sys/PollableCondition.h b/qpid/cpp/src/qpid/sys/PollableCondition.h
new file mode 100644
index 0000000000..2eb6f2d947
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/PollableCondition.h
@@ -0,0 +1,64 @@
+#ifndef QPID_SYS_POLLABLECONDITION_H
+#define QPID_SYS_POLLABLECONDITION_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+
+namespace qpid {
+namespace sys {
+
+class PollableConditionPrivate;
+
+class PollableCondition {
+public:
+ typedef boost::function1<void, PollableCondition&> Callback;
+
+ QPID_COMMON_EXTERN PollableCondition(const Callback& cb,
+ const boost::shared_ptr<sys::Poller>& poller);
+
+ QPID_COMMON_EXTERN ~PollableCondition();
+
+ /**
+ * Set the condition. Triggers callback to Callback from Poller.
+ */
+ QPID_COMMON_EXTERN void set();
+
+ /**
+ * Clear the condition. Stops callbacks from Poller.
+ */
+ QPID_COMMON_EXTERN void clear();
+
+ private:
+ PollableConditionPrivate *impl;
+
+ Callback callback;
+ boost::shared_ptr<sys::Poller> poller;
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_POLLABLECONDITION_H*/
diff --git a/qpid/cpp/src/qpid/sys/PollableQueue.h b/qpid/cpp/src/qpid/sys/PollableQueue.h
new file mode 100644
index 0000000000..81c2301c1e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/PollableQueue.h
@@ -0,0 +1,176 @@
+#ifndef QPID_SYS_POLLABLEQUEUE_H
+#define QPID_SYS_POLLABLEQUEUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <vector>
+
+namespace qpid {
+namespace sys {
+
+class Poller;
+
+/**
+ * A queue whose item processing is dispatched by sys::Poller.
+ * Any thread can push to the queue; items pushed trigger an event the Poller
+ * recognizes. When a Poller I/O thread dispatches the event, a
+ * user-specified callback is invoked with all items on the queue.
+ */
+template <class T>
+class PollableQueue {
+ public:
+ typedef std::vector<T> Batch;
+ typedef T value_type;
+
+ /**
+ * Callback to process a batch of items from the queue.
+ *
+ * @param batch Queue of values to process. Any items remaining
+ * on return from Callback are put back on the queue.
+ * @return iterator pointing to the first un-processed item in batch.
+ * Items from this point up to batch.end() are put back on the queue.
+ */
+ typedef boost::function<typename Batch::const_iterator (const Batch& batch)> Callback;
+
+ /**
+ * Constructor; sets necessary parameters.
+ *
+ * @param cb Callback that will be called to process items on the
+ * queue. Will be called from a Poller I/O thread.
+ * @param poller Poller to use for dispatching queue events.
+ */
+ PollableQueue(const Callback& cb,
+ const boost::shared_ptr<sys::Poller>& poller);
+
+ ~PollableQueue();
+
+ /** Push a value onto the queue. Thread safe */
+ void push(const T& t);
+
+ /** Start polling. */
+ void start();
+
+ /** Stop polling and wait for the current callback, if any, to complete. */
+ void stop();
+
+ /** Are we currently stopped?*/
+ bool isStopped() const { ScopedLock l(lock); return stopped; }
+
+ size_t size() { ScopedLock l(lock); return queue.size(); }
+ bool empty() { ScopedLock l(lock); return queue.empty(); }
+
+ /**
+ * Allow any queued events to be processed; intended for calling
+ * after all dispatch threads exit the Poller loop in order to
+ * ensure clean shutdown with no events left on the queue.
+ */
+ void shutdown();
+
+ private:
+ typedef sys::Monitor::ScopedLock ScopedLock;
+ typedef sys::Monitor::ScopedUnlock ScopedUnlock;
+
+ void dispatch(PollableCondition& cond);
+ void process();
+
+ mutable sys::Monitor lock;
+ Callback callback;
+ PollableCondition condition;
+ Batch queue, batch;
+ Thread dispatcher;
+ bool stopped;
+};
+
+template <class T> PollableQueue<T>::PollableQueue(
+ const Callback& cb, const boost::shared_ptr<sys::Poller>& p)
+ : callback(cb),
+ condition(boost::bind(&PollableQueue<T>::dispatch, this, _1), p),
+ stopped(true)
+{
+}
+
+template <class T> void PollableQueue<T>::start() {
+ ScopedLock l(lock);
+ if (!stopped) return;
+ stopped = false;
+ if (!queue.empty()) condition.set();
+}
+
+template <class T> PollableQueue<T>::~PollableQueue() {
+}
+
+template <class T> void PollableQueue<T>::push(const T& t) {
+ ScopedLock l(lock);
+ if (queue.empty() && !stopped) condition.set();
+ queue.push_back(t);
+}
+
+template <class T> void PollableQueue<T>::dispatch(PollableCondition& cond) {
+ ScopedLock l(lock);
+ assert(!dispatcher);
+ dispatcher = Thread::current();
+ process();
+ dispatcher = Thread();
+ if (queue.empty()) cond.clear();
+ if (stopped) lock.notifyAll();
+}
+
+template <class T> void PollableQueue<T>::process() {
+ // Called with lock held
+ while (!stopped && !queue.empty()) {
+ assert(batch.empty());
+ batch.swap(queue);
+ typename Batch::const_iterator putBack;
+ {
+ ScopedUnlock u(lock); // Allow concurrent push to queue.
+ putBack = callback(batch);
+ }
+ // put back unprocessed items.
+ queue.insert(queue.begin(), putBack, typename Batch::const_iterator(batch.end()));
+ batch.clear();
+ }
+}
+
+template <class T> void PollableQueue<T>::shutdown() {
+ ScopedLock l(lock);
+ process();
+}
+
+template <class T> void PollableQueue<T>::stop() {
+ ScopedLock l(lock);
+ if (stopped) return;
+ condition.clear();
+ stopped = true;
+ // Avoid deadlock if stop is called from the dispatch thread
+ if (dispatcher && dispatcher != Thread::current())
+ while (dispatcher) lock.wait();
+}
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_POLLABLEQUEUE_H*/
diff --git a/qpid/cpp/src/qpid/sys/Poller.h b/qpid/cpp/src/qpid/sys/Poller.h
new file mode 100644
index 0000000000..01ee139ee6
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Poller.h
@@ -0,0 +1,135 @@
+#ifndef _sys_Poller_h
+#define _sys_Poller_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/shared_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Poller is an abstract base class that registers callbacks to be
+ * called when there is IO activity. Concrete derived classes
+ * implement polling APIs such as epoll or equivalents on other
+ * operating systems.
+ *
+ * On the broker, Connection::received() is called with incoming
+ * frames from clients, and Connection::doOutput() is called when a
+ * connection is writeable.
+ *
+ * @see DispatchHandler for more details of normal use.
+ */
+class PollerHandle;
+class PollerPrivate;
+class Poller : public Runnable {
+ PollerPrivate* const impl;
+
+public:
+ typedef boost::shared_ptr<Poller> shared_ptr;
+
+ enum Direction {
+ NONE = 0,
+ INPUT,
+ OUTPUT,
+ INOUT
+ };
+
+ enum EventType {
+ INVALID = 0,
+ READABLE,
+ WRITABLE,
+ READ_WRITABLE,
+ DISCONNECTED,
+ SHUTDOWN,
+ TIMEOUT,
+ INTERRUPTED
+ };
+
+ struct Event {
+ PollerHandle* handle;
+ EventType type;
+
+ Event(PollerHandle* handle0, EventType type0) :
+ handle(handle0),
+ type(type0) {
+ }
+
+ void process();
+ };
+
+ QPID_COMMON_EXTERN Poller();
+ QPID_COMMON_EXTERN ~Poller();
+ /** Note: this function is async-signal safe */
+ QPID_COMMON_EXTERN void shutdown();
+
+ // Interrupt waiting for a specific poller handle
+ // returns true if we could interrupt the handle
+ // - in this case on return the handle is no longer being monitored,
+ // but we will receive an event from some invocation of poller::wait
+ // with the handle and the INTERRUPTED event type
+ // if it returns false then the handle is not being monitored by the poller
+ // - This can either be because it has just received an event which has been
+ // reported and has not been reenabled since.
+ // - Because it was removed from the monitoring set
+ // - Or because it is already being interrupted
+ QPID_COMMON_EXTERN bool interrupt(PollerHandle& handle);
+
+ // Poller run loop
+ QPID_COMMON_EXTERN void run();
+
+ QPID_COMMON_EXTERN void registerHandle(PollerHandle& handle);
+ QPID_COMMON_EXTERN void unregisterHandle(PollerHandle& handle);
+ QPID_COMMON_EXTERN void monitorHandle(PollerHandle& handle, Direction dir);
+ QPID_COMMON_EXTERN void unmonitorHandle(PollerHandle& handle, Direction dir);
+ QPID_COMMON_EXTERN Event wait(Duration timeout = TIME_INFINITE);
+
+ QPID_COMMON_EXTERN bool hasShutdown();
+};
+
+/**
+ * Handle class to use for polling
+ */
+class IOHandle;
+class PollerHandlePrivate;
+class PollerHandle {
+ friend class Poller;
+ friend class PollerPrivate;
+ friend struct Poller::Event;
+
+ PollerHandlePrivate* const impl;
+ QPID_COMMON_INLINE_EXTERN virtual void processEvent(Poller::EventType) {};
+
+public:
+ QPID_COMMON_EXTERN PollerHandle(const IOHandle& h);
+ QPID_COMMON_EXTERN virtual ~PollerHandle();
+};
+
+inline void Poller::Event::process() {
+ handle->processEvent(type);
+}
+
+}}
+#endif // _sys_Poller_h
diff --git a/qpid/cpp/src/qpid/sys/ProtocolFactory.h b/qpid/cpp/src/qpid/sys/ProtocolFactory.h
new file mode 100644
index 0000000000..4d198a92da
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ProtocolFactory.h
@@ -0,0 +1,57 @@
+#ifndef _sys_ProtocolFactory_h
+#define _sys_ProtocolFactory_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/SharedObject.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace sys {
+
+class Poller;
+
+class ProtocolFactory : public qpid::SharedObject<ProtocolFactory>
+{
+ public:
+ typedef boost::function2<void, int, std::string> ConnectFailedCallback;
+
+ virtual ~ProtocolFactory() = 0;
+ virtual uint16_t getPort() const = 0;
+ virtual void accept(boost::shared_ptr<Poller>, ConnectionCodec::Factory*) = 0;
+ virtual void connect(
+ boost::shared_ptr<Poller>,
+ const std::string& host, const std::string& port,
+ ConnectionCodec::Factory* codec,
+ ConnectFailedCallback failed) = 0;
+ virtual bool supports(const std::string& /*capability*/) { return false; }
+};
+
+inline ProtocolFactory::~ProtocolFactory() {}
+
+}}
+
+
+
+#endif //!_sys_ProtocolFactory_h
diff --git a/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp
new file mode 100644
index 0000000000..631d116b41
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/rdma/RdmaIO.h"
+#include "qpid/sys/rdma/rdma_exception.h"
+#include "qpid/sys/OutputControl.h"
+#include "qpid/sys/SecuritySettings.h"
+
+#include <boost/bind.hpp>
+#include <memory>
+
+#include <netdb.h>
+
+using std::auto_ptr;
+using std::string;
+using std::stringstream;
+
+namespace qpid {
+namespace sys {
+
+class RdmaIOHandler : public OutputControl {
+ std::string identifier;
+ ConnectionCodec::Factory* factory;
+ ConnectionCodec* codec;
+ bool readError;
+
+ sys::Mutex pollingLock;
+ bool polling;
+
+ Rdma::AsynchIO* aio;
+ Rdma::Connection::intrusive_ptr connection;
+
+ void write(const framing::ProtocolInitiation&);
+ void disconnectAction();
+
+ public:
+ RdmaIOHandler(Rdma::Connection::intrusive_ptr c, ConnectionCodec::Factory* f);
+ ~RdmaIOHandler();
+ void init(Rdma::AsynchIO* a);
+ void start(Poller::shared_ptr poller);
+
+ // Output side
+ void close();
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t credit);
+ void initProtocolOut();
+
+ // Input side
+ void readbuff(Rdma::AsynchIO& aio, Rdma::Buffer* buff);
+ void initProtocolIn(Rdma::Buffer* buff);
+
+ // Notifications
+ void full(Rdma::AsynchIO& aio);
+ void idle(Rdma::AsynchIO& aio);
+ void error(Rdma::AsynchIO& aio);
+ void disconnected();
+ void drained();
+};
+
+RdmaIOHandler::RdmaIOHandler(Rdma::Connection::intrusive_ptr c, qpid::sys::ConnectionCodec::Factory* f) :
+ identifier(c->getFullName()),
+ factory(f),
+ codec(0),
+ readError(false),
+ polling(false),
+ connection(c)
+{
+}
+
+RdmaIOHandler::~RdmaIOHandler() {
+ if (codec)
+ codec->closed();
+ delete codec;
+ delete aio;
+}
+
+void RdmaIOHandler::init(Rdma::AsynchIO* a) {
+ aio = a;
+}
+
+void RdmaIOHandler::start(Poller::shared_ptr poller) {
+ Mutex::ScopedLock l(pollingLock);
+ assert(!polling);
+
+ polling = true;
+
+ aio->start(poller);
+}
+
+void RdmaIOHandler::write(const framing::ProtocolInitiation& data)
+{
+ QPID_LOG(debug, "Rdma: SENT [" << identifier << "] INIT(" << data << ")");
+ Rdma::Buffer* buff = aio->getSendBuffer();
+ assert(buff);
+ framing::Buffer out(buff->bytes(), buff->byteCount());
+ data.encode(out);
+ buff->dataCount(data.encodedSize());
+ aio->queueWrite(buff);
+}
+
+void RdmaIOHandler::close() {
+ aio->drainWriteQueue(boost::bind(&RdmaIOHandler::drained, this));
+}
+
+// TODO: Dummy implementation, need to fill this in for heartbeat timeout to work
+void RdmaIOHandler::abort() {
+}
+
+void RdmaIOHandler::activateOutput() {
+ aio->notifyPendingWrite();
+}
+
+void RdmaIOHandler::idle(Rdma::AsynchIO&) {
+ // TODO: Shouldn't need this test as idle() should only ever be called when
+ // the connection is writable anyway
+ if ( !aio->writable() ) {
+ return;
+ }
+ if (codec == 0) return;
+ if (!codec->canEncode()) {
+ return;
+ }
+ Rdma::Buffer* buff = aio->getSendBuffer();
+ if (buff) {
+ size_t encoded=codec->encode(buff->bytes(), buff->byteCount());
+ buff->dataCount(encoded);
+ aio->queueWrite(buff);
+ if (codec->isClosed()) {
+ close();
+ }
+ }
+}
+
+void RdmaIOHandler::initProtocolOut() {
+ // We mustn't have already started the conversation
+ // but we must be able to send
+ assert( codec == 0 );
+ assert( aio->writable() );
+ codec = factory->create(*this, identifier, SecuritySettings());
+ write(framing::ProtocolInitiation(codec->getVersion()));
+}
+
+void RdmaIOHandler::error(Rdma::AsynchIO&) {
+ disconnected();
+}
+
+namespace {
+ void stopped(RdmaIOHandler* async) {
+ delete async;
+ }
+}
+
+void RdmaIOHandler::disconnectAction() {
+ {
+ Mutex::ScopedLock l(pollingLock);
+ // If we're closed already then we'll get to drained() anyway
+ if (!polling) return;
+ polling = false;
+ }
+ aio->stop(boost::bind(&stopped, this));
+}
+
+void RdmaIOHandler::disconnected() {
+ aio->requestCallback(boost::bind(&RdmaIOHandler::disconnectAction, this));
+}
+
+void RdmaIOHandler::drained() {
+ // We know we've drained the write queue now, but we don't have to do anything
+ // because we can rely on the client to disconnect to trigger the connection
+ // cleanup.
+}
+
+void RdmaIOHandler::full(Rdma::AsynchIO&) {
+ QPID_LOG(debug, "Rdma: buffer full [" << identifier << "]");
+}
+
+// TODO: Dummy implementation of read throttling
+void RdmaIOHandler::giveReadCredit(int32_t) {
+}
+
+// The logic here is subtly different from TCP as RDMA is message oriented
+// so we define that an RDMA message is a frame - in this case there is no putting back
+// of any message remainder - there shouldn't be any. And what we read here can't be
+// smaller than a frame
+void RdmaIOHandler::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) {
+ if (readError) {
+ return;
+ }
+ size_t decoded = 0;
+ try {
+ if (codec) {
+ decoded = codec->decode(buff->bytes(), buff->dataCount());
+ }else{
+ // Need to start protocol processing
+ initProtocolIn(buff);
+ }
+ }catch(const std::exception& e){
+ QPID_LOG(error, e.what());
+ readError = true;
+ close();
+ }
+}
+
+void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) {
+ framing::Buffer in(buff->bytes(), buff->dataCount());
+ framing::ProtocolInitiation protocolInit;
+ size_t decoded = 0;
+ if (protocolInit.decode(in)) {
+ decoded = in.getPosition();
+ QPID_LOG(debug, "Rdma: RECV [" << identifier << "] INIT(" << protocolInit << ")");
+
+ codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
+
+ // If we failed to create the codec then we don't understand the offered protocol version
+ if (!codec) {
+ // send valid version header & close connection.
+ write(framing::ProtocolInitiation(framing::highestProtocolVersion));
+ readError = true;
+ close();
+ }
+ }
+}
+
+class RdmaIOProtocolFactory : public ProtocolFactory {
+ auto_ptr<Rdma::Listener> listener;
+ const uint16_t listeningPort;
+
+ public:
+ RdmaIOProtocolFactory(int16_t port, int backlog);
+ void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
+ void connect(Poller::shared_ptr, const string& host, const std::string& port, ConnectionCodec::Factory*, ConnectFailedCallback);
+
+ uint16_t getPort() const;
+
+ private:
+ bool request(Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&, ConnectionCodec::Factory*);
+ void established(Poller::shared_ptr, Rdma::Connection::intrusive_ptr);
+ void connected(Poller::shared_ptr, Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&, ConnectionCodec::Factory*);
+ void connectionError(Rdma::Connection::intrusive_ptr, Rdma::ErrorType);
+ void disconnected(Rdma::Connection::intrusive_ptr);
+ void rejected(Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&, ConnectFailedCallback);
+};
+
+// Static instance to initialise plugin
+static class RdmaIOPlugin : public Plugin {
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ // Check whether we actually have any rdma devices
+ if ( Rdma::deviceCount() == 0 ) {
+ QPID_LOG(info, "Rdma: Disabled: no rdma devices found");
+ return;
+ }
+
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocol(new RdmaIOProtocolFactory(opts.port, opts.connectionBacklog));
+ QPID_LOG(notice, "Rdma: Listening on RDMA port " << protocol->getPort());
+ broker->registerProtocolFactory("rdma", protocol);
+ }
+ }
+} rdmaPlugin;
+
+RdmaIOProtocolFactory::RdmaIOProtocolFactory(int16_t port, int /*backlog*/) :
+ listeningPort(port)
+{}
+
+void RdmaIOProtocolFactory::established(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr ci) {
+ RdmaIOHandler* async = ci->getContext<RdmaIOHandler>();
+ async->start(poller);
+}
+
+bool RdmaIOProtocolFactory::request(Rdma::Connection::intrusive_ptr ci, const Rdma::ConnectionParams& cp,
+ ConnectionCodec::Factory* f) {
+ try {
+ if (cp.rdmaProtocolVersion == 0) {
+ QPID_LOG(warning, "Rdma: connection from protocol version 0 client");
+ }
+ RdmaIOHandler* async = new RdmaIOHandler(ci, f);
+ Rdma::AsynchIO* aio =
+ new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.rdmaProtocolVersion,
+ cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES,
+ boost::bind(&RdmaIOHandler::readbuff, async, _1, _2),
+ boost::bind(&RdmaIOHandler::idle, async, _1),
+ 0, // boost::bind(&RdmaIOHandler::full, async, _1),
+ boost::bind(&RdmaIOHandler::error, async, _1));
+ async->init(aio);
+
+ // Record aio so we can get it back from a connection
+ ci->addContext(async);
+ return true;
+ } catch (const Rdma::Exception& e) {
+ QPID_LOG(error, "Rdma: Cannot accept new connection (Rdma exception): " << e.what());
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Rdma: Cannot accept new connection (unknown exception): " << e.what());
+ }
+
+ // If we get here we caught an exception so reject connection
+ return false;
+}
+
+void RdmaIOProtocolFactory::connectionError(Rdma::Connection::intrusive_ptr, Rdma::ErrorType) {
+}
+
+void RdmaIOProtocolFactory::disconnected(Rdma::Connection::intrusive_ptr ci) {
+ // If we've got a connection already tear it down, otherwise ignore
+ RdmaIOHandler* async = ci->getContext<RdmaIOHandler>();
+ if (async) {
+ // Make sure we don't disconnect more than once
+ ci->removeContext();
+ async->disconnected();
+ }
+}
+
+uint16_t RdmaIOProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void RdmaIOProtocolFactory::accept(Poller::shared_ptr poller, ConnectionCodec::Factory* fact) {
+ ::sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(listeningPort);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ listener.reset(
+ new Rdma::Listener(
+ Rdma::ConnectionParams(65536, Rdma::DEFAULT_WR_ENTRIES),
+ boost::bind(&RdmaIOProtocolFactory::established, this, poller, _1),
+ boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2),
+ boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1),
+ boost::bind(&RdmaIOProtocolFactory::request, this, _1, _2, fact)));
+
+ SocketAddress sa("",boost::lexical_cast<std::string>(listeningPort));
+ listener->start(poller, sa);
+}
+
+// Only used for outgoing connections (in federation)
+void RdmaIOProtocolFactory::rejected(Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&, ConnectFailedCallback failed) {
+ failed(-1, "Connection rejected");
+}
+
+// Do the same as connection request and established but mark a client too
+void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr ci, const Rdma::ConnectionParams& cp,
+ ConnectionCodec::Factory* f) {
+ (void) request(ci, cp, f);
+ established(poller, ci);
+ RdmaIOHandler* async = ci->getContext<RdmaIOHandler>();
+ async->initProtocolOut();
+}
+
+void RdmaIOProtocolFactory::connect(
+ Poller::shared_ptr poller,
+ const std::string& host, const std::string& port,
+ ConnectionCodec::Factory* f,
+ ConnectFailedCallback failed)
+{
+ Rdma::Connector* c =
+ new Rdma::Connector(
+ Rdma::ConnectionParams(8000, Rdma::DEFAULT_WR_ENTRIES),
+ boost::bind(&RdmaIOProtocolFactory::connected, this, poller, _1, _2, f),
+ boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2),
+ boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1),
+ boost::bind(&RdmaIOProtocolFactory::rejected, this, _1, _2, failed));
+
+ SocketAddress sa(host, port);
+ c->start(poller, sa);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/Runnable.cpp b/qpid/cpp/src/qpid/sys/Runnable.cpp
new file mode 100644
index 0000000000..325d87c91b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Runnable.cpp
@@ -0,0 +1,32 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/Runnable.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace sys {
+
+Runnable::~Runnable() {}
+
+Runnable::Functor Runnable::functor()
+{
+ return boost::bind(&Runnable::run, this);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/ScopedIncrement.h b/qpid/cpp/src/qpid/sys/ScopedIncrement.h
new file mode 100644
index 0000000000..8645ab2484
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ScopedIncrement.h
@@ -0,0 +1,67 @@
+#ifndef _posix_ScopedIncrement_h
+#define _posix_ScopedIncrement_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Increment counter in constructor and decrement in destructor.
+ * Optionally call a function if the decremented counter value is 0.
+ * Note the function must not throw, it is called in the destructor.
+ */
+template <class T, class F=boost::function<void()> >
+class ScopedIncrement : boost::noncopyable
+{
+ public:
+ ScopedIncrement(T& c, F f=0)
+ : count(c), callback(f) { ++count; }
+ ~ScopedIncrement() { if (--count == 0 && callback) callback(); }
+
+ private:
+ T& count;
+ F callback;
+};
+
+
+/** Decrement counter in constructor and increment in destructor. */
+template <class T>
+class ScopedDecrement : boost::noncopyable
+{
+ public:
+ ScopedDecrement(T& c) : count(c) { value = --count; }
+ ~ScopedDecrement() { ++count; }
+
+ /** Return the value after the decrement. */
+ operator long() { return value; }
+
+ private:
+ T& count;
+ long value;
+};
+
+
+}}
+
+
+#endif // _posix_ScopedIncrement_h
diff --git a/qpid/cpp/src/qpid/sys/SecurityLayer.h b/qpid/cpp/src/qpid/sys/SecurityLayer.h
new file mode 100644
index 0000000000..52bc40e352
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/SecurityLayer.h
@@ -0,0 +1,42 @@
+#ifndef QPID_SYS_SECURITYLAYER_H
+#define QPID_SYS_SECURITYLAYER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Codec.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Defines interface to a SASL negotiated Security Layer (for
+ * encryption/integrity)
+ */
+class SecurityLayer : public Codec
+{
+ public:
+ virtual void init(Codec*) = 0;
+ virtual ~SecurityLayer() {}
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_SECURITYLAYER_H*/
diff --git a/qpid/cpp/src/qpid/sys/SecuritySettings.h b/qpid/cpp/src/qpid/sys/SecuritySettings.h
new file mode 100644
index 0000000000..bfcd08fd0f
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/SecuritySettings.h
@@ -0,0 +1,58 @@
+#ifndef QPID_SYS_SECURITYSETTINGS_H
+#define QPID_SYS_SECURITYSETTINGS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace sys {
+
+/**
+ * Conveys security information from a given transport to the upper
+ * layers.
+ */
+struct SecuritySettings
+{
+ /**
+ * Security Strength Factor (SSF). Possible values are:
+ *
+ * @li 0 No security
+ * @li 1 Integrity checking only
+ * @li >1 Integrity and confidentiality with the number
+ * giving the encryption key length.
+ */
+ unsigned int ssf;
+ /**
+ * An authorisation id
+ */
+ std::string authid;
+
+ /**
+ * Disables SASL mechanisms that are vulnerable to passive
+ * dictionary-based password attacks
+ */
+ bool nodict;
+
+ SecuritySettings() : ssf(0), nodict(false) {}
+};
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_SECURITYSETTINGS_H*/
diff --git a/qpid/cpp/src/qpid/sys/Semaphore.h b/qpid/cpp/src/qpid/sys/Semaphore.h
new file mode 100644
index 0000000000..9d70f89aeb
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Semaphore.h
@@ -0,0 +1,79 @@
+#ifndef _sys_Semaphore_h
+#define _sys_Semaphore_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/Monitor.h"
+
+namespace qpid {
+namespace sys {
+
+class Semaphore
+{
+public:
+ Semaphore(uint c = 1) : count(c) {}
+
+ void lock() { acquire(); }
+ void unlock() { release(); }
+ bool trylock() { return tryAcquire(); }
+
+ bool tryAcquire()
+ {
+ Monitor::ScopedLock l(monitor);
+ if (count) {
+ count--;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void acquire()
+ {
+ Monitor::ScopedLock l(monitor);
+ while (count == 0) monitor.wait();
+ count--;
+ }
+
+ void release(uint n)
+ {
+ Monitor::ScopedLock l(monitor);
+ if (count==0) monitor.notifyAll();
+ count+=n;
+ }
+
+ void release()
+ {
+ release(1);
+ }
+
+ void forceLock()
+ {
+ Monitor::ScopedLock l(monitor);
+ count = 0;
+ }
+
+private:
+ Monitor monitor;
+ uint count;
+};
+
+}}
+
+#endif /*!_sys_Semaphore_h*/
diff --git a/qpid/cpp/src/qpid/sys/Shlib.cpp b/qpid/cpp/src/qpid/sys/Shlib.cpp
new file mode 100644
index 0000000000..342d726876
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Shlib.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Shlib.h"
+
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace sys {
+
+AutoShlib::~AutoShlib() throw() {
+ try {
+ unload();
+ } catch(const std::exception& e) {
+ QPID_LOG(error, "Unloading shared library: " << e.what());
+ }
+}
+
+// Note: Other functions are defined in apr/Shlib.cpp or posix/Shlib.cpp.
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/Shlib.h b/qpid/cpp/src/qpid/sys/Shlib.h
new file mode 100644
index 0000000000..7f66cfec14
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Shlib.h
@@ -0,0 +1,76 @@
+#ifndef QPID_SYS_SHLIB_H
+#define QPID_SYS_SHLIB_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/CommonImportExport.h"
+#include <boost/noncopyable.hpp>
+#include <iostream>
+
+namespace qpid {
+namespace sys {
+
+/** Encapsulates a shared library handle.
+ *@see AutoShlib
+ */
+class Shlib {
+ public:
+ /** Load a shared library */
+ Shlib(const char* libname) { load(libname); }
+
+ /** Load a shared library */
+ Shlib(const std::string& libname) { load(libname.c_str()); }
+
+ /** Unload shared library. */
+ QPID_COMMON_EXTERN void unload();
+
+ /** Look up symbol. */
+ QPID_COMMON_EXTERN void* getSymbol(const char* symbol);
+
+ /** Look up symbol in shared library, cast it to the desired
+ * pointer type, void* by default.
+ */
+ template <class T>
+ T getSymbol(const char* symbol) {
+ // Double cast avoids warning about casting object to function pointer
+ return reinterpret_cast<T>(reinterpret_cast<intptr_t>(
+ this->getSymbol(symbol)));
+ }
+
+ private:
+ void* handle;
+ QPID_COMMON_EXTERN void load(const char* libname);
+};
+
+/** A shared library handle that unloads the shlib in it's dtor */
+class AutoShlib : public Shlib {
+ public:
+ /** Load shared library */
+ AutoShlib(const std::string& libname) : Shlib(libname) {}
+ /** Calls unload() */
+ QPID_COMMON_EXTERN ~AutoShlib() throw();
+};
+
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_SHLIB_H*/
diff --git a/qpid/cpp/src/qpid/sys/ShutdownHandler.h b/qpid/cpp/src/qpid/sys/ShutdownHandler.h
new file mode 100644
index 0000000000..88baecb5b6
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ShutdownHandler.h
@@ -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.
+ *
+ */
+#ifndef _ShutdownHandler_
+#define _ShutdownHandler_
+
+namespace qpid {
+namespace sys {
+
+ class ShutdownHandler
+ {
+ public:
+ virtual void shutdown() = 0;
+ virtual ~ShutdownHandler(){}
+ };
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/Socket.h b/qpid/cpp/src/qpid/sys/Socket.h
new file mode 100644
index 0000000000..9f62f3be1c
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Socket.h
@@ -0,0 +1,103 @@
+#ifndef _sys_Socket_h
+#define _sys_Socket_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <string>
+
+namespace qpid {
+namespace sys {
+
+class Duration;
+class SocketAddress;
+
+class QPID_COMMON_CLASS_EXTERN Socket : public IOHandle
+{
+public:
+ /** Create a socket wrapper for descriptor. */
+ QPID_COMMON_EXTERN Socket();
+
+ /** Set socket non blocking */
+ void setNonblocking() const;
+
+ QPID_COMMON_EXTERN void setTcpNoDelay() const;
+
+ QPID_COMMON_EXTERN void connect(const std::string& host, const std::string& port) const;
+ QPID_COMMON_EXTERN void connect(const SocketAddress&) const;
+
+ QPID_COMMON_EXTERN void close() const;
+
+ /** Bind to a port and start listening.
+ *@param port 0 means choose an available port.
+ *@param backlog maximum number of pending connections.
+ *@return The bound port.
+ */
+ QPID_COMMON_EXTERN int listen(const std::string& host = "", const std::string& port = "0", int backlog = 10) const;
+ QPID_COMMON_EXTERN int listen(const SocketAddress&, int backlog = 10) const;
+
+ /**
+ * Returns an address (host and port) for the remote end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getPeerAddress() const;
+ /**
+ * Returns an address (host and port) for the local end of the
+ * socket
+ */
+ QPID_COMMON_EXTERN std::string getLocalAddress() const;
+
+ /**
+ * Returns the full address of the connection: local and remote host and port.
+ */
+ QPID_COMMON_INLINE_EXTERN std::string getFullAddress() const { return getLocalAddress()+"-"+getPeerAddress(); }
+
+ /**
+ * Returns the error code stored in the socket. This may be used
+ * to determine the result of a non-blocking connect.
+ */
+ int getError() const;
+
+ /** Accept a connection from a socket that is already listening
+ * and has an incoming connection
+ */
+ QPID_COMMON_EXTERN Socket* accept() const;
+
+ // TODO The following are raw operations, maybe they need better wrapping?
+ QPID_COMMON_EXTERN int read(void *buf, size_t count) const;
+ QPID_COMMON_EXTERN int write(const void *buf, size_t count) const;
+
+private:
+ /** Create socket */
+ void createSocket(const SocketAddress&) const;
+
+ Socket(IOHandlePrivate*);
+ mutable std::string localname;
+ mutable std::string peername;
+ mutable bool nonblocking;
+ mutable bool nodelay;
+};
+
+}}
+#endif /*!_sys_Socket_h*/
diff --git a/qpid/cpp/src/qpid/sys/SocketAddress.h b/qpid/cpp/src/qpid/sys/SocketAddress.h
new file mode 100644
index 0000000000..c2120338cf
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/SocketAddress.h
@@ -0,0 +1,53 @@
+#ifndef _sys_SocketAddress_h
+#define _sys_SocketAddress_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
+#include <string>
+
+struct addrinfo;
+
+namespace qpid {
+namespace sys {
+
+class SocketAddress {
+ friend const ::addrinfo& getAddrInfo(const SocketAddress&);
+
+public:
+ /** Create a SocketAddress from hostname and port*/
+ QPID_COMMON_EXTERN SocketAddress(const std::string& host, const std::string& port);
+ QPID_COMMON_EXTERN SocketAddress(const SocketAddress&);
+ QPID_COMMON_EXTERN SocketAddress& operator=(const SocketAddress&);
+ QPID_COMMON_EXTERN ~SocketAddress();
+
+ std::string asString(bool numeric=true) const;
+
+private:
+ std::string host;
+ std::string port;
+ mutable ::addrinfo* addrInfo;
+};
+
+}}
+#endif /*!_sys_SocketAddress_h*/
diff --git a/qpid/cpp/src/qpid/sys/SslPlugin.cpp b/qpid/cpp/src/qpid/sys/SslPlugin.cpp
new file mode 100644
index 0000000000..471a0cef60
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/SslPlugin.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/sys/ssl/check.h"
+#include "qpid/sys/ssl/util.h"
+#include "qpid/sys/ssl/SslHandler.h"
+#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/bind.hpp>
+#include <memory>
+
+
+namespace qpid {
+namespace sys {
+
+struct SslServerOptions : ssl::SslOptions
+{
+ uint16_t port;
+ bool clientAuth;
+ bool nodict;
+
+ SslServerOptions() : port(5671),
+ clientAuth(false),
+ nodict(false)
+ {
+ addOptions()
+ ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
+ ("ssl-require-client-authentication", optValue(clientAuth),
+ "Forces clients to authenticate in order to establish an SSL connection")
+ ("ssl-sasl-no-dict", optValue(nodict),
+ "Disables SASL mechanisms that are vulnerable to passive dictionary-based password attacks");
+ }
+};
+
+class SslProtocolFactory : public ProtocolFactory {
+ const bool tcpNoDelay;
+ qpid::sys::ssl::SslSocket listener;
+ const uint16_t listeningPort;
+ std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor;
+ bool nodict;
+
+ public:
+ SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+ void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
+ void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
+ ConnectionCodec::Factory*,
+ boost::function2<void, int, std::string> failed);
+
+ uint16_t getPort() const;
+ bool supports(const std::string& capability);
+
+ private:
+ void established(Poller::shared_ptr, const qpid::sys::ssl::SslSocket&, ConnectionCodec::Factory*,
+ bool isClient);
+};
+
+// Static instance to initialise plugin
+static struct SslPlugin : public Plugin {
+ SslServerOptions options;
+
+ Options* getOptions() { return &options; }
+
+ ~SslPlugin() { ssl::shutdownNSS(); }
+
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ if (options.certDbPath.empty()) {
+ QPID_LOG(notice, "SSL plugin not enabled, you must set --ssl-cert-db to enable it.");
+ } else {
+ try {
+ ssl::initNSS(options, true);
+
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
+ opts.connectionBacklog,
+ opts.tcpNoDelay));
+ QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+ broker->registerProtocolFactory("ssl", protocol);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what());
+ }
+ }
+ }
+ }
+} sslPlugin;
+
+SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) :
+ tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)),
+ nodict(options.nodict)
+{}
+
+void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s,
+ ConnectionCodec::Factory* f, bool isClient) {
+ qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getFullAddress(), f, nodict);
+
+ if (tcpNoDelay) {
+ s.setTcpNoDelay(tcpNoDelay);
+ QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ if (isClient)
+ async->setClient();
+ qpid::sys::ssl::SslIO* aio = new qpid::sys::ssl::SslIO(s,
+ boost::bind(&qpid::sys::ssl::SslHandler::readbuff, async, _1, _2),
+ boost::bind(&qpid::sys::ssl::SslHandler::eof, async, _1),
+ boost::bind(&qpid::sys::ssl::SslHandler::disconnect, async, _1),
+ boost::bind(&qpid::sys::ssl::SslHandler::closedSocket, async, _1, _2),
+ boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1),
+ boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1));
+
+ async->init(aio, 4);
+ aio->start(poller);
+}
+
+uint16_t SslProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void SslProtocolFactory::accept(Poller::shared_ptr poller,
+ ConnectionCodec::Factory* fact) {
+ acceptor.reset(
+ new qpid::sys::ssl::SslAcceptor(listener,
+ boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
+ acceptor->start(poller);
+}
+
+void SslProtocolFactory::connect(
+ Poller::shared_ptr poller,
+ const std::string& host, const std::string& port,
+ ConnectionCodec::Factory* fact,
+ ConnectFailedCallback failed)
+{
+ // Note that the following logic does not cause a memory leak.
+ // The allocated Socket is freed either by the SslConnector
+ // upon connection failure or by the SslIoHandle upon connection
+ // shutdown. The allocated SslConnector frees itself when it
+ // is no longer needed.
+
+ qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket();
+ new qpid::sys::ssl::SslConnector (*socket, poller, host, port,
+ boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, true),
+ failed);
+}
+
+namespace
+{
+const std::string SSL = "ssl";
+}
+
+bool SslProtocolFactory::supports(const std::string& capability)
+{
+ std::string s = capability;
+ transform(s.begin(), s.end(), s.begin(), tolower);
+ return s == SSL;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/StateMonitor.h b/qpid/cpp/src/qpid/sys/StateMonitor.h
new file mode 100644
index 0000000000..eac37a8543
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/StateMonitor.h
@@ -0,0 +1,78 @@
+#ifndef QPID_SYS_STATEMONITOR_H
+#define QPID_SYS_STATEMONITOR_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/Waitable.h"
+
+#include <bitset>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A monitor with an enum state value.
+ *
+ *@param Enum: enum type to use for states.
+ *@param EnumMax: Highest enum value.
+ */
+template <class Enum, size_t MaxEnum>
+class StateMonitor : public Waitable
+{
+ public:
+ struct Set : public std::bitset<MaxEnum + 1> {
+ Set() {}
+ Set(Enum s) { set(s); }
+ Set(Enum s, Enum t) { std::bitset<MaxEnum + 1>::set(s).set(t); }
+ Set(Enum s, Enum t, Enum u) { std::bitset<MaxEnum + 1>::set(s).set(t).set(u); }
+ Set(Enum s, Enum t, Enum u, Enum v) { std::bitset<MaxEnum + 1>::set(s).set(t).set(u).set(v); }
+ };
+
+
+ StateMonitor(Enum initial) { state=initial; }
+
+ /** @pre Caller holds a ScopedLock. */
+ void set(Enum s) { state=s; notifyAll(); }
+ /** @pre Caller holds a ScopedLock. */
+ StateMonitor& operator=(Enum s) { set(s); return *this; }
+
+ /** @pre Caller holds a ScopedLock. */
+ Enum get() const { return state; }
+ /** @pre Caller holds a ScopedLock. */
+ operator Enum() const { return state; }
+
+ /** @pre Caller holds a ScopedLock */
+ void waitFor(Enum s) { ScopedWait w(*this); while (s != state) wait(); }
+ /** @pre Caller holds a ScopedLock */
+ void waitFor(Set s) { ScopedWait w(*this); while (!s.test(state)) wait(); }
+ /** @pre Caller holds a ScopedLock */
+ void waitNot(Enum s) { ScopedWait w(*this); while (s == state) wait(); }
+ /** @pre Caller holds a ScopedLock */
+ void waitNot(Set s) { ScopedWait w(*this); while (s.test(state)) wait(); }
+
+ private:
+ Enum state;
+};
+
+}}
+
+
+#endif /*!QPID_SYS_STATEMONITOR_H*/
diff --git a/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp
new file mode 100644
index 0000000000..34338ce434
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/ProtocolFactory.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/AsynchIO.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/bind.hpp>
+#include <memory>
+
+namespace qpid {
+namespace sys {
+
+class AsynchIOProtocolFactory : public ProtocolFactory {
+ const bool tcpNoDelay;
+ Socket listener;
+ const uint16_t listeningPort;
+ std::auto_ptr<AsynchAcceptor> acceptor;
+
+ public:
+ AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay);
+ void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
+ void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
+ ConnectionCodec::Factory*,
+ ConnectFailedCallback);
+
+ uint16_t getPort() const;
+
+ private:
+ void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*,
+ bool isClient);
+ void connectFailed(const Socket&, int, const std::string&, ConnectFailedCallback);
+};
+
+// Static instance to initialise plugin
+static class TCPIOPlugin : public Plugin {
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocolt(
+ new AsynchIOProtocolFactory(
+ "", boost::lexical_cast<std::string>(opts.port),
+ opts.connectionBacklog,
+ opts.tcpNoDelay));
+ QPID_LOG(notice, "Listening on TCP port " << protocolt->getPort());
+ broker->registerProtocolFactory("tcp", protocolt);
+ }
+ }
+} tcpPlugin;
+
+AsynchIOProtocolFactory::AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay) :
+ tcpNoDelay(nodelay), listeningPort(listener.listen(host, port, backlog))
+{}
+
+void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f, bool isClient) {
+ AsynchIOHandler* async = new AsynchIOHandler(s.getFullAddress(), f);
+
+ if (tcpNoDelay) {
+ s.setTcpNoDelay();
+ QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ if (isClient)
+ async->setClient();
+ AsynchIO* aio = AsynchIO::create
+ (s,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ async->init(aio, 4);
+ aio->start(poller);
+}
+
+uint16_t AsynchIOProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller,
+ ConnectionCodec::Factory* fact) {
+ acceptor.reset(
+ AsynchAcceptor::create(listener,
+ boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false)));
+ acceptor->start(poller);
+}
+
+void AsynchIOProtocolFactory::connectFailed(
+ const Socket& s, int ec, const std::string& emsg,
+ ConnectFailedCallback failedCb)
+{
+ failedCb(ec, emsg);
+ s.close();
+ delete &s;
+}
+
+void AsynchIOProtocolFactory::connect(
+ Poller::shared_ptr poller,
+ const std::string& host, const std::string& port,
+ ConnectionCodec::Factory* fact,
+ ConnectFailedCallback failed)
+{
+ // Note that the following logic does not cause a memory leak.
+ // The allocated Socket is freed either by the AsynchConnector
+ // upon connection failure or by the AsynchIO upon connection
+ // shutdown. The allocated AsynchConnector frees itself when it
+ // is no longer needed.
+ Socket* socket = new Socket();
+ AsynchConnector* c = AsynchConnector::create(
+ *socket,
+ host,
+ port,
+ boost::bind(&AsynchIOProtocolFactory::established,
+ this, poller, _1, fact, true),
+ boost::bind(&AsynchIOProtocolFactory::connectFailed,
+ this, _1, _2, _3, failed));
+ c->start(poller);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/TimeoutHandler.h b/qpid/cpp/src/qpid/sys/TimeoutHandler.h
new file mode 100644
index 0000000000..0c10709bbf
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/TimeoutHandler.h
@@ -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.
+ *
+ */
+#ifndef _TimeoutHandler_
+#define _TimeoutHandler_
+
+namespace qpid {
+namespace sys {
+
+ class TimeoutHandler
+ {
+ public:
+ virtual void idleOut() = 0;
+ virtual void idleIn() = 0;
+ virtual ~TimeoutHandler(){}
+ };
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/Timer.cpp b/qpid/cpp/src/qpid/sys/Timer.cpp
new file mode 100644
index 0000000000..fdb2e8c6bb
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Timer.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/log/Statement.h"
+
+#include <numeric>
+
+using boost::intrusive_ptr;
+using std::max;
+
+namespace qpid {
+namespace sys {
+
+TimerTask::TimerTask(Duration timeout, const std::string& n) :
+ name(n),
+ sortTime(AbsTime::FarFuture()),
+ period(timeout),
+ nextFireTime(AbsTime::now(), timeout),
+ cancelled(false)
+{}
+
+TimerTask::TimerTask(AbsTime time, const std::string& n) :
+ name(n),
+ sortTime(AbsTime::FarFuture()),
+ period(0),
+ nextFireTime(time),
+ cancelled(false)
+{}
+
+TimerTask::~TimerTask() {}
+
+bool TimerTask::readyToFire() const {
+ return !(nextFireTime > AbsTime::now());
+}
+
+void TimerTask::fireTask() {
+ cancelled = true;
+ fire();
+}
+
+// This can only be used to setup the next fire time. After the Timer has already fired
+void TimerTask::setupNextFire() {
+ if (period && readyToFire()) {
+ nextFireTime = max(AbsTime::now(), AbsTime(nextFireTime, period));
+ cancelled = false;
+ } else {
+ QPID_LOG(error, name << " couldn't setup next timer firing: " << Duration(nextFireTime, AbsTime::now()) << "[" << period << "]");
+ }
+}
+
+// Only allow tasks to be delayed
+void TimerTask::restart() { nextFireTime = max(nextFireTime, AbsTime(AbsTime::now(), period)); }
+
+void TimerTask::cancel() {
+ ScopedLock<Mutex> l(callbackLock);
+ cancelled = true;
+}
+
+Timer::Timer() :
+ active(false),
+ late(50 * TIME_MSEC),
+ overran(2 * TIME_MSEC),
+ lateCancel(500 * TIME_MSEC),
+ warn(5 * TIME_SEC)
+{
+ start();
+}
+
+Timer::~Timer()
+{
+ stop();
+}
+
+// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary
+void Timer::run()
+{
+ Monitor::ScopedLock l(monitor);
+ while (active) {
+ if (tasks.empty()) {
+ monitor.wait();
+ } else {
+ intrusive_ptr<TimerTask> t = tasks.top();
+ tasks.pop();
+ assert(!(t->nextFireTime < t->sortTime));
+
+ // warn on extreme lateness
+ AbsTime start(AbsTime::now());
+ Duration delay(t->sortTime, start);
+ {
+ ScopedLock<Mutex> l(t->callbackLock);
+ if (t->cancelled) {
+ {
+ Monitor::ScopedUnlock u(monitor);
+ drop(t);
+ }
+ if (delay > lateCancel) {
+ QPID_LOG(debug, t->name << " cancelled timer woken up " <<
+ delay / TIME_MSEC << "ms late");
+ }
+ continue;
+ } else if(Duration(t->nextFireTime, start) >= 0) {
+ {
+ Monitor::ScopedUnlock u(monitor);
+ fire(t);
+ }
+ // Warn if callback overran next timer's start.
+ AbsTime end(AbsTime::now());
+ Duration overrun (0);
+ if (!tasks.empty()) {
+ overrun = Duration(tasks.top()->nextFireTime, end);
+ }
+ bool warningsEnabled;
+ QPID_LOG_TEST(warning, warningsEnabled);
+ if (warningsEnabled) {
+ if (overrun > overran) {
+ if (delay > overran) // if delay is significant to an overrun.
+ warn.lateAndOverran(t->name, delay, overrun, Duration(start, end));
+ else
+ warn.overran(t->name, overrun, Duration(start, end));
+ }
+ else if (delay > late)
+ warn.late(t->name, delay);
+ }
+ continue;
+ } else {
+ // If the timer was adjusted into the future it might no longer
+ // be the next event, so push and then get top to make sure
+ // You can only push events into the future
+ t->sortTime = t->nextFireTime;
+ tasks.push(t);
+ }
+ }
+ assert(!tasks.empty());
+ monitor.wait(tasks.top()->sortTime);
+ }
+ }
+}
+
+void Timer::add(intrusive_ptr<TimerTask> task)
+{
+ Monitor::ScopedLock l(monitor);
+ task->sortTime = task->nextFireTime;
+ tasks.push(task);
+ monitor.notify();
+}
+
+void Timer::start()
+{
+ Monitor::ScopedLock l(monitor);
+ if (!active) {
+ active = true;
+ runner = Thread(this);
+ }
+}
+
+void Timer::stop()
+{
+ {
+ Monitor::ScopedLock l(monitor);
+ if (!active) return;
+ active = false;
+ monitor.notifyAll();
+ }
+ runner.join();
+}
+
+// Allow subclasses to override behavior when firing a task.
+void Timer::fire(boost::intrusive_ptr<TimerTask> t) {
+ try {
+ t->fireTask();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Exception thrown by timer task " << t->getName() << ": " << e.what());
+ }
+}
+
+// Provided for subclasses: called when a task is droped.
+void Timer::drop(boost::intrusive_ptr<TimerTask>) {}
+
+bool operator<(const intrusive_ptr<TimerTask>& a,
+ const intrusive_ptr<TimerTask>& b)
+{
+ // Lower priority if time is later
+ return a.get() && b.get() && a->sortTime > b->sortTime;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/Timer.h b/qpid/cpp/src/qpid/sys/Timer.h
new file mode 100644
index 0000000000..98ba39ce38
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Timer.h
@@ -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.
+ *
+ */
+#ifndef sys_Timer
+#define sys_Timer
+
+#include "qpid/sys/TimerWarnings.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/RefCounted.h"
+#include "qpid/CommonImportExport.h"
+#include <memory>
+#include <queue>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+namespace sys {
+
+class Timer;
+
+class TimerTask : public RefCounted {
+ friend class Timer;
+ friend bool operator<(const boost::intrusive_ptr<TimerTask>&,
+ const boost::intrusive_ptr<TimerTask>&);
+
+ std::string name;
+ AbsTime sortTime;
+ Duration period;
+ AbsTime nextFireTime;
+ Mutex callbackLock;
+ volatile bool cancelled;
+
+ bool readyToFire() const;
+ void fireTask();
+
+ public:
+ QPID_COMMON_EXTERN TimerTask(Duration period, const std::string& name);
+ QPID_COMMON_EXTERN TimerTask(AbsTime fireTime, const std::string& name);
+ QPID_COMMON_EXTERN virtual ~TimerTask();
+
+ QPID_COMMON_EXTERN void setupNextFire();
+ QPID_COMMON_EXTERN void restart();
+ QPID_COMMON_EXTERN void cancel();
+
+ std::string getName() const { return name; }
+
+ protected:
+ // Must be overridden with callback
+ virtual void fire() = 0;
+};
+
+// For the priority_queue order
+bool operator<(const boost::intrusive_ptr<TimerTask>& a,
+ const boost::intrusive_ptr<TimerTask>& b);
+
+class Timer : private Runnable {
+ qpid::sys::Monitor monitor;
+ std::priority_queue<boost::intrusive_ptr<TimerTask> > tasks;
+ qpid::sys::Thread runner;
+ bool active;
+
+ // Runnable interface
+ void run();
+
+ public:
+ QPID_COMMON_EXTERN Timer();
+ QPID_COMMON_EXTERN virtual ~Timer();
+
+ QPID_COMMON_EXTERN virtual void add(boost::intrusive_ptr<TimerTask> task);
+ QPID_COMMON_EXTERN virtual void start();
+ QPID_COMMON_EXTERN virtual void stop();
+
+ protected:
+ QPID_COMMON_EXTERN virtual void fire(boost::intrusive_ptr<TimerTask> task);
+ QPID_COMMON_EXTERN virtual void drop(boost::intrusive_ptr<TimerTask> task);
+ // Allow derived classes to change the late/overran thresholds.
+ Duration late;
+ Duration overran;
+ Duration lateCancel;
+ TimerWarnings warn;
+};
+
+
+}}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/TimerWarnings.cpp b/qpid/cpp/src/qpid/sys/TimerWarnings.cpp
new file mode 100644
index 0000000000..87c3169456
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/TimerWarnings.cpp
@@ -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.
+ *
+ */
+#include "TimerWarnings.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace sys {
+
+TimerWarnings::TimerWarnings(Duration reportInterval) :
+ interval(reportInterval),
+ nextReport(now(), reportInterval)
+{}
+
+void TimerWarnings::late(const std::string& task, Duration delay) {
+ taskStats[task].lateDelay.add(delay);
+ log();
+}
+
+void TimerWarnings::overran(const std::string& task, Duration overrun, Duration time)
+{
+ taskStats[task].overranOverrun.add(overrun);
+ taskStats[task].overranTime.add(time);
+ log();
+}
+
+void TimerWarnings::lateAndOverran(
+ const std::string& task, Duration delay, Duration overrun, Duration time)
+{
+ taskStats[task].lateAndOverranDelay.add(delay);
+ taskStats[task].lateAndOverranOverrun.add(overrun);
+ taskStats[task].lateAndOverranTime.add(time);
+ log();
+}
+
+void TimerWarnings::log() {
+ if (!taskStats.empty() && nextReport < now()) {
+ for (TaskStatsMap::iterator i = taskStats.begin(); i != taskStats.end(); ++i) {
+ std::string task = i->first;
+ TaskStats& stats = i->second;
+ if (stats.lateDelay.count)
+ QPID_LOG(warning, task << " task late "
+ << stats.lateDelay.count << " times by "
+ << stats.lateDelay.average()/TIME_MSEC << "ms on average.");
+
+ if (stats.overranOverrun.count)
+ QPID_LOG(warning, task << " task overran "
+ << stats.overranOverrun.count << " times by "
+ << stats.overranOverrun.average()/TIME_MSEC << "ms (taking "
+ << stats.overranTime.average() << "ns) on average.");
+
+ if (stats.lateAndOverranOverrun.count)
+ QPID_LOG(warning, task << " task late and overran "
+ << stats.lateAndOverranOverrun.count << " times: late "
+ << stats.lateAndOverranDelay.average()/TIME_MSEC << "ms, overran "
+ << stats.lateAndOverranOverrun.average()/TIME_MSEC << "ms (taking "
+ << stats.lateAndOverranTime.average() << "ns) on average.");
+
+ }
+ nextReport = AbsTime(now(), interval);
+ taskStats.clear();
+ }
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/TimerWarnings.h b/qpid/cpp/src/qpid/sys/TimerWarnings.h
new file mode 100644
index 0000000000..337a434ab5
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/TimerWarnings.h
@@ -0,0 +1,81 @@
+#ifndef QPID_SYS_TIMERWARNINGS_H
+#define QPID_SYS_TIMERWARNINGS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+#include <map>
+#include <string>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * The Timer class logs warnings when timer tasks are late and/or overrun.
+ *
+ * It is too expensive to log a warning for every late/overrun
+ * incident, doing so aggravates the problem of tasks over-running and
+ * being late.
+ *
+ * This class collects statistical data about each incident and prints
+ * an aggregated report at regular intervals.
+ */
+class TimerWarnings
+{
+ public:
+ TimerWarnings(Duration reportInterval);
+
+ void late(const std::string& task, Duration delay);
+
+ void overran(const std::string& task, Duration overrun, Duration time);
+
+ void lateAndOverran(const std::string& task,
+ Duration delay, Duration overrun, Duration time);
+
+ private:
+ struct Statistic {
+ Statistic() : total(0), count(0) {}
+ void add(int64_t value) { total += value; ++count; }
+ int64_t average() const { return count ? total/count : 0; }
+ int64_t total;
+ int64_t count;
+ };
+
+ // Keep statistics for 3 classes of incident: late, overrun and both.
+ struct TaskStats {
+ Statistic lateDelay; // Just late
+ Statistic overranOverrun, overranTime; // Just overrun
+ // Both
+ Statistic lateAndOverranDelay, lateAndOverranOverrun, lateAndOverranTime;
+ };
+
+ typedef std::map<std::string, TaskStats> TaskStatsMap;
+
+ void log();
+
+ Duration interval;
+ AbsTime nextReport;
+ TaskStatsMap taskStats;
+};
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_TIMERWARNINGS_H*/
diff --git a/qpid/cpp/src/qpid/sys/Waitable.h b/qpid/cpp/src/qpid/sys/Waitable.h
new file mode 100644
index 0000000000..8f6bd17049
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/Waitable.h
@@ -0,0 +1,117 @@
+#ifndef QPID_SYS_WAITABLE_H
+#define QPID_SYS_WAITABLE_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/ExceptionHolder.h"
+#include <assert.h>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A monitor that keeps track of waiting threads. Threads declare a
+ * ScopedWait around wait() inside a ScopedLock to be considered
+ * waiters.
+ *
+ * Allows waiting threads to be interrupted by an exception.
+ */
+class Waitable : public Monitor {
+ public:
+ Waitable() : waiters(0) {}
+
+ ~Waitable() { assert(waiters == 0); }
+
+ /** Use this inside a scoped lock around the
+ * call to wait() to be counted as a waiter.
+ */
+ struct ScopedWait {
+ Waitable& w;
+ ScopedWait(Waitable& w_) : w(w_) { ++w.waiters; }
+ ~ScopedWait() { if (--w.waiters==0) w.notifyAll(); }
+ };
+
+ /** Block till there are no more waiters in ScopedWaits.
+ * waitWaiters() does not raise an exception even if waiters
+ * were interrupted by one.
+ *@pre Must be called inside a ScopedLock but NOT a ScopedWait.
+ */
+ void waitWaiters() {
+ while (waiters != 0)
+ Monitor::wait();
+ }
+
+ /** Returns the number of outstanding ScopedWaits.
+ * Must be called with the lock held.
+ */
+ size_t hasWaiters() const {
+ return waiters;
+ }
+
+ /** Set an execption to interrupt waiters in ScopedWait.
+ * Must be called with the lock held.
+ */
+ void setException(const ExceptionHolder& e) {
+ exception = e;
+ notifyAll();
+
+ }
+
+ /** True if the waitable has an exception */
+ bool hasException() const { return exception; }
+
+ /** Throws if the waitable has an exception */
+ void checkException() const { exception.raise(); }
+
+ /** Clear the exception if any */
+ void resetException() { exception.reset(); }
+
+ /** Throws an exception if one is set before or during the wait. */
+ void wait() {
+ ExCheck e(exception);
+ Monitor::wait();
+ }
+
+ /** Throws an exception if one is set before or during the wait. */
+ bool wait(const AbsTime& absoluteTime) {
+ ExCheck e(exception);
+ return Monitor::wait(absoluteTime);
+ }
+
+ private:
+ struct ExCheck {
+ const ExceptionHolder& exception;
+ ExCheck(const ExceptionHolder& e) : exception(e) { e.raise(); }
+ ~ExCheck() { exception.raise(); }
+ };
+
+ size_t waiters;
+ ExceptionHolder exception;
+
+ friend struct ScopedWait;
+};
+
+}} // namespace qpid::sys
+
+
+
+#endif /*!QPID_SYS_WAITABLE_H*/
diff --git a/qpid/cpp/src/qpid/sys/alloca.h b/qpid/cpp/src/qpid/sys/alloca.h
new file mode 100644
index 0000000000..0f58920908
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/alloca.h
@@ -0,0 +1,42 @@
+#ifndef QPID_SYS_ALLOCA_H
+#define QPID_SYS_ALLOCA_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 (defined(_WINDOWS) || defined (WIN32))
+# include <malloc.h>
+
+# if defined(_MSC_VER)
+# ifdef alloc
+# undef alloc
+# endif
+# define alloc _alloc
+# ifdef alloca
+# undef alloca
+# endif
+# define alloca _alloca
+# endif
+# if !defined _WINDOWS && !defined WIN32
+# include <alloca.h>
+# endif
+#endif
+
+#endif /*!QPID_SYS_ALLOCA_H*/
diff --git a/qpid/cpp/src/qpid/sys/apr/APRBase.cpp b/qpid/cpp/src/qpid/sys/apr/APRBase.cpp
new file mode 100644
index 0000000000..8bdba66bdc
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/APRBase.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/log/Statement.h"
+#include "qpid/sys/apr/APRBase.h"
+
+using namespace qpid::sys;
+
+APRBase* APRBase::instance = 0;
+
+APRBase* APRBase::getInstance(){
+ if(instance == 0){
+ instance = new APRBase();
+ }
+ return instance;
+}
+
+
+APRBase::APRBase() : count(0){
+ apr_initialize();
+ CHECK_APR_SUCCESS(apr_pool_create(&pool, 0));
+ CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool));
+}
+
+APRBase::~APRBase(){
+ CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex));
+ apr_pool_destroy(pool);
+ apr_terminate();
+}
+
+bool APRBase::_increment(){
+ bool deleted(false);
+ CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex));
+ if(this == instance){
+ count++;
+ }else{
+ deleted = true;
+ }
+ CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex));
+ return !deleted;
+}
+
+void APRBase::_decrement(){
+ APRBase* copy = 0;
+ CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex));
+ if(--count == 0){
+ copy = instance;
+ instance = 0;
+ }
+ CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex));
+ if(copy != 0){
+ delete copy;
+ }
+}
+
+void APRBase::increment(){
+ int count = 0;
+ while(count++ < 2 && !getInstance()->_increment())
+ QPID_LOG(warning, "APR initialization triggered concurrently with termination.");
+}
+
+void APRBase::decrement(){
+ getInstance()->_decrement();
+}
+
+std::string qpid::sys::get_desc(apr_status_t status){
+ const int size = 50;
+ char tmp[size];
+ return std::string(apr_strerror(status, tmp, size));
+}
+
diff --git a/qpid/cpp/src/qpid/sys/apr/APRBase.h b/qpid/cpp/src/qpid/sys/apr/APRBase.h
new file mode 100644
index 0000000000..7b5644a129
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/APRBase.h
@@ -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.
+ *
+ */
+#ifndef _APRBase_
+#define _APRBase_
+
+#include <string>
+#include <apr_thread_mutex.h>
+#include <apr_errno.h>
+
+namespace qpid {
+namespace sys {
+
+ /**
+ * Use of APR libraries necessitates explicit init and terminate
+ * calls. Any class using APR libs should obtain the reference to
+ * this singleton and increment on construction, decrement on
+ * destruction. This class can then correctly initialise apr
+ * before the first use and terminate after the last use.
+ */
+ class APRBase{
+ static APRBase* instance;
+ apr_pool_t* pool;
+ apr_thread_mutex_t* mutex;
+ int count;
+
+ APRBase();
+ ~APRBase();
+ static APRBase* getInstance();
+ bool _increment();
+ void _decrement();
+ public:
+ static void increment();
+ static void decrement();
+ };
+
+ //this is also a convenient place for a helper function for error checking:
+ void check(apr_status_t status, const char* file, const int line);
+ std::string get_desc(apr_status_t status);
+
+#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__);
+
+}
+}
+
+// Inlined as it is called *a lot*
+void inline qpid::sys::check(apr_status_t status, const char* file, const int line){
+ if (status != APR_SUCCESS){
+ char tmp[256];
+ throw Exception(QPID_MSG(apr_strerror(status, tmp, size)))
+ }
+}
+
+
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/apr/APRPool.cpp b/qpid/cpp/src/qpid/sys/apr/APRPool.cpp
new file mode 100644
index 0000000000..e221bfc2f1
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/APRPool.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/apr/APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
+#include <boost/pool/detail/singleton.hpp>
+
+using namespace qpid::sys;
+
+APRPool::APRPool(){
+ APRBase::increment();
+ CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL));
+}
+
+APRPool::~APRPool(){
+ apr_pool_destroy(pool);
+ APRBase::decrement();
+}
+
+apr_pool_t* APRPool::get() {
+ return boost::details::pool::singleton_default<APRPool>::instance().pool;
+}
+
diff --git a/qpid/cpp/src/qpid/sys/apr/APRPool.h b/qpid/cpp/src/qpid/sys/apr/APRPool.h
new file mode 100644
index 0000000000..da7661fcfa
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/APRPool.h
@@ -0,0 +1,50 @@
+#ifndef _APRPool_
+#define _APRPool_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <boost/noncopyable.hpp>
+#include <apr_pools.h>
+
+namespace qpid {
+namespace sys {
+/**
+ * Singleton APR memory pool.
+ */
+class APRPool : private boost::noncopyable {
+ public:
+ APRPool();
+ ~APRPool();
+
+ /** Get singleton instance */
+ static apr_pool_t* get();
+
+ private:
+ apr_pool_t* pool;
+};
+
+}}
+
+
+
+
+
+#endif /*!_APRPool_*/
diff --git a/qpid/cpp/src/qpid/sys/apr/Condition.h b/qpid/cpp/src/qpid/sys/apr/Condition.h
new file mode 100644
index 0000000000..66d465ca75
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Condition.h
@@ -0,0 +1,84 @@
+#ifndef _sys_apr_Condition_h
+#define _sys_apr_Condition_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/apr/APRPool.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Time.h"
+
+#include <sys/errno.h>
+#include <boost/noncopyable.hpp>
+#include <apr_thread_cond.h>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A condition variable for thread synchronization.
+ */
+class Condition
+{
+ public:
+ inline Condition();
+ inline ~Condition();
+ inline void wait(Mutex&);
+ inline bool wait(Mutex&, const AbsTime& absoluteTime);
+ inline void notify();
+ inline void notifyAll();
+
+ private:
+ apr_thread_cond_t* condition;
+};
+
+
+Condition::Condition() {
+ CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get()));
+}
+
+Condition::~Condition() {
+ CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition));
+}
+
+void Condition::wait(Mutex& mutex) {
+ CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex));
+}
+
+bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){
+ // APR uses microseconds.
+ apr_status_t status =
+ apr_thread_cond_timedwait(
+ condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC);
+ if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status);
+ return status == 0;
+}
+
+void Condition::notify(){
+ CHECK_APR_SUCCESS(apr_thread_cond_signal(condition));
+}
+
+void Condition::notifyAll(){
+ CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition));
+}
+
+}}
+#endif /*!_sys_apr_Condition_h*/
diff --git a/qpid/cpp/src/qpid/sys/apr/Mutex.h b/qpid/cpp/src/qpid/sys/apr/Mutex.h
new file mode 100644
index 0000000000..cb75f5b339
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Mutex.h
@@ -0,0 +1,124 @@
+#ifndef _sys_apr_Mutex_h
+#define _sys_apr_Mutex_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
+
+#include <boost/noncopyable.hpp>
+#include <apr_thread_mutex.h>
+
+namespace qpid {
+namespace sys {
+
+class Condition;
+
+/**
+ * Mutex lock.
+ */
+class Mutex : private boost::noncopyable {
+ public:
+ typedef ScopedLock<Mutex> ScopedLock;
+ typedef ScopedUnlock<Mutex> ScopedUnlock;
+
+ inline Mutex();
+ inline ~Mutex();
+ inline void lock();
+ inline void unlock();
+ inline bool trylock();
+
+ protected:
+ apr_thread_mutex_t* mutex;
+ friend class Condition;
+};
+
+Mutex::Mutex() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get()));
+}
+
+Mutex::~Mutex(){
+ CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex));
+}
+
+void Mutex::lock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex));
+}
+void Mutex::unlock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex));
+}
+
+bool Mutex::trylock() {
+ return apr_thread_mutex_trylock(mutex) == 0;
+}
+
+
+/**
+ * RW lock.
+ */
+class RWlock : private boost::noncopyable {
+ friend class Condition;
+
+public:
+ typedef ScopedRlock<RWlock> ScopedRlock;
+ typedef ScopedWlock<RWlock> ScopedWlock;
+
+ inline RWlock();
+ inline ~RWlock();
+ inline void wlock(); // will write-lock
+ inline void rlock(); // will read-lock
+ inline void unlock();
+ inline bool trywlock(); // will write-try
+ inline bool tryrlock(); // will read-try
+
+ protected:
+ apr_thread_mutex_t* mutex;
+};
+
+RWlock::RWlock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get()));
+}
+
+RWlock::~RWlock(){
+ CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex));
+}
+
+void RWlock::wlock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex));
+}
+
+void RWlock::rlock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex));
+}
+
+void RWlock::unlock() {
+ CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex));
+}
+
+bool RWlock::trywlock() {
+ return apr_thread_mutex_trylock(mutex) == 0;
+}
+
+bool RWlock::tryrlock() {
+ return apr_thread_mutex_trylock(mutex) == 0;
+}
+
+
+}}
+#endif /*!_sys_apr_Mutex_h*/
diff --git a/qpid/cpp/src/qpid/sys/apr/Shlib.cpp b/qpid/cpp/src/qpid/sys/apr/Shlib.cpp
new file mode 100644
index 0000000000..b7ee13a03b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Shlib.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Shlib.h"
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
+#include <apr_dso.h>
+
+namespace qpid {
+namespace sys {
+
+void Shlib::load(const char* libname) {
+ apr_dso_handle_t* aprHandle;
+ CHECK_APR_SUCCESS(
+ apr_dso_load(&aprHandle, libname, APRPool::get()));
+ handle=aprHandle;
+}
+
+void Shlib::unload() {
+ CHECK_APR_SUCCESS(
+ apr_dso_unload(static_cast<apr_dso_handle_t*>(handle)));
+}
+
+void* Shlib::getSymbol(const char* name) {
+ apr_dso_handle_sym_t symbol;
+ CHECK_APR_SUCCESS(apr_dso_sym(&symbol,
+ static_cast<apr_dso_handle_t*>(handle),
+ name));
+ return (void*) symbol;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/apr/Socket.cpp b/qpid/cpp/src/qpid/sys/apr/Socket.cpp
new file mode 100644
index 0000000000..d9024d11c1
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Socket.cpp
@@ -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.
+ *
+ */
+
+
+#include "qpid/sys/Socket.h"
+
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
+
+#include <apr_network_io.h>
+
+namespace qpid {
+namespace sys {
+
+class SocketPrivate {
+public:
+ SocketPrivate(apr_socket_t* s = 0) :
+ socket(s)
+ {}
+
+ apr_socket_t* socket;
+};
+
+Socket::Socket() :
+ impl(new SocketPrivate)
+{
+ createTcp();
+}
+
+Socket::Socket(SocketPrivate* sp) :
+ impl(sp)
+{}
+
+Socket::~Socket() {
+ delete impl;
+}
+
+void Socket::createTcp() const {
+ apr_socket_t*& socket = impl->socket;
+ apr_socket_t* s;
+ CHECK_APR_SUCCESS(
+ apr_socket_create(
+ &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
+ APRPool::get()));
+ socket = s;
+}
+
+void Socket::setTimeout(const Duration& interval) const {
+ apr_socket_t*& socket = impl->socket;
+ apr_socket_timeout_set(socket, interval/TIME_USEC);
+}
+
+void Socket::connect(const std::string& host, int port) const {
+ apr_socket_t*& socket = impl->socket;
+ apr_sockaddr_t* address;
+ CHECK_APR_SUCCESS(
+ apr_sockaddr_info_get(
+ &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK,
+ APRPool::get()));
+ CHECK_APR_SUCCESS(apr_socket_connect(socket, address));
+}
+
+void Socket::close() const {
+ apr_socket_t*& socket = impl->socket;
+ if (socket == 0) return;
+ CHECK_APR_SUCCESS(apr_socket_close(socket));
+ socket = 0;
+}
+
+ssize_t Socket::send(const void* data, size_t size) const
+{
+ apr_socket_t*& socket = impl->socket;
+ apr_size_t sent = size;
+ apr_status_t status =
+ apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent);
+ if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT;
+ if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF;
+ CHECK_APR_SUCCESS(status);
+ return sent;
+}
+
+ssize_t Socket::recv(void* data, size_t size) const
+{
+ apr_socket_t*& socket = impl->socket;
+ apr_size_t received = size;
+ apr_status_t status =
+ apr_socket_recv(socket, reinterpret_cast<char*>(data), &received);
+ if (APR_STATUS_IS_TIMEUP(status))
+ return SOCKET_TIMEOUT;
+ if (APR_STATUS_IS_EOF(status))
+ return SOCKET_EOF;
+ CHECK_APR_SUCCESS(status);
+ return received;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/apr/Thread.cpp b/qpid/cpp/src/qpid/sys/apr/Thread.cpp
new file mode 100644
index 0000000000..b52d0e6ace
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Thread.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/apr/Thread.h"
+#include "qpid/sys/Runnable.h"
+
+using namespace qpid::sys;
+using qpid::sys::Runnable;
+
+void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) {
+ reinterpret_cast<Runnable*>(data)->run();
+ CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS));
+ return NULL;
+}
+
+
diff --git a/qpid/cpp/src/qpid/sys/apr/Thread.h b/qpid/cpp/src/qpid/sys/apr/Thread.h
new file mode 100644
index 0000000000..6cc63db5c9
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Thread.h
@@ -0,0 +1,106 @@
+#ifndef _sys_apr_Thread_h
+#define _sys_apr_Thread_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/apr/APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
+
+#include <apr_thread_proc.h>
+#include <apr_portable.h>
+
+namespace qpid {
+namespace sys {
+
+class Runnable;
+
+class Thread
+{
+ public:
+ inline static Thread current();
+
+ /** ID of current thread for logging.
+ * Workaround for broken Thread::current() in APR
+ */
+ inline static long logId();
+
+ inline static void yield();
+
+ inline Thread();
+ inline explicit Thread(qpid::sys::Runnable*);
+ inline explicit Thread(qpid::sys::Runnable&);
+
+ inline void join();
+
+ inline long id();
+
+ private:
+ static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data);
+ inline Thread(apr_thread_t* t);
+ apr_thread_t* thread;
+};
+
+Thread::Thread() : thread(0) {}
+
+Thread::Thread(Runnable* runnable) {
+ CHECK_APR_SUCCESS(
+ apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get()));
+}
+
+Thread::Thread(Runnable& runnable) {
+ CHECK_APR_SUCCESS(
+ apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get()));
+}
+
+void Thread::join(){
+ apr_status_t status;
+ if (thread != 0)
+ CHECK_APR_SUCCESS(apr_thread_join(&status, thread));
+}
+
+long Thread::id() {
+ return long(thread);
+}
+
+/** ID of current thread for logging.
+ * Workaround for broken Thread::current() in APR
+ */
+long Thread::logId() {
+ return static_cast<long>(apr_os_thread_current());
+}
+
+Thread::Thread(apr_thread_t* t) : thread(t) {}
+
+Thread Thread::current(){
+ apr_thread_t* thr;
+ apr_os_thread_t osthr = apr_os_thread_current();
+ CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get()));
+ return Thread(thr);
+}
+
+void Thread::yield()
+{
+ apr_thread_yield();
+}
+
+}}
+#endif /*!_sys_apr_Thread_h*/
diff --git a/qpid/cpp/src/qpid/sys/apr/Time.cpp b/qpid/cpp/src/qpid/sys/apr/Time.cpp
new file mode 100644
index 0000000000..34e740b144
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/apr/Time.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+
+#include <apr_time.h>
+
+namespace qpid {
+namespace sys {
+
+AbsTime AbsTime::now() {
+ AbsTime time_now;
+ time_now.time_ns = apr_time_now() * TIME_USEC;
+ return time_now;
+}
+
+}}
+
diff --git a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
new file mode 100644
index 0000000000..3d868da64b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
+#include <algorithm>
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include <string.h>
+
+namespace qpid {
+namespace sys {
+namespace cyrus {
+
+CyrusSecurityLayer::CyrusSecurityLayer(sasl_conn_t* c, uint16_t maxFrameSize) :
+ conn(c), decrypted(0), decryptedSize(0), encrypted(0), encryptedSize(0), codec(0), maxInputSize(0),
+ decodeBuffer(maxFrameSize), encodeBuffer(maxFrameSize), encoded(0)
+{
+ const void* value(0);
+ int result = sasl_getprop(conn, SASL_MAXOUTBUF, &value);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL encode error: " << sasl_errdetail(conn)));
+ }
+ maxInputSize = *(reinterpret_cast<const unsigned*>(value));
+}
+
+size_t CyrusSecurityLayer::decode(const char* input, size_t size)
+{
+ size_t inStart = 0;
+ do {
+ size_t inSize = std::min(size - inStart, maxInputSize);
+ int result = sasl_decode(conn, input + inStart, inSize, &decrypted, &decryptedSize);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL decode error: " << sasl_errdetail(conn)));
+ }
+ inStart += inSize;
+ size_t copied = 0;
+ do {
+ size_t count = std::min(decryptedSize - copied, decodeBuffer.size - decodeBuffer.position);
+ ::memcpy(decodeBuffer.data + decodeBuffer.position, decrypted + copied, count);
+ copied += count;
+ decodeBuffer.position += count;
+ size_t decodedSize = codec->decode(decodeBuffer.data, decodeBuffer.position);
+ if (decodedSize < decodeBuffer.position) {
+ ::memmove(decodeBuffer.data, decodeBuffer.data + decodedSize, decodeBuffer.position - decodedSize);
+ }
+ decodeBuffer.position -= decodedSize;
+ } while (copied < decryptedSize);
+ } while (inStart < size);
+ return size;
+}
+
+size_t CyrusSecurityLayer::encode(const char* buffer, size_t size)
+{
+ size_t processed = 0;//records how many bytes have been written to buffer
+ do {
+ if (!encrypted) {
+ if (!encoded) {
+ encodeBuffer.position = 0;
+ encoded = codec->encode(encodeBuffer.data, encodeBuffer.size);
+ if (!encoded) break;//nothing more to do
+ }
+
+ size_t encryptable = std::min(encoded, maxInputSize);
+ int result = sasl_encode(conn, encodeBuffer.data + encodeBuffer.position, encryptable, &encrypted, &encryptedSize);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL encode error: " << sasl_errdetail(conn)));
+ }
+ encodeBuffer.position += encryptable;
+ encoded -= encryptable;
+ }
+ size_t remaining = size - processed;
+ if (remaining < encryptedSize) {
+ //can't fit all encrypted data in the buffer we've
+ //been given, copy in what we can and hold on to the
+ //rest until the next call
+ ::memcpy(const_cast<char*>(buffer + processed), encrypted, remaining);
+ processed += remaining;
+ encrypted += remaining;
+ encryptedSize -= remaining;
+ } else {
+ ::memcpy(const_cast<char*>(buffer + processed), encrypted, encryptedSize);
+ processed += encryptedSize;
+ encrypted = 0;
+ encryptedSize = 0;
+ }
+ } while (processed < size);
+ return processed;
+}
+
+bool CyrusSecurityLayer::canEncode()
+{
+ return codec && (encrypted || codec->canEncode());
+}
+
+void CyrusSecurityLayer::init(qpid::sys::Codec* c)
+{
+ codec = c;
+}
+
+CyrusSecurityLayer::DataBuffer::DataBuffer(size_t s) : position(0), size(s)
+{
+ data = new char[size];
+}
+
+CyrusSecurityLayer::DataBuffer::~DataBuffer()
+{
+ delete[] data;
+}
+
+}}} // namespace qpid::sys::cyrus
diff --git a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
new file mode 100644
index 0000000000..1645cf1a58
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
@@ -0,0 +1,68 @@
+#ifndef QPID_SYS_CYRUS_CYRUSSECURITYLAYER_H
+#define QPID_SYS_CYRUS_CYRUSSECURITYLAYER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/SecurityLayer.h"
+#include <sasl/sasl.h>
+
+namespace qpid {
+namespace sys {
+namespace cyrus {
+
+
+/**
+ * Implementation of SASL security layer using cyrus-sasl library
+ */
+class CyrusSecurityLayer : public qpid::sys::SecurityLayer
+{
+ public:
+ CyrusSecurityLayer(sasl_conn_t*, uint16_t maxFrameSize);
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+ void init(qpid::sys::Codec*);
+ private:
+ struct DataBuffer
+ {
+ char* data;
+ size_t position;
+ const size_t size;
+ DataBuffer(size_t);
+ ~DataBuffer();
+ };
+
+ sasl_conn_t* conn;
+ const char* decrypted;
+ unsigned decryptedSize;
+ const char* encrypted;
+ unsigned encryptedSize;
+ qpid::sys::Codec* codec;
+ size_t maxInputSize;
+ DataBuffer decodeBuffer;
+ DataBuffer encodeBuffer;
+ size_t encoded;
+};
+}}} // namespace qpid::sys::cyrus
+
+#endif /*!QPID_SYS_CYRUS_CYRUSSECURITYLAYER_H*/
diff --git a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp
new file mode 100644
index 0000000000..9ad05c71a3
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp
@@ -0,0 +1,674 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/AtomicCount.h"
+#include "qpid/sys/DeletionManager.h"
+#include "qpid/sys/posix/check.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/log/Statement.h"
+
+#include <sys/epoll.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <assert.h>
+#include <queue>
+#include <set>
+#include <exception>
+
+namespace qpid {
+namespace sys {
+
+// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used
+DeletionManager<PollerHandlePrivate> PollerHandleDeletionManager;
+
+// Instantiate (and define) class static for DeletionManager
+template <>
+DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerHandlePrivate>::allThreadsStatuses(0);
+
+class PollerHandlePrivate {
+ friend class Poller;
+ friend class PollerPrivate;
+ friend class PollerHandle;
+
+ enum FDStat {
+ ABSENT,
+ MONITORED,
+ INACTIVE,
+ HUNGUP,
+ MONITORED_HUNGUP,
+ INTERRUPTED,
+ INTERRUPTED_HUNGUP,
+ DELETED
+ };
+
+ ::__uint32_t events;
+ const IOHandlePrivate* ioHandle;
+ PollerHandle* pollerHandle;
+ FDStat stat;
+ Mutex lock;
+
+ PollerHandlePrivate(const IOHandlePrivate* h, PollerHandle* p) :
+ events(0),
+ ioHandle(h),
+ pollerHandle(p),
+ stat(ABSENT) {
+ }
+
+ int fd() const {
+ return toFd(ioHandle);
+ }
+
+ bool isActive() const {
+ return stat == MONITORED || stat == MONITORED_HUNGUP;
+ }
+
+ void setActive() {
+ stat = (stat == HUNGUP || stat == INTERRUPTED_HUNGUP)
+ ? MONITORED_HUNGUP
+ : MONITORED;
+ }
+
+ bool isInactive() const {
+ return stat == INACTIVE || stat == HUNGUP;
+ }
+
+ void setInactive() {
+ stat = INACTIVE;
+ }
+
+ bool isIdle() const {
+ return stat == ABSENT;
+ }
+
+ void setIdle() {
+ stat = ABSENT;
+ }
+
+ bool isHungup() const {
+ return
+ stat == MONITORED_HUNGUP ||
+ stat == HUNGUP ||
+ stat == INTERRUPTED_HUNGUP;
+ }
+
+ void setHungup() {
+ assert(stat == MONITORED);
+ stat = HUNGUP;
+ }
+
+ bool isInterrupted() const {
+ return stat == INTERRUPTED || stat == INTERRUPTED_HUNGUP;
+ }
+
+ void setInterrupted() {
+ stat = (stat == MONITORED_HUNGUP || stat == HUNGUP)
+ ? INTERRUPTED_HUNGUP
+ : INTERRUPTED;
+ }
+
+ bool isDeleted() const {
+ return stat == DELETED;
+ }
+
+ void setDeleted() {
+ stat = DELETED;
+ }
+};
+
+PollerHandle::PollerHandle(const IOHandle& h) :
+ impl(new PollerHandlePrivate(h.impl, this))
+{}
+
+PollerHandle::~PollerHandle() {
+ {
+ ScopedLock<Mutex> l(impl->lock);
+ if (impl->isDeleted()) {
+ return;
+ }
+ impl->pollerHandle = 0;
+ if (impl->isInterrupted()) {
+ impl->setDeleted();
+ return;
+ }
+ assert(impl->isIdle());
+ impl->setDeleted();
+ }
+ PollerHandleDeletionManager.markForDeletion(impl);
+}
+
+class HandleSet
+{
+ Mutex lock;
+ std::set<PollerHandle*> handles;
+ public:
+ void add(PollerHandle*);
+ void remove(PollerHandle*);
+ void cleanup();
+};
+
+void HandleSet::add(PollerHandle* h)
+{
+ ScopedLock<Mutex> l(lock);
+ handles.insert(h);
+}
+void HandleSet::remove(PollerHandle* h)
+{
+ ScopedLock<Mutex> l(lock);
+ handles.erase(h);
+}
+void HandleSet::cleanup()
+{
+ // Inform all registered handles of disconnection
+ std::set<PollerHandle*> copy;
+ handles.swap(copy);
+ for (std::set<PollerHandle*>::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+ Poller::Event event(*i, Poller::DISCONNECTED);
+ event.process();
+ }
+}
+
+/**
+ * Concrete implementation of Poller to use the Linux specific epoll
+ * interface
+ */
+class PollerPrivate {
+ friend class Poller;
+
+ static const int DefaultFds = 256;
+
+ struct ReadablePipe {
+ int fds[2];
+
+ /**
+ * This encapsulates an always readable pipe which we can add
+ * to the epoll set to force epoll_wait to return
+ */
+ ReadablePipe() {
+ QPID_POSIX_CHECK(::pipe(fds));
+ // Just write the pipe's fds to the pipe
+ QPID_POSIX_CHECK(::write(fds[1], fds, 2));
+ }
+
+ ~ReadablePipe() {
+ ::close(fds[0]);
+ ::close(fds[1]);
+ }
+
+ int getFD() {
+ return fds[0];
+ }
+ };
+
+ static ReadablePipe alwaysReadable;
+ static int alwaysReadableFd;
+
+ class InterruptHandle: public PollerHandle {
+ std::queue<PollerHandle*> handles;
+
+ void processEvent(Poller::EventType) {
+ PollerHandle* handle = handles.front();
+ handles.pop();
+ assert(handle);
+
+ // Synthesise event
+ Poller::Event event(handle, Poller::INTERRUPTED);
+
+ // Process synthesised event
+ event.process();
+ }
+
+ public:
+ InterruptHandle() :
+ PollerHandle(DummyIOHandle)
+ {}
+
+ void addHandle(PollerHandle& h) {
+ handles.push(&h);
+ }
+
+ PollerHandle* getHandle() {
+ PollerHandle* handle = handles.front();
+ handles.pop();
+ return handle;
+ }
+
+ bool queuedHandles() {
+ return handles.size() > 0;
+ }
+ };
+
+ const int epollFd;
+ bool isShutdown;
+ InterruptHandle interruptHandle;
+ HandleSet registeredHandles;
+ AtomicCount threadCount;
+
+ static ::__uint32_t directionToEpollEvent(Poller::Direction dir) {
+ switch (dir) {
+ case Poller::INPUT: return ::EPOLLIN;
+ case Poller::OUTPUT: return ::EPOLLOUT;
+ case Poller::INOUT: return ::EPOLLIN | ::EPOLLOUT;
+ default: return 0;
+ }
+ }
+
+ static Poller::EventType epollToDirection(::__uint32_t events) {
+ // POLLOUT & POLLHUP are mutually exclusive really, but at least socketpairs
+ // can give you both!
+ events = (events & ::EPOLLHUP) ? events & ~::EPOLLOUT : events;
+ ::__uint32_t e = events & (::EPOLLIN | ::EPOLLOUT);
+ switch (e) {
+ case ::EPOLLIN: return Poller::READABLE;
+ case ::EPOLLOUT: return Poller::WRITABLE;
+ case ::EPOLLIN | ::EPOLLOUT: return Poller::READ_WRITABLE;
+ default:
+ return (events & (::EPOLLHUP | ::EPOLLERR)) ?
+ Poller::DISCONNECTED : Poller::INVALID;
+ }
+ }
+
+ PollerPrivate() :
+ epollFd(::epoll_create(DefaultFds)),
+ isShutdown(false) {
+ QPID_POSIX_CHECK(epollFd);
+ // Add always readable fd into our set (but not listening to it yet)
+ ::epoll_event epe;
+ epe.events = 0;
+ epe.data.u64 = 1;
+ QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_ADD, alwaysReadableFd, &epe));
+ }
+
+ ~PollerPrivate() {
+ // It's probably okay to ignore any errors here as there can't be data loss
+ ::close(epollFd);
+
+ // Need to put the interruptHandle in idle state to delete it
+ static_cast<PollerHandle&>(interruptHandle).impl->setIdle();
+ }
+
+ void resetMode(PollerHandlePrivate& handle);
+
+ void interrupt() {
+ ::epoll_event epe;
+ // Use EPOLLONESHOT so we only wake a single thread
+ epe.events = ::EPOLLIN | ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &static_cast<PollerHandle&>(interruptHandle);
+ QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, alwaysReadableFd, &epe));
+ }
+
+ void interruptAll() {
+ ::epoll_event epe;
+ // Not EPOLLONESHOT, so we eventually get all threads
+ epe.events = ::EPOLLIN;
+ epe.data.u64 = 2; // Keep valgrind happy
+ QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, alwaysReadableFd, &epe));
+ }
+};
+
+PollerPrivate::ReadablePipe PollerPrivate::alwaysReadable;
+int PollerPrivate::alwaysReadableFd = alwaysReadable.getFD();
+
+void Poller::registerHandle(PollerHandle& handle) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(eh.isIdle());
+
+ ::epoll_event epe;
+ epe.events = ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &eh;
+
+ impl->registeredHandles.add(&handle);
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, eh.fd(), &epe));
+
+ eh.setActive();
+}
+
+void Poller::unregisterHandle(PollerHandle& handle) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isIdle());
+
+ impl->registeredHandles.remove(&handle);
+ int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd(), 0);
+ // Ignore EBADF since deleting a nonexistent fd has the overall required result!
+ // And allows the case where a sloppy program closes the fd and then does the delFd()
+ if (rc == -1 && errno != EBADF) {
+ QPID_POSIX_CHECK(rc);
+ }
+
+ eh.setIdle();
+}
+
+void PollerPrivate::resetMode(PollerHandlePrivate& eh) {
+ PollerHandle* ph;
+ {
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isActive());
+
+ if (eh.isIdle() || eh.isDeleted()) {
+ return;
+ }
+
+ if (eh.events==0) {
+ eh.setActive();
+ return;
+ }
+
+ if (!eh.isInterrupted()) {
+ ::epoll_event epe;
+ epe.events = eh.events | ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &eh;
+
+ QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
+
+ eh.setActive();
+ return;
+ }
+ ph = eh.pollerHandle;
+ }
+
+ PollerHandlePrivate& ihp = *static_cast<PollerHandle&>(interruptHandle).impl;
+ ScopedLock<Mutex> l(ihp.lock);
+ interruptHandle.addHandle(*ph);
+ ihp.setActive();
+ interrupt();
+}
+
+void Poller::monitorHandle(PollerHandle& handle, Direction dir) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isIdle());
+
+ ::__uint32_t oldEvents = eh.events;
+ eh.events |= PollerPrivate::directionToEpollEvent(dir);
+
+ // If no change nothing more to do - avoid unnecessary system call
+ if (oldEvents==eh.events) {
+ return;
+ }
+
+ // If we're not actually listening wait till we are to perform change
+ if (!eh.isActive()) {
+ return;
+ }
+
+ ::epoll_event epe;
+ epe.events = eh.events | ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &eh;
+
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
+}
+
+void Poller::unmonitorHandle(PollerHandle& handle, Direction dir) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isIdle());
+
+ ::__uint32_t oldEvents = eh.events;
+ eh.events &= ~PollerPrivate::directionToEpollEvent(dir);
+
+ // If no change nothing more to do - avoid unnecessary system call
+ if (oldEvents==eh.events) {
+ return;
+ }
+
+ // If we're not actually listening wait till we are to perform change
+ if (!eh.isActive()) {
+ return;
+ }
+
+ ::epoll_event epe;
+ epe.events = eh.events | ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &eh;
+
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
+}
+
+void Poller::shutdown() {
+ // NB: this function must be async-signal safe, it must not
+ // call any function that is not async-signal safe.
+
+ // Allow sloppy code to shut us down more than once
+ if (impl->isShutdown)
+ return;
+
+ // Don't use any locking here - isShutdown will be visible to all
+ // after the epoll_ctl() anyway (it's a memory barrier)
+ impl->isShutdown = true;
+
+ impl->interruptAll();
+}
+
+bool Poller::interrupt(PollerHandle& handle) {
+ {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ if (eh.isIdle() || eh.isDeleted()) {
+ return false;
+ }
+
+ if (eh.isInterrupted()) {
+ return true;
+ }
+
+ // Stop monitoring handle for read or write
+ ::epoll_event epe;
+ epe.events = 0;
+ epe.data.u64 = 0; // Keep valgrind happy
+ epe.data.ptr = &eh;
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
+
+ if (eh.isInactive()) {
+ eh.setInterrupted();
+ return true;
+ }
+ eh.setInterrupted();
+ }
+
+ PollerPrivate::InterruptHandle& ih = impl->interruptHandle;
+ PollerHandlePrivate& eh = *static_cast<PollerHandle&>(ih).impl;
+ ScopedLock<Mutex> l(eh.lock);
+ ih.addHandle(handle);
+
+ impl->interrupt();
+ eh.setActive();
+ return true;
+}
+
+void Poller::run() {
+ // Ensure that we exit thread responsibly under all circumstances
+ try {
+ // Make sure we can't be interrupted by signals at a bad time
+ ::sigset_t ss;
+ ::sigfillset(&ss);
+ ::pthread_sigmask(SIG_SETMASK, &ss, 0);
+
+ ++(impl->threadCount);
+ do {
+ Event event = wait();
+
+ // If can read/write then dispatch appropriate callbacks
+ if (event.handle) {
+ event.process();
+ } else {
+ // Handle shutdown
+ switch (event.type) {
+ case SHUTDOWN:
+ PollerHandleDeletionManager.destroyThreadState();
+ //last thread to respond to shutdown cleans up:
+ if (--(impl->threadCount) == 0) impl->registeredHandles.cleanup();
+ return;
+ default:
+ // This should be impossible
+ assert(false);
+ }
+ }
+ } while (true);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "IO worker thread exiting with unhandled exception: " << e.what());
+ }
+ PollerHandleDeletionManager.destroyThreadState();
+ --(impl->threadCount);
+}
+
+bool Poller::hasShutdown()
+{
+ return impl->isShutdown;
+}
+
+Poller::Event Poller::wait(Duration timeout) {
+ static __thread PollerHandlePrivate* lastReturnedHandle = 0;
+ epoll_event epe;
+ int timeoutMs = (timeout == TIME_INFINITE) ? -1 : timeout / TIME_MSEC;
+ AbsTime targetTimeout =
+ (timeout == TIME_INFINITE) ?
+ FAR_FUTURE :
+ AbsTime(now(), timeout);
+
+ if (lastReturnedHandle) {
+ impl->resetMode(*lastReturnedHandle);
+ lastReturnedHandle = 0;
+ }
+
+ // Repeat until we weren't interrupted by signal
+ do {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ int rc = ::epoll_wait(impl->epollFd, &epe, 1, timeoutMs);
+ if (rc ==-1 && errno != EINTR) {
+ QPID_POSIX_CHECK(rc);
+ } else if (rc > 0) {
+ assert(rc == 1);
+ void* dataPtr = epe.data.ptr;
+
+ // Check if this is an interrupt
+ PollerPrivate::InterruptHandle& interruptHandle = impl->interruptHandle;
+ if (dataPtr == &interruptHandle) {
+ // If we are shutting down we need to rearm the shutdown interrupt to
+ // ensure everyone still sees it. It's okay that this might be overridden
+ // below as we will be back here if it is.
+ if (impl->isShutdown) {
+ impl->interruptAll();
+ }
+ PollerHandle* wrappedHandle = 0;
+ {
+ ScopedLock<Mutex> l(interruptHandle.impl->lock);
+ if (interruptHandle.impl->isActive()) {
+ wrappedHandle = interruptHandle.getHandle();
+ // If there is an interrupt queued behind this one we need to arm it
+ // We do it this way so that another thread can pick it up
+ if (interruptHandle.queuedHandles()) {
+ impl->interrupt();
+ interruptHandle.impl->setActive();
+ } else {
+ interruptHandle.impl->setInactive();
+ }
+ }
+ }
+ if (wrappedHandle) {
+ PollerHandlePrivate& eh = *wrappedHandle->impl;
+ {
+ ScopedLock<Mutex> l(eh.lock);
+ if (!eh.isDeleted()) {
+ if (!eh.isIdle()) {
+ eh.setInactive();
+ }
+ lastReturnedHandle = &eh;
+ assert(eh.pollerHandle == wrappedHandle);
+ return Event(wrappedHandle, INTERRUPTED);
+ }
+ }
+ PollerHandleDeletionManager.markForDeletion(&eh);
+ }
+ continue;
+ }
+
+ // Check for shutdown
+ if (impl->isShutdown) {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ return Event(0, SHUTDOWN);
+ }
+
+ PollerHandlePrivate& eh = *static_cast<PollerHandlePrivate*>(dataPtr);
+ ScopedLock<Mutex> l(eh.lock);
+
+ // the handle could have gone inactive since we left the epoll_wait
+ if (eh.isActive()) {
+ PollerHandle* handle = eh.pollerHandle;
+ assert(handle);
+
+ // If the connection has been hungup we could still be readable
+ // (just not writable), allow us to readable until we get here again
+ if (epe.events & ::EPOLLHUP) {
+ if (eh.isHungup()) {
+ eh.setInactive();
+ // Don't set up last Handle so that we don't reset this handle
+ // on re-entering Poller::wait. This means that we will never
+ // be set active again once we've returned disconnected, and so
+ // can never be returned again.
+ return Event(handle, DISCONNECTED);
+ }
+ eh.setHungup();
+ } else {
+ eh.setInactive();
+ }
+ lastReturnedHandle = &eh;
+ return Event(handle, PollerPrivate::epollToDirection(epe.events));
+ }
+ }
+ // We only get here if one of the following:
+ // * epoll_wait was interrupted by a signal
+ // * epoll_wait timed out
+ // * the state of the handle changed after being returned by epoll_wait
+ //
+ // The only things we can do here are return a timeout or wait more.
+ // Obviously if we timed out we return timeout; if the wait was meant to
+ // be indefinite then we should never return with a time out so we go again.
+ // If the wait wasn't indefinite, we check whether we are after the target wait
+ // time or not
+ if (timeoutMs == -1) {
+ continue;
+ }
+ if (rc == 0 && now() > targetTimeout) {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ return Event(0, TIMEOUT);
+ }
+ } while (true);
+}
+
+// Concrete constructors
+Poller::Poller() :
+ impl(new PollerPrivate())
+{}
+
+Poller::~Poller() {
+ delete impl;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
new file mode 100644
index 0000000000..b5a0b0bf32
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
@@ -0,0 +1,611 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/Time.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/sys/posix/check.h"
+
+// TODO The basic algorithm here is not really POSIX specific and with a
+// bit more abstraction could (should) be promoted to be platform portable
+#include <unistd.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+
+using namespace qpid::sys;
+
+namespace {
+
+struct StaticInit {
+ StaticInit() {
+ /**
+ * Make *process* not generate SIGPIPE when writing to closed
+ * pipe/socket (necessary as default action is to terminate process)
+ */
+ ::signal(SIGPIPE, SIG_IGN);
+ };
+} init;
+
+/*
+ * We keep per thread state to avoid locking overhead. The assumption is that
+ * on average all the connections are serviced by all the threads so the state
+ * recorded in each thread is about the same. If this turns out not to be the
+ * case we could rebalance the info occasionally.
+ */
+__thread int threadReadTotal = 0;
+__thread int threadMaxRead = 0;
+__thread int threadReadCount = 0;
+__thread int threadWriteTotal = 0;
+__thread int threadWriteCount = 0;
+__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms
+}
+
+/*
+ * Asynch Acceptor
+ */
+namespace qpid {
+namespace sys {
+namespace posix {
+
+class AsynchAcceptor : public qpid::sys::AsynchAcceptor {
+public:
+ AsynchAcceptor(const Socket& s, AsynchAcceptor::Callback callback);
+ ~AsynchAcceptor();
+ void start(Poller::shared_ptr poller);
+
+private:
+ void readable(DispatchHandle& handle);
+
+private:
+ AsynchAcceptor::Callback acceptedCallback;
+ DispatchHandle handle;
+ const Socket& socket;
+
+};
+
+AsynchAcceptor::AsynchAcceptor(const Socket& s,
+ AsynchAcceptor::Callback callback) :
+ acceptedCallback(callback),
+ handle(s, boost::bind(&AsynchAcceptor::readable, this, _1), 0, 0),
+ socket(s) {
+
+ s.setNonblocking();
+}
+
+AsynchAcceptor::~AsynchAcceptor() {
+ handle.stopWatch();
+}
+
+void AsynchAcceptor::start(Poller::shared_ptr poller) {
+ handle.startWatch(poller);
+}
+
+/*
+ * We keep on accepting as long as there is something to accept
+ */
+void AsynchAcceptor::readable(DispatchHandle& h) {
+ Socket* s;
+ do {
+ errno = 0;
+ // TODO: Currently we ignore the peers address, perhaps we should
+ // log it or use it for connection acceptance.
+ try {
+ s = socket.accept();
+ if (s) {
+ acceptedCallback(*s);
+ } else {
+ break;
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Could not accept socket: " << e.what());
+ break;
+ }
+ } while (true);
+
+ h.rewatch();
+}
+
+/*
+ * POSIX version of AsynchIO TCP socket connector.
+ *
+ * The class is implemented in terms of DispatchHandle to allow it to be
+ * deleted by deleting the contained DispatchHandle.
+ */
+class AsynchConnector : public qpid::sys::AsynchConnector,
+ private DispatchHandle {
+
+private:
+ void connComplete(DispatchHandle& handle);
+
+private:
+ ConnectedCallback connCallback;
+ FailedCallback failCallback;
+ const Socket& socket;
+
+public:
+ AsynchConnector(const Socket& socket,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb);
+ void start(Poller::shared_ptr poller);
+ void stop();
+};
+
+AsynchConnector::AsynchConnector(const Socket& s,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb) :
+ DispatchHandle(s,
+ 0,
+ boost::bind(&AsynchConnector::connComplete, this, _1),
+ boost::bind(&AsynchConnector::connComplete, this, _1)),
+ connCallback(connCb),
+ failCallback(failCb),
+ socket(s)
+{
+ socket.setNonblocking();
+ SocketAddress sa(hostname, port);
+ // Note, not catching any exceptions here, also has effect of destructing
+ socket.connect(sa);
+}
+
+void AsynchConnector::start(Poller::shared_ptr poller)
+{
+ startWatch(poller);
+}
+
+void AsynchConnector::stop()
+{
+ stopWatch();
+}
+
+void AsynchConnector::connComplete(DispatchHandle& h)
+{
+ h.stopWatch();
+ int errCode = socket.getError();
+ if (errCode == 0) {
+ connCallback(socket);
+ } else {
+ failCallback(socket, errCode, strError(errCode));
+ }
+ DispatchHandle::doDelete();
+}
+
+/*
+ * POSIX version of AsynchIO reader/writer
+ *
+ * The class is implemented in terms of DispatchHandle to allow it to be
+ * deleted by deleting the contained DispatchHandle.
+ */
+class AsynchIO : public qpid::sys::AsynchIO, private DispatchHandle {
+
+public:
+ AsynchIO(const Socket& s,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0);
+
+ // Methods inherited from qpid::sys::AsynchIO
+
+ virtual void queueForDeletion();
+
+ virtual void start(Poller::shared_ptr poller);
+ virtual void queueReadBuffer(BufferBase* buff);
+ virtual void unread(BufferBase* buff);
+ virtual void queueWrite(BufferBase* buff);
+ virtual void notifyPendingWrite();
+ virtual void queueWriteClose();
+ virtual bool writeQueueEmpty();
+ virtual void startReading();
+ virtual void stopReading();
+ virtual void requestCallback(RequestCallback);
+ virtual BufferBase* getQueuedBuffer();
+
+private:
+ ~AsynchIO();
+
+ // Methods that are callback targets from Dispatcher.
+ void readable(DispatchHandle& handle);
+ void writeable(DispatchHandle& handle);
+ void disconnected(DispatchHandle& handle);
+ void requestedCall(RequestCallback);
+ void close(DispatchHandle& handle);
+
+private:
+ ReadCallback readCallback;
+ EofCallback eofCallback;
+ DisconnectCallback disCallback;
+ ClosedCallback closedCallback;
+ BuffersEmptyCallback emptyCallback;
+ IdleCallback idleCallback;
+ const Socket& socket;
+ std::deque<BufferBase*> bufferQueue;
+ std::deque<BufferBase*> writeQueue;
+ bool queuedClose;
+ /**
+ * This flag is used to detect and handle concurrency between
+ * calls to notifyPendingWrite() (which can be made from any thread) and
+ * the execution of the writeable() method (which is always on the
+ * thread processing this handle.
+ */
+ volatile bool writePending;
+ /**
+ * This records whether we've been reading is flow controlled:
+ * it's safe as a simple boolean as the only way to be stopped
+ * is in calls only allowed in the callback context, the only calls
+ * checking it are also in calls only allowed in callback context.
+ */
+ volatile bool readingStopped;
+};
+
+AsynchIO::AsynchIO(const Socket& s,
+ ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
+ ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) :
+
+ DispatchHandle(s,
+ boost::bind(&AsynchIO::readable, this, _1),
+ boost::bind(&AsynchIO::writeable, this, _1),
+ boost::bind(&AsynchIO::disconnected, this, _1)),
+ readCallback(rCb),
+ eofCallback(eofCb),
+ disCallback(disCb),
+ closedCallback(cCb),
+ emptyCallback(eCb),
+ idleCallback(iCb),
+ socket(s),
+ queuedClose(false),
+ writePending(false),
+ readingStopped(false) {
+
+ s.setNonblocking();
+}
+
+struct deleter
+{
+ template <typename T>
+ void operator()(T *ptr){ delete ptr;}
+};
+
+AsynchIO::~AsynchIO() {
+ std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter());
+ std::for_each( writeQueue.begin(), writeQueue.end(), deleter());
+}
+
+void AsynchIO::queueForDeletion() {
+ DispatchHandle::doDelete();
+}
+
+void AsynchIO::start(Poller::shared_ptr poller) {
+ DispatchHandle::startWatch(poller);
+}
+
+void AsynchIO::queueReadBuffer(BufferBase* buff) {
+ assert(buff);
+ buff->dataStart = 0;
+ buff->dataCount = 0;
+
+ bool queueWasEmpty = bufferQueue.empty();
+ bufferQueue.push_back(buff);
+ if (queueWasEmpty && !readingStopped)
+ DispatchHandle::rewatchRead();
+}
+
+void AsynchIO::unread(BufferBase* buff) {
+ assert(buff);
+ buff->squish();
+
+ bool queueWasEmpty = bufferQueue.empty();
+ bufferQueue.push_front(buff);
+ if (queueWasEmpty && !readingStopped)
+ DispatchHandle::rewatchRead();
+}
+
+void AsynchIO::queueWrite(BufferBase* buff) {
+ assert(buff);
+ // If we've already closed the socket then throw the write away
+ if (queuedClose) {
+ queueReadBuffer(buff);
+ return;
+ } else {
+ writeQueue.push_front(buff);
+ }
+ writePending = false;
+ DispatchHandle::rewatchWrite();
+}
+
+// This can happen outside the callback context
+void AsynchIO::notifyPendingWrite() {
+ writePending = true;
+ DispatchHandle::rewatchWrite();
+}
+
+void AsynchIO::queueWriteClose() {
+ queuedClose = true;
+ DispatchHandle::rewatchWrite();
+}
+
+bool AsynchIO::writeQueueEmpty() {
+ return writeQueue.empty();
+}
+
+// This can happen outside the callback context
+void AsynchIO::startReading() {
+ readingStopped = false;
+ DispatchHandle::rewatchRead();
+}
+
+void AsynchIO::stopReading() {
+ readingStopped = true;
+ DispatchHandle::unwatchRead();
+}
+
+void AsynchIO::requestCallback(RequestCallback callback) {
+ // TODO creating a function object every time isn't all that
+ // efficient - if this becomes heavily used do something better (what?)
+ assert(callback);
+ DispatchHandle::call(boost::bind(&AsynchIO::requestedCall, this, callback));
+}
+
+void AsynchIO::requestedCall(RequestCallback callback) {
+ assert(callback);
+ callback(*this);
+}
+
+/** Return a queued buffer if there are enough
+ * to spare
+ */
+AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() {
+ // Always keep at least one buffer (it might have data that was "unread" in it)
+ if (bufferQueue.size()<=1)
+ return 0;
+ BufferBase* buff = bufferQueue.back();
+ assert(buff);
+ buff->dataStart = 0;
+ buff->dataCount = 0;
+ bufferQueue.pop_back();
+ return buff;
+}
+
+/*
+ * We keep on reading as long as we have something to read, a buffer
+ * to put it in and reading is not stopped by flow control.
+ */
+void AsynchIO::readable(DispatchHandle& h) {
+ if (readingStopped) {
+ // We have been flow controlled.
+ return;
+ }
+ int readTotal = 0;
+ AbsTime readStartTime = AbsTime::now();
+ do {
+ // (Try to) get a buffer
+ if (!bufferQueue.empty()) {
+ // Read into buffer
+ BufferBase* buff = bufferQueue.front();
+ assert(buff);
+ bufferQueue.pop_front();
+ errno = 0;
+ int readCount = buff->byteCount-buff->dataCount;
+ int rc = socket.read(buff->bytes + buff->dataCount, readCount);
+ if (rc > 0) {
+ buff->dataCount += rc;
+ threadReadTotal += rc;
+ readTotal += rc;
+
+ readCallback(*this, buff);
+ if (readingStopped) {
+ // We have been flow controlled.
+ break;
+ }
+
+ if (rc != readCount) {
+ // If we didn't fill the read buffer then time to stop reading
+ break;
+ }
+
+ // Stop reading if we've overrun our timeslot
+ if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) {
+ break;
+ }
+
+ } else {
+ // Put buffer back (at front so it doesn't interfere with unread buffers)
+ bufferQueue.push_front(buff);
+ assert(buff);
+
+ // Eof or other side has gone away
+ if (rc == 0 || errno == ECONNRESET) {
+ eofCallback(*this);
+ h.unwatchRead();
+ break;
+ } else if (errno == EAGAIN) {
+ // We have just put a buffer back so we know
+ // we can carry on watching for reads
+ break;
+ } else {
+ // Report error then just treat as a socket disconnect
+ QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(errno) << "(" << errno << ")" );
+ eofCallback(*this);
+ h.unwatchRead();
+ break;
+ }
+ }
+ } else {
+ // Something to read but no buffer
+ if (emptyCallback) {
+ emptyCallback(*this);
+ }
+ // If we still have no buffers we can't do anything more
+ if (bufferQueue.empty()) {
+ h.unwatchRead();
+ break;
+ }
+
+ }
+ } while (true);
+
+ ++threadReadCount;
+ threadMaxRead = std::max(threadMaxRead, readTotal);
+ return;
+}
+
+/*
+ * We carry on writing whilst we have data to write and we can write
+ */
+void AsynchIO::writeable(DispatchHandle& h) {
+ int writeTotal = 0;
+ do {
+ // See if we've got something to write
+ if (!writeQueue.empty()) {
+ // Write buffer
+ BufferBase* buff = writeQueue.back();
+ writeQueue.pop_back();
+ errno = 0;
+ assert(buff->dataStart+buff->dataCount <= buff->byteCount);
+ int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount);
+ if (rc >= 0) {
+ threadWriteTotal += rc;
+ writeTotal += rc;
+
+ // If we didn't write full buffer put rest back
+ if (rc != buff->dataCount) {
+ buff->dataStart += rc;
+ buff->dataCount -= rc;
+ writeQueue.push_back(buff);
+ break;
+ }
+
+ // Recycle the buffer
+ queueReadBuffer(buff);
+
+ // If we've already written more than the max for reading then stop
+ // (this is to stop writes dominating reads)
+ if (writeTotal > threadMaxRead)
+ break;
+ } else {
+ // Put buffer back
+ writeQueue.push_back(buff);
+ if (errno == ECONNRESET || errno == EPIPE) {
+ // Just stop watching for write here - we'll get a
+ // disconnect callback soon enough
+ h.unwatchWrite();
+ break;
+ } else if (errno == EAGAIN) {
+ // We have just put a buffer back so we know
+ // we can carry on watching for writes
+ break;
+ } else {
+ // Report error then just treat as a socket disconnect
+ QPID_LOG(error, "Error writing socket: " << qpid::sys::strError(errno) << "(" << errno << ")" );
+ h.unwatchWrite();
+ break;
+ }
+ }
+ } else {
+ // If we're waiting to close the socket then can do it now as there is nothing to write
+ if (queuedClose) {
+ close(h);
+ break;
+ }
+ // Fd is writable, but nothing to write
+ if (idleCallback) {
+ writePending = false;
+ idleCallback(*this);
+ }
+ // If we still have no buffers to write we can't do anything more
+ if (writeQueue.empty() && !writePending && !queuedClose) {
+ h.unwatchWrite();
+ // The following handles the case where writePending is
+ // set to true after the test above; in this case its
+ // possible that the unwatchWrite overwrites the
+ // desired rewatchWrite so we correct that here
+ if (writePending)
+ h.rewatchWrite();
+ break;
+ }
+ }
+ } while (true);
+
+ ++threadWriteCount;
+ return;
+}
+
+void AsynchIO::disconnected(DispatchHandle& h) {
+ // If we have not already queued close then call disconnected callback before closing
+ if (!queuedClose && disCallback) disCallback(*this);
+ close(h);
+}
+
+/*
+ * Close the socket and callback to say we've done it
+ */
+void AsynchIO::close(DispatchHandle& h) {
+ h.stopWatch();
+ socket.close();
+ if (closedCallback) {
+ closedCallback(*this, socket);
+ }
+}
+
+} // namespace posix
+
+AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
+ Callback callback)
+{
+ return new posix::AsynchAcceptor(s, callback);
+}
+
+AsynchConnector* AsynchConnector::create(const Socket& s,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb)
+{
+ return new posix::AsynchConnector(s, hostname, port, connCb, failCb);
+}
+
+AsynchIO* AsynchIO::create(const Socket& s,
+ AsynchIO::ReadCallback rCb,
+ AsynchIO::EofCallback eofCb,
+ AsynchIO::DisconnectCallback disCb,
+ AsynchIO::ClosedCallback cCb,
+ AsynchIO::BuffersEmptyCallback eCb,
+ AsynchIO::IdleCallback iCb)
+{
+ return new posix::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp b/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp
new file mode 100755
index 0000000000..22dc487e74
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/FileSysDir.cpp
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/FileSysDir.h"
+#include "qpid/sys/StrError.h"
+#include "qpid/Exception.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <cerrno>
+#include <unistd.h>
+
+namespace qpid {
+namespace sys {
+
+bool FileSysDir::exists (void) const
+{
+ const char *cpath = dirPath.c_str ();
+ struct stat s;
+ if (::stat(cpath, &s)) {
+ if (errno == ENOENT) {
+ return false;
+ }
+ throw qpid::Exception (strError(errno) +
+ ": Can't check directory: " + dirPath);
+ }
+ if (S_ISDIR(s.st_mode))
+ return true;
+ throw qpid::Exception(dirPath + " is not a directory");
+}
+
+void FileSysDir::mkdir(void)
+{
+ if (::mkdir(dirPath.c_str(), 0755))
+ throw Exception ("Can't create directory: " + dirPath);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/Fork.cpp b/qpid/cpp/src/qpid/sys/posix/Fork.cpp
new file mode 100644
index 0000000000..a0d404a16e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Fork.cpp
@@ -0,0 +1,129 @@
+/*
+ *
+ * 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.
+ *
+ */
+#include "qpid/sys/Fork.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace qpid {
+namespace sys {
+
+using namespace std;
+
+namespace {
+
+void writeStr(int fd, const std::string& str) {
+ const char* WRITE_ERR = "Error writing to parent process";
+ int size = str.size();
+ if (int(sizeof(size)) > ::write(fd, &size, sizeof(size))) throw ErrnoException(WRITE_ERR);
+ if (size > ::write(fd, str.data(), size)) throw ErrnoException(WRITE_ERR);
+}
+
+string readStr(int fd) {
+ string value;
+ const char* READ_ERR = "Error reading from forked process";
+ int size;
+ if (int(sizeof(size)) > ::read(fd, &size, sizeof(size))) throw ErrnoException(READ_ERR);
+ if (size > 0) { // Read string message
+ value.resize(size);
+ if (size > ::read(fd, const_cast<char*>(value.data()), size)) throw ErrnoException(READ_ERR);
+ }
+ return value;
+}
+
+} // namespace
+
+Fork::Fork() {}
+Fork::~Fork() {}
+
+void Fork::fork() {
+ pid_t pid = ::fork();
+ if (pid < 0) throw ErrnoException("Failed to fork the process");
+ if (pid == 0) child();
+ else parent(pid);
+}
+
+ForkWithMessage::ForkWithMessage() {
+ pipeFds[0] = pipeFds[1] = -1;
+}
+
+struct AutoCloseFd {
+ int fd;
+ AutoCloseFd(int d) : fd(d) {}
+ ~AutoCloseFd() { ::close(fd); }
+};
+
+void ForkWithMessage::fork() {
+ if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
+ pid_t pid = ::fork();
+ if(pid < 0) throw ErrnoException("Fork fork failed");
+ if (pid == 0) { // Child
+ AutoCloseFd ac(pipeFds[1]); // Write side.
+ ::close(pipeFds[0]); // Read side
+ try {
+ child();
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(error, "Error in forked child: " << e.what());
+ std::string msg = e.what();
+ if (msg.empty()) msg = " "; // Make sure we send a non-empty error string.
+ writeStr(pipeFds[1], msg);
+ }
+ }
+ else { // Parent
+ close(pipeFds[1]); // Write side.
+ AutoCloseFd ac(pipeFds[0]); // Read side
+ parent(pid);
+ }
+}
+
+string ForkWithMessage::wait(int timeout) { // parent waits for child.
+ errno = 0;
+ struct timeval tv;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(pipeFds[0], &fds);
+ int n=select(FD_SETSIZE, &fds, 0, 0, &tv);
+ if(n<0) throw ErrnoException("Error waiting for fork");
+ if (n==0) throw Exception("Timed out waiting for fork");
+
+ string error = readStr(pipeFds[0]);
+ if (error.empty()) return readStr(pipeFds[0]);
+ else throw Exception("Error in forked process: " + error);
+}
+
+// Write empty error string followed by value string to pipe.
+void ForkWithMessage::ready(const string& value) { // child
+ // Write empty string for error followed by value.
+ writeStr(pipeFds[1], string()); // No error
+ writeStr(pipeFds[1], value);
+}
+
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/Fork.h b/qpid/cpp/src/qpid/sys/posix/Fork.h
new file mode 100644
index 0000000000..698c61ed30
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Fork.h
@@ -0,0 +1,82 @@
+#ifndef QPID_SYS_POSIX_FORK_H
+#define QPID_SYS_POSIX_FORK_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <string>
+#include <sys/types.h>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Fork the process. Call parent() in parent and child() in child.
+ */
+class Fork {
+ public:
+ Fork();
+ virtual ~Fork();
+
+ /**
+ * Fork the process.
+ * Calls parent() in the parent process, child() in the child.
+ */
+ virtual void fork();
+
+ protected:
+
+ /** Called in parent process.
+ *@child pid of child process
+ */
+ virtual void parent(pid_t child) = 0;
+
+ /** Called in child process */
+ virtual void child() = 0;
+};
+
+/**
+ * Like Fork but also allows the child to send a string message
+ * or throw an exception to the parent.
+ */
+class ForkWithMessage : public Fork {
+ public:
+ ForkWithMessage();
+ void fork();
+
+ protected:
+ /** Call from parent(): wait for child to send a value or throw exception.
+ * @timeout in seconds to wait for response.
+ * @return value passed by child to ready().
+ */
+ std::string wait(int timeout);
+
+ /** Call from child(): Send a value to the parent.
+ *@param value returned by parent call to wait().
+ */
+ void ready(const std::string& value);
+
+ private:
+ int pipeFds[2];
+};
+
+}} // namespace qpid::sys
+
+
+
+#endif /*!QPID_SYS_POSIX_FORK_H*/
diff --git a/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp b/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp
new file mode 100644
index 0000000000..9c049ee1de
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/IOHandle.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/IOHandle.h"
+
+#include "qpid/sys/posix/PrivatePosix.h"
+
+namespace qpid {
+namespace sys {
+
+int toFd(const IOHandlePrivate* h)
+{
+ return h->fd;
+}
+
+NullIOHandle DummyIOHandle;
+
+IOHandle::IOHandle(IOHandlePrivate* h) :
+ impl(h)
+{}
+
+IOHandle::~IOHandle() {
+ delete impl;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/LockFile.cpp b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
new file mode 100755
index 0000000000..1862ff6ac9
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+
+#include "qpid/sys/LockFile.h"
+#include "qpid/sys/posix/PidFile.h"
+
+#include <string>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "qpid/sys/posix/check.h"
+
+namespace qpid {
+namespace sys {
+
+class LockFilePrivate {
+ friend class LockFile;
+ friend class PidFile;
+
+ int fd;
+
+public:
+ LockFilePrivate(int f) : fd(f) {}
+};
+
+LockFile::LockFile(const std::string& path_, bool create)
+ : path(path_), created(create) {
+
+ errno = 0;
+ int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR;
+ int fd = ::open(path.c_str(), flags, 0644);
+ if (fd < 0) throw ErrnoException("Cannot open " + path, errno);
+ if (::lockf(fd, F_TLOCK, 0) < 0) {
+ ::close(fd);
+ throw ErrnoException("Cannot lock " + path, errno);
+ }
+ impl.reset(new LockFilePrivate(fd));
+}
+
+LockFile::~LockFile() {
+ if (impl) {
+ int f = impl->fd;
+ if (f >= 0) {
+ int unused_ret;
+ unused_ret = ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value.
+ ::close(f);
+ impl->fd = -1;
+ }
+ }
+}
+
+int LockFile::read(void* bytes, size_t len) const {
+ if (!impl)
+ throw Exception("Lock file not open: " + path);
+
+ ssize_t rc = ::read(impl->fd, bytes, len);
+ if ((ssize_t)len > rc) {
+ throw Exception("Cannot read lock file: " + path);
+ }
+ return rc;
+}
+
+int LockFile::write(void* bytes, size_t len) const {
+ if (!impl)
+ throw Exception("Lock file not open: " + path);
+
+ ssize_t rc = ::write(impl->fd, bytes, len);
+ if ((ssize_t)len > rc) {
+ throw Exception("Cannot write lock file: " + path);
+ }
+ return rc;
+}
+
+PidFile::PidFile(const std::string& path_, bool create):
+ LockFile(path_, create)
+{}
+
+pid_t PidFile::readPid(void) const {
+ pid_t pid;
+ int desired_read = sizeof(pid_t);
+ read(&pid, desired_read);
+ return pid;
+}
+
+void PidFile::writePid(void) {
+ pid_t pid = getpid();
+ int desired_write = sizeof(pid_t);
+ write(&pid, desired_write);
+}
+
+}} /* namespace qpid::sys */
diff --git a/qpid/cpp/src/qpid/sys/posix/Mutex.cpp b/qpid/cpp/src/qpid/sys/posix/Mutex.cpp
new file mode 100644
index 0000000000..0e1f0d30c2
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Mutex.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace sys {
+
+/**
+ * Initialise a recursive mutex attr for use in creating mutexes later
+ * (we use pthread_once to make sure it is initialised exactly once)
+ */
+
+namespace {
+pthread_once_t onceControl = PTHREAD_ONCE_INIT;
+pthread_mutexattr_t mutexattr;
+
+void initMutexattr() {
+ pthread_mutexattr_init(&mutexattr);
+ pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
+}
+}
+
+const pthread_mutexattr_t* Mutex::getAttribute() {
+ pthread_once(&onceControl, initMutexattr);
+ return &mutexattr;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/PidFile.h b/qpid/cpp/src/qpid/sys/posix/PidFile.h
new file mode 100644
index 0000000000..fb19d407f4
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/PidFile.h
@@ -0,0 +1,62 @@
+#ifndef _sys_PidFile_h
+#define _sys_PidFile_h
+
+/*
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+
+#include "qpid/sys/LockFile.h"
+
+#include "qpid/CommonImportExport.h"
+#include "qpid/sys/IntegerTypes.h"
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace qpid {
+namespace sys {
+
+class PidFile : public LockFile
+{
+public:
+ QPID_COMMON_EXTERN PidFile(const std::string& path_, bool create);
+
+ /**
+ * Read the process ID from the lock file. This method assumes that
+ * if there is a process ID in the file, it was written there by
+ * writePid(); thus, it's at the start of the file.
+ *
+ * Throws an exception if there is an error reading the file.
+ *
+ * @returns The stored process ID. No validity check is done on it.
+ */
+ QPID_COMMON_EXTERN pid_t readPid(void) const;
+
+ /**
+ * Write the current process's ID to the lock file. It's written at
+ * the start of the file and will overwrite any other content that
+ * may be in the file.
+ *
+ * Throws an exception if the write fails.
+ */
+ QPID_COMMON_EXTERN void writePid(void);
+};
+
+}} /* namespace qpid::sys */
+
+#endif /*!_sys_PidFile_h*/
diff --git a/qpid/cpp/src/qpid/sys/posix/PipeHandle.cpp b/qpid/cpp/src/qpid/sys/posix/PipeHandle.cpp
new file mode 100755
index 0000000000..4b19783338
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/PipeHandle.cpp
@@ -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.
+//
+
+#include "qpid/sys/PipeHandle.h"
+#include "qpid/sys/posix/check.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+namespace qpid {
+namespace sys {
+
+PipeHandle::PipeHandle(bool nonBlocking) {
+
+ int pair[2];
+ pair[0] = pair[1] = -1;
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, pair) == -1)
+ throw qpid::Exception(QPID_MSG("Creation of pipe failed"));
+
+ writeFd = pair[0];
+ readFd = pair[1];
+
+ // Set the socket to non-blocking
+ if (nonBlocking) {
+ int flags = fcntl(readFd, F_GETFL);
+ fcntl(readFd, F_SETFL, flags | O_NONBLOCK);
+ }
+}
+
+PipeHandle::~PipeHandle() {
+ close(readFd);
+ close(writeFd);
+}
+
+int PipeHandle::read(void* buf, size_t bufSize) {
+ return ::read(readFd,buf,bufSize);
+}
+
+int PipeHandle::write(const void* buf, size_t bufSize) {
+ return ::write(writeFd,buf,bufSize);
+}
+
+int PipeHandle::getReadHandle() {
+ return readFd;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp b/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp
new file mode 100644
index 0000000000..b22a615a54
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp
@@ -0,0 +1,124 @@
+#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP
+#define QPID_SYS_LINUX_POLLABLECONDITION_CPP
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/Exception.h"
+
+#include <boost/bind.hpp>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace qpid {
+namespace sys {
+
+class PollableConditionPrivate : public sys::IOHandle {
+ friend class PollableCondition;
+
+private:
+ PollableConditionPrivate(const sys::PollableCondition::Callback& cb,
+ sys::PollableCondition& parent,
+ const boost::shared_ptr<sys::Poller>& poller);
+ ~PollableConditionPrivate();
+
+ void dispatch(sys::DispatchHandle& h);
+ void set();
+ void clear();
+
+private:
+ PollableCondition::Callback cb;
+ PollableCondition& parent;
+ boost::shared_ptr<sys::Poller> poller;
+ int writeFd;
+ std::auto_ptr<DispatchHandleRef> handle;
+};
+
+PollableConditionPrivate::PollableConditionPrivate(
+ const sys::PollableCondition::Callback& cb,
+ sys::PollableCondition& parent,
+ const boost::shared_ptr<sys::Poller>& poller
+) : IOHandle(new sys::IOHandlePrivate), cb(cb), parent(parent)
+{
+ int fds[2];
+ if (::pipe(fds) == -1)
+ throw ErrnoException(QPID_MSG("Can't create PollableCondition"));
+ impl->fd = fds[0];
+ writeFd = fds[1];
+ if (::fcntl(impl->fd, F_SETFL, O_NONBLOCK) == -1)
+ throw ErrnoException(QPID_MSG("Can't create PollableCondition"));
+ if (::fcntl(writeFd, F_SETFL, O_NONBLOCK) == -1)
+ throw ErrnoException(QPID_MSG("Can't create PollableCondition"));
+ handle.reset (new DispatchHandleRef(
+ *this,
+ boost::bind(&sys::PollableConditionPrivate::dispatch, this, _1),
+ 0, 0));
+ handle->startWatch(poller);
+ handle->unwatch();
+
+ // Make the read FD readable
+ static const char dummy=0;
+ ssize_t n = ::write(writeFd, &dummy, 1);
+ if (n == -1 && errno != EAGAIN)
+ throw ErrnoException("Error setting PollableCondition");
+}
+
+PollableConditionPrivate::~PollableConditionPrivate() {
+ handle->stopWatch();
+ close(writeFd);
+}
+
+void PollableConditionPrivate::dispatch(sys::DispatchHandle&) {
+ cb(parent);
+}
+
+void PollableConditionPrivate::set() {
+ handle->rewatch();
+}
+
+void PollableConditionPrivate::clear() {
+ handle->unwatch();
+}
+
+
+PollableCondition::PollableCondition(const Callback& cb,
+ const boost::shared_ptr<sys::Poller>& poller
+) : impl(new PollableConditionPrivate(cb, *this, poller))
+{
+}
+
+PollableCondition::~PollableCondition()
+{
+ delete impl;
+}
+
+void PollableCondition::set() { impl->set(); }
+
+void PollableCondition::clear() { impl->clear(); }
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/
diff --git a/qpid/cpp/src/qpid/sys/posix/Shlib.cpp b/qpid/cpp/src/qpid/sys/posix/Shlib.cpp
new file mode 100644
index 0000000000..3fb685d5b8
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Shlib.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Shlib.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include <dlfcn.h>
+
+
+namespace qpid {
+namespace sys {
+
+void Shlib::load(const char* name) {
+ ::dlerror();
+ handle = ::dlopen(name, RTLD_NOW);
+ const char* error = ::dlerror();
+ if (error) {
+ throw Exception(QPID_MSG(error << ": " << name));
+ }
+}
+
+void Shlib::unload() {
+ if (handle) {
+ ::dlerror();
+ ::dlclose(handle);
+ const char* error = ::dlerror();
+ if (error) {
+ throw Exception(QPID_MSG(error));
+ }
+ handle = 0;
+ }
+}
+
+void* Shlib::getSymbol(const char* name) {
+ ::dlerror();
+ void* sym = ::dlsym(handle, name);
+ const char* error = ::dlerror();
+ if (error)
+ throw Exception(QPID_MSG(error << ": " << name));
+ return sym;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/Socket.cpp b/qpid/cpp/src/qpid/sys/posix/Socket.cpp
new file mode 100644
index 0000000000..aa25f8062d
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Socket.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Socket.h"
+
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/posix/check.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <cstdlib>
+#include <string.h>
+#include <iostream>
+
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace sys {
+
+namespace {
+std::string getName(int fd, bool local)
+{
+ ::sockaddr_storage name; // big enough for any socket address
+ ::socklen_t namelen = sizeof(name);
+
+ int result = -1;
+ if (local) {
+ result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
+ } else {
+ result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
+ }
+ QPID_POSIX_CHECK(result);
+
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ return std::string(dispName) + ":" + std::string(servName);
+}
+}
+
+Socket::Socket() :
+ IOHandle(new IOHandlePrivate),
+ nonblocking(false),
+ nodelay(false)
+{}
+
+Socket::Socket(IOHandlePrivate* h) :
+ IOHandle(h),
+ nonblocking(false),
+ nodelay(false)
+{}
+
+void Socket::createSocket(const SocketAddress& sa) const
+{
+ int& socket = impl->fd;
+ if (socket != -1) Socket::close();
+ int s = ::socket(getAddrInfo(sa).ai_family, getAddrInfo(sa).ai_socktype, 0);
+ if (s < 0) throw QPID_POSIX_ERROR(errno);
+ socket = s;
+
+ try {
+ if (nonblocking) setNonblocking();
+ if (nodelay) setTcpNoDelay();
+ } catch (std::exception&) {
+ ::close(s);
+ socket = -1;
+ throw;
+ }
+}
+
+void Socket::setNonblocking() const {
+ int& socket = impl->fd;
+ nonblocking = true;
+ if (socket != -1) {
+ QPID_POSIX_CHECK(::fcntl(socket, F_SETFL, O_NONBLOCK));
+ }
+}
+
+void Socket::setTcpNoDelay() const
+{
+ int& socket = impl->fd;
+ nodelay = true;
+ if (socket != -1) {
+ int flag = 1;
+ int result = setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
+ QPID_POSIX_CHECK(result);
+ }
+}
+
+void Socket::connect(const std::string& host, const std::string& port) const
+{
+ SocketAddress sa(host, port);
+ connect(sa);
+}
+
+void Socket::connect(const SocketAddress& addr) const
+{
+ // The display name for an outbound connection needs to be the name that was specified
+ // for the address rather than a resolved IP address as we don't know which of
+ // the IP addresses is actually the one that will be connected to.
+ peername = addr.asString(false);
+
+ // However the string we compare with the local port must be numeric or it might not
+ // match when it should as getLocalAddress() will always be numeric
+ std::string connectname = addr.asString();
+
+ createSocket(addr);
+
+ const int& socket = impl->fd;
+ // TODO the correct thing to do here is loop on failure until you've used all the returned addresses
+ if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) < 0) &&
+ (errno != EINPROGRESS)) {
+ throw Exception(QPID_MSG(strError(errno) << ": " << peername));
+ }
+ // When connecting to a port on the same host which no longer has
+ // a process associated with it, the OS occasionally chooses the
+ // remote port (which is unoccupied) as the port to bind the local
+ // end of the socket, resulting in a "circular" connection.
+ //
+ // This seems like something the OS should prevent but I have
+ // confirmed that sporadic hangs in
+ // cluster_tests.LongTests.test_failover on RHEL5 are caused by
+ // such a circular connection.
+ //
+ // Raise an error if we see such a connection, since we know there is
+ // no listener on the peer address.
+ //
+ if (getLocalAddress() == connectname) {
+ close();
+ throw Exception(QPID_MSG("Connection refused: " << peername));
+ }
+}
+
+void
+Socket::close() const
+{
+ int& socket = impl->fd;
+ if (socket == -1) return;
+ if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno);
+ socket = -1;
+}
+
+int Socket::listen(const std::string& host, const std::string& port, int backlog) const
+{
+ SocketAddress sa(host, port);
+ return listen(sa, backlog);
+}
+
+int Socket::listen(const SocketAddress& sa, int backlog) const
+{
+ createSocket(sa);
+
+ const int& socket = impl->fd;
+ int yes=1;
+ QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
+
+ if (::bind(socket, getAddrInfo(sa).ai_addr, getAddrInfo(sa).ai_addrlen) < 0)
+ throw Exception(QPID_MSG("Can't bind to port " << sa.asString() << ": " << strError(errno)));
+ if (::listen(socket, backlog) < 0)
+ throw Exception(QPID_MSG("Can't listen on port " << sa.asString() << ": " << strError(errno)));
+
+ struct sockaddr_in name;
+ socklen_t namelen = sizeof(name);
+ if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0)
+ throw QPID_POSIX_ERROR(errno);
+
+ return ntohs(name.sin_port);
+}
+
+Socket* Socket::accept() const
+{
+ int afd = ::accept(impl->fd, 0, 0);
+ if ( afd >= 0) {
+ Socket* s = new Socket(new IOHandlePrivate(afd));
+ s->localname = localname;
+ return s;
+ }
+ else if (errno == EAGAIN)
+ return 0;
+ else throw QPID_POSIX_ERROR(errno);
+}
+
+int Socket::read(void *buf, size_t count) const
+{
+ return ::read(impl->fd, buf, count);
+}
+
+int Socket::write(const void *buf, size_t count) const
+{
+ return ::write(impl->fd, buf, count);
+}
+
+std::string Socket::getPeerAddress() const
+{
+ if (peername.empty()) {
+ peername = getName(impl->fd, false);
+ }
+ return peername;
+}
+
+std::string Socket::getLocalAddress() const
+{
+ if (localname.empty()) {
+ localname = getName(impl->fd, true);
+ }
+ return localname;
+}
+
+int Socket::getError() const
+{
+ int result;
+ socklen_t rSize = sizeof (result);
+
+ if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0)
+ throw QPID_POSIX_ERROR(errno);
+
+ return result;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
new file mode 100644
index 0000000000..10f1c8a563
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/SocketAddress.h"
+
+#include "qpid/sys/posix/check.h"
+
+#include <sys/socket.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <algorithm>
+
+namespace qpid {
+namespace sys {
+
+SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) :
+ host(host0),
+ port(port0),
+ addrInfo(0)
+{
+}
+
+SocketAddress::SocketAddress(const SocketAddress& sa) :
+ host(sa.host),
+ port(sa.port),
+ addrInfo(0)
+{
+}
+
+SocketAddress& SocketAddress::operator=(const SocketAddress& sa)
+{
+ SocketAddress temp(sa);
+
+ std::swap(temp, *this);
+ return *this;
+}
+
+SocketAddress::~SocketAddress()
+{
+ if (addrInfo) {
+ ::freeaddrinfo(addrInfo);
+ }
+}
+
+std::string SocketAddress::asString(bool numeric) const
+{
+ if (!numeric)
+ return host + ":" + port;
+ // Canonicalise into numeric id
+ const ::addrinfo& ai = getAddrInfo(*this);
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo(ai.ai_addr, ai.ai_addrlen,
+ dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ std::string s(dispName);
+ s += ":";
+ s += servName;
+ return s;
+}
+
+const ::addrinfo& getAddrInfo(const SocketAddress& sa)
+{
+ if (!sa.addrInfo) {
+ ::addrinfo hints;
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; // Change this to support IPv6
+ hints.ai_socktype = SOCK_STREAM;
+
+ const char* node = 0;
+ if (sa.host.empty()) {
+ hints.ai_flags |= AI_PASSIVE;
+ } else {
+ node = sa.host.c_str();
+ }
+ const char* service = sa.port.empty() ? "0" : sa.port.c_str();
+
+ int n = ::getaddrinfo(node, service, &hints, &sa.addrInfo);
+ if (n != 0)
+ throw Exception(QPID_MSG("Cannot resolve " << sa.asString(false) << ": " << ::gai_strerror(n)));
+ }
+
+ return *sa.addrInfo;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/posix/StrError.cpp b/qpid/cpp/src/qpid/sys/posix/StrError.cpp
new file mode 100644
index 0000000000..633e20213c
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/StrError.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/StrError.h"
+
+#include <string.h>
+
+namespace qpid {
+namespace sys {
+
+std::string strError(int err) {
+ char buf[512] = "Unknown error";
+#ifdef _GNU_SOURCE
+ // GNU strerror_r returns the message
+ return ::strerror_r(err, buf, sizeof(buf));
+#else
+ // POSIX strerror_r doesn't return the buffer
+ ::strerror_r(err, buf, sizeof(buf));
+ return std::string(buf);
+#endif
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp
new file mode 100755
index 0000000000..a19ab6885c
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/SystemInfo.h"
+
+#include "qpid/sys/posix/check.h"
+
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/types.h> // For FreeBSD
+#include <sys/socket.h> // For FreeBSD
+#include <netinet/in.h> // For FreeBSD
+#include <ifaddrs.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <netdb.h>
+
+#ifndef HOST_NAME_MAX
+# define HOST_NAME_MAX 256
+#endif
+
+using namespace std;
+
+namespace qpid {
+namespace sys {
+
+long SystemInfo::concurrency() {
+#ifdef _SC_NPROCESSORS_ONLN // Linux specific.
+ return sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ return -1;
+#endif
+}
+
+bool SystemInfo::getLocalHostname (Address &address) {
+ char name[HOST_NAME_MAX];
+ if (::gethostname(name, sizeof(name)) != 0)
+ return false;
+ address.host = name;
+ return true;
+}
+
+static const string LOCALHOST("127.0.0.1");
+static const string TCP("tcp");
+
+void SystemInfo::getLocalIpAddresses (uint16_t port,
+ std::vector<Address> &addrList) {
+ ::ifaddrs* ifaddr = 0;
+ QPID_POSIX_CHECK(::getifaddrs(&ifaddr));
+ for (::ifaddrs* ifap = ifaddr; ifap != 0; ifap = ifap->ifa_next) {
+ if (ifap->ifa_addr == 0) continue;
+
+ int family = ifap->ifa_addr->sa_family;
+ switch (family) {
+ case AF_INET: {
+ char dispName[NI_MAXHOST];
+ int rc = ::getnameinfo(
+ ifap->ifa_addr,
+ (family == AF_INET)
+ ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6),
+ dispName, sizeof(dispName),
+ 0, 0, NI_NUMERICHOST);
+ if (rc != 0) {
+ throw QPID_POSIX_ERROR(rc);
+ }
+ string addr(dispName);
+ if (addr != LOCALHOST) {
+ addrList.push_back(Address(TCP, addr, port));
+ }
+ break;
+ }
+ // TODO: Url parsing currently can't cope with IPv6 addresses so don't return them
+ // when it can cope move this line to above "case AF_INET:"
+ case AF_INET6:
+ default:
+ continue;
+ }
+ }
+ freeifaddrs(ifaddr);
+
+ if (addrList.empty()) {
+ addrList.push_back(Address(TCP, LOCALHOST, port));
+ }
+}
+
+void SystemInfo::getSystemId (std::string &osName,
+ std::string &nodeName,
+ std::string &release,
+ std::string &version,
+ std::string &machine)
+{
+ struct utsname _uname;
+ if (uname (&_uname) == 0)
+ {
+ osName = _uname.sysname;
+ nodeName = _uname.nodename;
+ release = _uname.release;
+ version = _uname.version;
+ machine = _uname.machine;
+ }
+}
+
+uint32_t SystemInfo::getProcessId()
+{
+ return (uint32_t) ::getpid();
+}
+
+uint32_t SystemInfo::getParentProcessId()
+{
+ return (uint32_t) ::getppid();
+}
+
+// Linux specific (Solaris has quite different stuff in /proc)
+string SystemInfo::getProcessName()
+{
+ string value;
+
+ ifstream input("/proc/self/status");
+ if (input.good()) {
+ while (!input.eof()) {
+ string key;
+ input >> key;
+ if (key == "Name:") {
+ input >> value;
+ break;
+ }
+ }
+ input.close();
+ }
+
+ return value;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/posix/Thread.cpp b/qpid/cpp/src/qpid/sys/posix/Thread.cpp
new file mode 100644
index 0000000000..a1d6396763
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Thread.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Thread.h"
+
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/posix/check.h"
+
+#include <pthread.h>
+
+namespace qpid {
+namespace sys {
+
+namespace {
+void* runRunnable(void* p)
+{
+ static_cast<Runnable*>(p)->run();
+ return 0;
+}
+}
+
+class ThreadPrivate {
+public:
+ pthread_t thread;
+
+ ThreadPrivate(Runnable* runnable) {
+ QPID_POSIX_ASSERT_THROW_IF(::pthread_create(&thread, NULL, runRunnable, runnable));
+ }
+
+ ThreadPrivate() : thread(::pthread_self()) {}
+};
+
+Thread::Thread() {}
+
+Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {}
+
+Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {}
+
+Thread::operator bool() {
+ return impl;
+}
+
+bool Thread::operator==(const Thread& t) const {
+ return ::pthread_equal(impl->thread, t.impl->thread) != 0;
+}
+
+bool Thread::operator!=(const Thread& t) const {
+ return !(*this==t);
+}
+
+void Thread::join(){
+ if (impl) {
+ QPID_POSIX_ASSERT_THROW_IF(::pthread_join(impl->thread, 0));
+ }
+}
+
+unsigned long Thread::logId() {
+ // This does need to be the C cast operator as
+ // pthread_t could be either a pointer or an integer
+ // and so we can't know static_cast<> or reinterpret_cast<>
+ return (unsigned long) ::pthread_self();
+}
+
+Thread Thread::current() {
+ Thread t;
+ t.impl.reset(new ThreadPrivate());
+ return t;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/posix/Time.cpp b/qpid/cpp/src/qpid/sys/posix/Time.cpp
new file mode 100644
index 0000000000..9661f0c5e8
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/Time.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include "qpid/sys/Time.h"
+#include <ostream>
+#include <time.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <iomanip>
+
+namespace {
+int64_t max_abstime() { return std::numeric_limits<int64_t>::max(); }
+}
+
+namespace qpid {
+namespace sys {
+
+AbsTime::AbsTime(const AbsTime& t, const Duration& d) :
+ timepoint(d == Duration::max() ? max_abstime() : t.timepoint+d.nanosecs)
+{}
+
+AbsTime AbsTime::Epoch() {
+ AbsTime epoch; epoch.timepoint = 0;
+ return epoch;
+}
+
+AbsTime AbsTime::FarFuture() {
+ AbsTime ff; ff.timepoint = max_abstime(); return ff;
+}
+
+AbsTime AbsTime::now() {
+ struct timespec ts;
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ AbsTime time_now;
+ time_now.timepoint = toTime(ts).nanosecs;
+ return time_now;
+}
+
+Duration::Duration(const AbsTime& start, const AbsTime& finish) :
+ nanosecs(finish.timepoint - start.timepoint)
+{}
+
+struct timespec& toTimespec(struct timespec& ts, const Duration& t) {
+ ts.tv_sec = t / TIME_SEC;
+ ts.tv_nsec = t % TIME_SEC;
+ return ts;
+}
+
+struct timeval& toTimeval(struct timeval& tv, const Duration& t) {
+ tv.tv_sec = t/TIME_SEC;
+ tv.tv_usec = (t%TIME_SEC)/TIME_USEC;
+ return tv;
+}
+
+Duration toTime(const struct timespec& ts) {
+ return ts.tv_sec*TIME_SEC + ts.tv_nsec;
+}
+
+std::ostream& operator<<(std::ostream& o, const Duration& d) {
+ return o << int64_t(d) << "ns";
+}
+
+namespace {
+inline std::ostream& outputFormattedTime(std::ostream& o, const ::time_t* time) {
+ ::tm timeinfo;
+ char time_string[100];
+ ::strftime(time_string, 100,
+ "%Y-%m-%d %H:%M:%S",
+ localtime_r(time, &timeinfo));
+ return o << time_string;
+}
+}
+
+std::ostream& operator<<(std::ostream& o, const AbsTime& t) {
+ ::time_t rawtime(t.timepoint/TIME_SEC);
+ return outputFormattedTime(o, &rawtime);
+}
+
+void outputFormattedNow(std::ostream& o) {
+ ::time_t rawtime;
+ ::time(&rawtime);
+ outputFormattedTime(o, &rawtime);
+ o << " ";
+}
+
+void outputHiresNow(std::ostream& o) {
+ ::timespec time;
+ ::clock_gettime(CLOCK_REALTIME, &time);
+ o << time.tv_sec << "." << std::setw(9) << std::setfill('0') << time.tv_nsec << "s ";
+}
+
+void sleep(int secs) {
+ ::sleep(secs);
+}
+
+void usleep(uint64_t usecs) {
+ ::usleep(usecs);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp
new file mode 100644
index 0000000000..38e9b59541
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/RdmaClient.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/rdma/RdmaIO.h"
+#include "qpid/sys/rdma/rdma_exception.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <algorithm>
+#include <cmath>
+#include <boost/bind.hpp>
+
+using std::vector;
+using std::string;
+using std::cout;
+using std::cerr;
+using std::copy;
+using std::rand;
+
+using qpid::sys::Thread;
+using qpid::sys::Poller;
+using qpid::sys::Dispatcher;
+using qpid::sys::SocketAddress;
+using qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::sys::TIME_SEC;
+using qpid::sys::TIME_INFINITE;
+
+namespace qpid {
+namespace tests {
+
+// count of messages
+int64_t smsgs = 0;
+int64_t sbytes = 0;
+int64_t rmsgs = 0;
+int64_t rbytes = 0;
+
+int target = 1000000;
+int msgsize = 200;
+AbsTime startTime;
+Duration sendingDuration(TIME_INFINITE);
+Duration fullTestDuration(TIME_INFINITE);
+
+// Random generator
+// This is an RNG designed by George Marsaglia see http://en.wikipedia.org/wiki/Xorshift
+class Xor128Generator {
+ uint32_t x;
+ uint32_t y;
+ uint32_t z;
+ uint32_t w;
+
+public:
+ Xor128Generator() :
+ x(123456789),y(362436069),z(521288629),w(88675123)
+ {++(*this);}
+
+ Xor128Generator& operator++() {
+ uint32_t t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w = w ^ (w >> 19) ^ t ^ (t >> 8);
+ return *this;
+ }
+
+ uint32_t operator*() {
+ return w;
+ }
+};
+
+Xor128Generator output;
+Xor128Generator input;
+
+void write(Rdma::AsynchIO& aio) {
+ while (aio.writable() && smsgs < target) {
+ Rdma::Buffer* b = aio.getSendBuffer();
+ if (!b) break;
+ b->dataCount(msgsize);
+ uint32_t* ip = reinterpret_cast<uint32_t*>(b->bytes());
+ uint32_t* lip = ip + b->dataCount() / sizeof(uint32_t);
+ while (ip != lip) {*ip++ = *output; ++output;}
+ aio.queueWrite(b);
+ ++smsgs;
+ sbytes += msgsize;
+ }
+}
+
+void dataError(Rdma::AsynchIO&) {
+ cout << "Data error:\n";
+}
+
+void data(Poller::shared_ptr p, Rdma::AsynchIO& aio, Rdma::Buffer* b) {
+ ++rmsgs;
+ rbytes += b->dataCount();
+
+ // Check message is unaltered
+ bool match = true;
+ uint32_t* ip = reinterpret_cast<uint32_t*>(b->bytes());
+ uint32_t* lip = ip + b->dataCount() / sizeof(uint32_t);
+ while (ip != lip) { if (*ip++ != *input) {match = false; break;} ++input;}
+ if (!match) {
+ cout << "Data doesn't match: at msg " << rmsgs << " byte " << rbytes-b->dataCount() << " (ish)\n";
+ exit(1);
+ }
+
+ // When all messages have been recvd stop
+ if (rmsgs < target) {
+ write(aio);
+ } else {
+ fullTestDuration = std::min(fullTestDuration, Duration(startTime, AbsTime::now()));
+ if (aio.incompletedWrites() == 0)
+ p->shutdown();
+ }
+}
+
+void full(Rdma::AsynchIO& a, Rdma::Buffer* b) {
+ // Warn as we shouldn't get here anymore
+ cerr << "!";
+
+ // Don't need to keep buffer just adjust the counts
+ --smsgs;
+ sbytes -= b->dataCount();
+
+ // Give buffer back
+ a.returnSendBuffer(b);
+}
+
+void idle(Poller::shared_ptr p, Rdma::AsynchIO& aio) {
+ if (smsgs < target) {
+ write(aio);
+ } else {
+ sendingDuration = std::min(sendingDuration, Duration(startTime, AbsTime::now()));
+ if (rmsgs >= target && aio.incompletedWrites() == 0)
+ p->shutdown();
+ }
+}
+
+void drained(Rdma::AsynchIO&) {
+ cout << "Drained:\n";
+}
+
+void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) {
+ cout << "Connected\n";
+ Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair();
+
+ Rdma::AsynchIO* aio = new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.rdmaProtocolVersion,
+ cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES,
+ boost::bind(&data, poller, _1, _2),
+ boost::bind(&idle, poller, _1),
+ &full,
+ dataError);
+
+ startTime = AbsTime::now();
+ write(*aio);
+
+ aio->start(poller);
+}
+
+void disconnected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&) {
+ cout << "Disconnected\n";
+ p->shutdown();
+}
+
+void connectionError(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ErrorType) {
+ cout << "Connection error\n";
+ p->shutdown();
+}
+
+void rejected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&) {
+ cout << "Connection rejected\n";
+ p->shutdown();
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char* argv[]) {
+ vector<string> args(&argv[0], &argv[argc]);
+
+ string host = args[1];
+ string port = (args.size() < 3) ? "20079" : args[2];
+
+ if (args.size() > 3)
+ msgsize = atoi(args[3].c_str());
+ cout << "Message size: " << msgsize << "\n";
+
+ try {
+ boost::shared_ptr<Poller> p(new Poller());
+
+ Rdma::Connector c(
+ Rdma::ConnectionParams(msgsize, Rdma::DEFAULT_WR_ENTRIES),
+ boost::bind(&connected, p, _1, _2),
+ boost::bind(&connectionError, p, _1, _2),
+ boost::bind(&disconnected, p, _1),
+ boost::bind(&rejected, p, _1, _2));
+
+ SocketAddress sa(host, port);
+ cout << "Connecting to: " << sa.asString() <<"\n";
+ c.start(p, sa);
+
+ // The poller loop blocks all signals so run in its own thread
+ Thread t(*p);
+ t.join();
+ } catch (Rdma::Exception& e) {
+ int err = e.getError();
+ cerr << "Error: " << e.what() << "(" << err << ")\n";
+ }
+
+ cout
+ << "Sent: " << smsgs
+ << "msgs (" << sbytes
+ << "bytes) in: " << double(sendingDuration)/TIME_SEC
+ << "s: " << double(smsgs)*TIME_SEC/sendingDuration
+ << "msgs/s(" << double(sbytes)*TIME_SEC/sendingDuration
+ << "bytes/s)\n";
+ cout
+ << "Recd: " << rmsgs
+ << "msgs (" << rbytes
+ << "bytes) in: " << double(fullTestDuration)/TIME_SEC
+ << "s: " << double(rmsgs)*TIME_SEC/fullTestDuration
+ << "msgs/s(" << double(rbytes)*TIME_SEC/fullTestDuration
+ << "bytes/s)\n";
+
+}
diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp
new file mode 100644
index 0000000000..78bcdec68e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.cpp
@@ -0,0 +1,720 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/rdma/RdmaIO.h"
+
+#include "qpid/log/Statement.h"
+
+#include <string>
+#include <boost/bind.hpp>
+
+using qpid::sys::SocketAddress;
+using qpid::sys::DispatchHandle;
+using qpid::sys::Poller;
+using qpid::sys::ScopedLock;
+using qpid::sys::Mutex;
+
+namespace Rdma {
+ // Set packing as these are 'on the wire' structures
+# pragma pack(push, 1)
+
+ // Header structure for each transmitted frame
+ struct FrameHeader {
+ const static uint32_t FlagsMask = 0xf0000000;
+ uint32_t data; // written in network order
+
+ FrameHeader() {}
+ FrameHeader(uint32_t credit, uint32_t flags = 0) {
+ data = htonl((credit & ~FlagsMask) | (flags & FlagsMask));
+ }
+
+ uint32_t credit() const {
+ return ntohl(data) & ~FlagsMask;
+ }
+
+ uint32_t flags() const {
+ return ntohl(data) & FlagsMask;
+ }
+ };
+
+ const size_t FrameHeaderSize = sizeof(FrameHeader);
+
+ // Structure for Connection Parameters on the network
+ //
+ // The original version (now called 0) of these parameters had a couple of mistakes:
+ // * No way to version the protocol (need to introduce a new protocol for iWarp)
+ // * Used host order int32 (but only deployed on LE archs as far as we know)
+ // so effectively was LE on the wire which is the opposite of network order.
+ //
+ // Fortunately the values sent were sufficiently restricted that a 16 bit short could
+ // be carved out to indicate the protocol version as these bits were always sent as 0.
+ //
+ // So the current version of parameters uses the last 2 bytes to indicate the protocol
+ // version, if this is 0 then we interpret the rest of the struct without byte swapping
+ // to remain compatible with the previous protocol.
+ struct NConnectionParams {
+ uint32_t maxRecvBufferSize;
+ uint16_t initialXmitCredit;
+ uint16_t rdmaProtocolVersion;
+
+ NConnectionParams(const ConnectionParams& c) :
+ maxRecvBufferSize(c.rdmaProtocolVersion ? htonl(c.maxRecvBufferSize) : c.maxRecvBufferSize),
+ initialXmitCredit(c.rdmaProtocolVersion ? htons(c.initialXmitCredit) : c.initialXmitCredit),
+ // 0 is the same with/without byteswapping!
+ rdmaProtocolVersion(htons(c.rdmaProtocolVersion))
+ {}
+
+ operator ConnectionParams() const {
+ return
+ ConnectionParams(
+ rdmaProtocolVersion ? ntohl(maxRecvBufferSize) : maxRecvBufferSize,
+ rdmaProtocolVersion ? ntohs(initialXmitCredit) : initialXmitCredit,
+ ntohs(rdmaProtocolVersion));
+ }
+ };
+# pragma pack(pop)
+
+ class IOException : public std::exception {
+ std::string s;
+
+ public:
+ IOException(std::string s0): s(s0) {}
+ ~IOException() throw() {}
+
+ const char* what() const throw() {
+ return s.c_str();
+ }
+ };
+
+ AsynchIO::AsynchIO(
+ QueuePair::intrusive_ptr q,
+ int version,
+ int size,
+ int xCredit,
+ int rCount,
+ ReadCallback rc,
+ IdleCallback ic,
+ FullCallback fc,
+ ErrorCallback ec
+ ) :
+ protocolVersion(version),
+ bufferSize(size),
+ recvCredit(0),
+ xmitCredit(xCredit),
+ recvBufferCount(rCount),
+ xmitBufferCount(xCredit),
+ outstandingWrites(0),
+ draining(false),
+ state(IDLE),
+ qp(q),
+ dataHandle(*qp, boost::bind(&AsynchIO::dataEvent, this), 0, 0),
+ readCallback(rc),
+ idleCallback(ic),
+ fullCallback(fc),
+ errorCallback(ec),
+ pendingWriteAction(boost::bind(&AsynchIO::writeEvent, this))
+ {
+ if (protocolVersion > maxSupportedProtocolVersion)
+ throw IOException("Unsupported Rdma Protocol");
+ qp->nonblocking();
+ qp->notifyRecv();
+ qp->notifySend();
+
+ // Prepost recv buffers before we go any further
+ qp->allocateRecvBuffers(recvBufferCount, bufferSize+FrameHeaderSize);
+
+ // Create xmit buffers, reserve space for frame header.
+ qp->createSendBuffers(xmitBufferCount, bufferSize, FrameHeaderSize);
+ }
+
+ AsynchIO::~AsynchIO() {
+ // Warn if we are deleting whilst there are still unreclaimed write buffers
+ if ( outstandingWrites>0 )
+ QPID_LOG(error, "RDMA: qp=" << qp << ": Deleting queue before all write buffers finished");
+
+ // Turn off callbacks if necessary (before doing the deletes)
+ if (state != STOPPED) {
+ QPID_LOG(error, "RDMA: qp=" << qp << ": Deleting queue whilst not shutdown");
+ dataHandle.stopWatch();
+ }
+ // TODO: It might turn out to be more efficient in high connection loads to reuse the
+ // buffers rather than having to reregister them all the time (this would be straightforward if all
+ // connections haver the same buffer size and harder otherwise)
+ }
+
+ void AsynchIO::start(Poller::shared_ptr poller) {
+ dataHandle.startWatch(poller);
+ }
+
+ // State constraints
+ // On entry: None
+ // On exit: STOPPED
+ // Mark for deletion/Delete this object when we have no outstanding writes
+ void AsynchIO::stop(NotifyCallback nc) {
+ ScopedLock<Mutex> l(stateLock);
+ state = STOPPED;
+ notifyCallback = nc;
+ dataHandle.call(boost::bind(&AsynchIO::doStoppedCallback, this));
+ }
+
+ namespace {
+ void requestedCall(AsynchIO* aio, AsynchIO::RequestCallback callback) {
+ assert(callback);
+ callback(*aio);
+ }
+ }
+
+ void AsynchIO::requestCallback(RequestCallback callback) {
+ // TODO creating a function object every time isn't all that
+ // efficient - if this becomes heavily used do something better (what?)
+ assert(callback);
+ dataHandle.call(boost::bind(&requestedCall, this, callback));
+ }
+
+ // Mark writing closed (so we don't accept any more writes or make any idle callbacks)
+ void AsynchIO::drainWriteQueue(NotifyCallback nc) {
+ draining = true;
+ notifyCallback = nc;
+ }
+
+ void AsynchIO::queueBuffer(Buffer* buff, int credit) {
+ switch (protocolVersion) {
+ case 0:
+ if (!buff) {
+ Buffer* ob = getSendBuffer();
+ // Have to send something as adapters hate it when you try to transfer 0 bytes
+ *reinterpret_cast< uint32_t* >(ob->bytes()) = htonl(credit);
+ ob->dataCount(sizeof(uint32_t));
+ qp->postSend(credit | IgnoreData, ob);
+ } else if (credit > 0) {
+ qp->postSend(credit, buff);
+ } else {
+ qp->postSend(buff);
+ }
+ break;
+ case 1:
+ if (!buff)
+ buff = getSendBuffer();
+ // Add FrameHeader after frame data
+ FrameHeader header(credit);
+ assert(buff->dataCount() <= buff->byteCount()); // ensure app data doesn't impinge on reserved space.
+ ::memcpy(buff->bytes()+buff->dataCount(), &header, FrameHeaderSize);
+ buff->dataCount(buff->dataCount()+FrameHeaderSize);
+ qp->postSend(buff);
+ break;
+ }
+ }
+
+ Buffer* AsynchIO::extractBuffer(const QueuePairEvent& e) {
+ Buffer* b = e.getBuffer();
+ switch (protocolVersion) {
+ case 0: {
+ bool dataPresent = true;
+ // Get our xmitCredit if it was sent
+ if (e.immPresent() ) {
+ assert(xmitCredit>=0);
+ xmitCredit += (e.getImm() & ~FlagsMask);
+ dataPresent = ((e.getImm() & IgnoreData) == 0);
+ assert(xmitCredit>0);
+ }
+ if (!dataPresent) {
+ b->dataCount(0);
+ }
+ break;
+ }
+ case 1:
+ b->dataCount(b->dataCount()-FrameHeaderSize);
+ FrameHeader header;
+ ::memcpy(&header, b->bytes()+b->dataCount(), FrameHeaderSize);
+ assert(xmitCredit>=0);
+ xmitCredit += header.credit();
+ assert(xmitCredit>=0);
+ break;
+ }
+
+ return b;
+ }
+
+ void AsynchIO::queueWrite(Buffer* buff) {
+ // Make sure we don't overrun our available buffers
+ // either at our end or the known available at the peers end
+ if (writable()) {
+ // TODO: We might want to batch up sending credit
+ int creditSent = recvCredit & ~FlagsMask;
+ queueBuffer(buff, creditSent);
+ recvCredit -= creditSent;
+ ++outstandingWrites;
+ --xmitCredit;
+ assert(xmitCredit>=0);
+ } else {
+ if (fullCallback) {
+ fullCallback(*this, buff);
+ } else {
+ QPID_LOG(error, "RDMA: qp=" << qp << ": Write queue full, but no callback, throwing buffer away");
+ returnSendBuffer(buff);
+ }
+ }
+ }
+
+ // State constraints
+ // On entry: None
+ // On exit: NOTIFY_PENDING || STOPPED
+ void AsynchIO::notifyPendingWrite() {
+ ScopedLock<Mutex> l(stateLock);
+ switch (state) {
+ case IDLE:
+ dataHandle.call(pendingWriteAction);
+ // Fall Thru
+ case NOTIFY:
+ state = NOTIFY_PENDING;
+ break;
+ case NOTIFY_PENDING:
+ case STOPPED:
+ break;
+ }
+ }
+
+ // State constraints
+ // On entry: IDLE || STOPPED
+ // On exit: IDLE || STOPPED
+ void AsynchIO::dataEvent() {
+ {
+ ScopedLock<Mutex> l(stateLock);
+
+ if (state == STOPPED) return;
+
+ state = NOTIFY_PENDING;
+ }
+ processCompletions();
+
+ writeEvent();
+ }
+
+ // State constraints
+ // On entry: NOTIFY_PENDING || STOPPED
+ // On exit: IDLE || STOPPED
+ void AsynchIO::writeEvent() {
+ State newState;
+ do {
+ {
+ ScopedLock<Mutex> l(stateLock);
+
+ switch (state) {
+ case STOPPED:
+ return;
+ default:
+ state = NOTIFY;
+ }
+ }
+
+ doWriteCallback();
+
+ {
+ ScopedLock<Mutex> l(stateLock);
+
+ newState = state;
+ switch (newState) {
+ case NOTIFY_PENDING:
+ case STOPPED:
+ break;
+ default:
+ state = IDLE;
+ }
+ }
+ } while (newState == NOTIFY_PENDING);
+ }
+
+ void AsynchIO::processCompletions() {
+ QueuePair::intrusive_ptr q = qp->getNextChannelEvent();
+
+ // Re-enable notification for queue:
+ // This needs to happen before we could do anything that could generate more work completion
+ // events (ie the callbacks etc. in the following).
+ // This can't make us reenter this code as the handle attached to the completion queue will still be
+ // disabled by the poller until we leave this code
+ qp->notifyRecv();
+ qp->notifySend();
+
+ int recvEvents = 0;
+ int sendEvents = 0;
+
+ // If no event do nothing
+ if (!q)
+ return;
+
+ assert(q == qp);
+
+ // Repeat until no more events
+ do {
+ QueuePairEvent e(qp->getNextEvent());
+ if (!e)
+ break;
+
+ ::ibv_wc_status status = e.getEventStatus();
+ if (status != IBV_WC_SUCCESS) {
+ // Need special check for IBV_WC_WR_FLUSH_ERR here
+ // we will get this for every send/recv queue entry that was pending
+ // when disconnected, these aren't real errors and mostly need to be ignored
+ if (status == IBV_WC_WR_FLUSH_ERR) {
+ QueueDirection dir = e.getDirection();
+ if (dir == SEND) {
+ Buffer* b = e.getBuffer();
+ ++sendEvents;
+ returnSendBuffer(b);
+ --outstandingWrites;
+ } else {
+ ++recvEvents;
+ }
+ continue;
+ }
+ errorCallback(*this);
+ // TODO: Probably need to flush queues at this point
+ return;
+ }
+
+ // Test if recv (or recv with imm)
+ //::ibv_wc_opcode eventType = e.getEventType();
+ QueueDirection dir = e.getDirection();
+ if (dir == RECV) {
+ ++recvEvents;
+
+ Buffer* b = extractBuffer(e);
+
+ // if there was no data sent then the message was only to update our credit
+ if ( b->dataCount() > 0 ) {
+ readCallback(*this, b);
+ }
+
+ // At this point the buffer has been consumed so put it back on the recv queue
+ // TODO: Is this safe to do if the connection is disconnected already?
+ qp->postRecv(b);
+
+ // Received another message
+ ++recvCredit;
+
+ // Send recvCredit if it is large enough (it will have got this large because we've not sent anything recently)
+ if (recvCredit > recvBufferCount/2) {
+ // TODO: This should use RDMA write with imm as there might not ever be a buffer to receive this message
+ // but this is a little unlikely, as to get in this state we have to have received messages without sending any
+ // for a while so its likely we've received an credit update from the far side.
+ if (writable()) {
+ int creditSent = recvCredit & ~FlagsMask;
+ queueBuffer(0, creditSent);
+ recvCredit -= creditSent;
+ ++outstandingWrites;
+ --xmitCredit;
+ assert(xmitCredit>=0);
+ } else {
+ QPID_LOG(warning, "RDMA: qp=" << qp << ": Unable to send unsolicited credit");
+ }
+ }
+ } else {
+ Buffer* b = e.getBuffer();
+ ++sendEvents;
+ returnSendBuffer(b);
+ --outstandingWrites;
+ }
+ } while (true);
+
+ // Not sure if this is expected or not
+ if (recvEvents == 0 && sendEvents == 0) {
+ QPID_LOG(debug, "RDMA: qp=" << qp << ": Got channel event with no recv/send completions");
+ }
+ }
+
+ void AsynchIO::doWriteCallback() {
+ // TODO: maybe don't call idle unless we're low on write buffers
+ // Keep on calling the idle routine as long as we are writable and we got something to write last call
+
+ // Do callback even if there are no available free buffers as the application itself might be
+ // holding onto buffers
+ while (writable()) {
+ int xc = xmitCredit;
+ idleCallback(*this);
+ // Check whether we actually wrote anything
+ if (xmitCredit == xc) {
+ QPID_LOG(debug, "RDMA: qp=" << qp << ": Called for data, but got none: xmitCredit=" << xmitCredit);
+ return;
+ }
+ }
+
+ checkDrained();
+ }
+
+ void AsynchIO::checkDrained() {
+ // If we've got all the write confirmations and we're draining
+ // We might get deleted in the drained callback so return immediately
+ if (draining) {
+ if (outstandingWrites == 0) {
+ draining = false;
+ NotifyCallback nc;
+ nc.swap(notifyCallback);
+ nc(*this);
+ }
+ return;
+ }
+ }
+
+ void AsynchIO::doStoppedCallback() {
+ // Ensure we can't get any more callbacks (except for the stopped callback)
+ dataHandle.stopWatch();
+
+ NotifyCallback nc;
+ nc.swap(notifyCallback);
+ nc(*this);
+ }
+
+ ConnectionManager::ConnectionManager(
+ ErrorCallback errc,
+ DisconnectedCallback dc
+ ) :
+ state(IDLE),
+ ci(Connection::make()),
+ handle(*ci, boost::bind(&ConnectionManager::event, this, _1), 0, 0),
+ errorCallback(errc),
+ disconnectedCallback(dc)
+ {
+ QPID_LOG(debug, "RDMA: ci=" << ci << ": Creating ConnectionManager");
+ ci->nonblocking();
+ }
+
+ ConnectionManager::~ConnectionManager()
+ {
+ QPID_LOG(debug, "RDMA: ci=" << ci << ": Deleting ConnectionManager");
+ }
+
+ void ConnectionManager::start(Poller::shared_ptr poller, const qpid::sys::SocketAddress& addr) {
+ startConnection(ci, addr);
+ handle.startWatch(poller);
+ }
+
+ void ConnectionManager::doStoppedCallback() {
+ // Ensure we can't get any more callbacks (except for the stopped callback)
+ handle.stopWatch();
+
+ NotifyCallback nc;
+ nc.swap(notifyCallback);
+ nc(*this);
+ }
+
+ void ConnectionManager::stop(NotifyCallback nc) {
+ state = STOPPED;
+ notifyCallback = nc;
+ handle.call(boost::bind(&ConnectionManager::doStoppedCallback, this));
+ }
+
+ void ConnectionManager::event(DispatchHandle&) {
+ if (state.get() == STOPPED) return;
+ connectionEvent(ci);
+ }
+
+ Listener::Listener(
+ const ConnectionParams& cp,
+ EstablishedCallback ec,
+ ErrorCallback errc,
+ DisconnectedCallback dc,
+ ConnectionRequestCallback crc
+ ) :
+ ConnectionManager(errc, dc),
+ checkConnectionParams(cp),
+ connectionRequestCallback(crc),
+ establishedCallback(ec)
+ {
+ }
+
+ void Listener::startConnection(Connection::intrusive_ptr ci, const qpid::sys::SocketAddress& addr) {
+ ci->bind(addr);
+ ci->listen();
+ }
+
+ namespace {
+ const int64_t PoisonContext = -1;
+ }
+
+ void Listener::connectionEvent(Connection::intrusive_ptr ci) {
+ ConnectionEvent e(ci->getNextEvent());
+
+ // If (for whatever reason) there was no event do nothing
+ if (!e)
+ return;
+
+ // Important documentation ommision the new rdma_cm_id
+ // you get from CONNECT_REQUEST has the same context info
+ // as its parent listening rdma_cm_id
+ ::rdma_cm_event_type eventType = e.getEventType();
+ ::rdma_conn_param conn_param = e.getConnectionParam();
+ Rdma::Connection::intrusive_ptr id = e.getConnection();
+
+ // Check for previous disconnection (it appears that you actually can get connection
+ // request events after a disconnect event in rare circumstances)
+ if (reinterpret_cast<int64_t>(id->getContext<void*>())==PoisonContext)
+ return;
+
+ switch (eventType) {
+ case RDMA_CM_EVENT_CONNECT_REQUEST: {
+ // Make sure peer has sent params we can use
+ if (!conn_param.private_data || conn_param.private_data_len < sizeof(NConnectionParams)) {
+ QPID_LOG(warning, "Rdma: rejecting connection attempt: unusable connection parameters");
+ id->reject();
+ break;
+ }
+
+ const NConnectionParams* rcp = static_cast<const NConnectionParams*>(conn_param.private_data);
+ ConnectionParams cp = *rcp;
+
+ // Reject if requested msg size is bigger than we allow
+ if (
+ cp.maxRecvBufferSize > checkConnectionParams.maxRecvBufferSize ||
+ cp.initialXmitCredit > checkConnectionParams.initialXmitCredit
+ ) {
+ QPID_LOG(warning, "Rdma: rejecting connection attempt: connection parameters out of range: ("
+ << cp.maxRecvBufferSize << ">" << checkConnectionParams.maxRecvBufferSize << " || "
+ << cp.initialXmitCredit << ">" << checkConnectionParams.initialXmitCredit
+ << ")");
+ id->reject(&checkConnectionParams);
+ break;
+ }
+
+ bool accept = true;
+ if (connectionRequestCallback)
+ accept = connectionRequestCallback(id, cp);
+
+ if (accept) {
+ // Accept connection
+ cp.initialXmitCredit = checkConnectionParams.initialXmitCredit;
+ id->accept(conn_param, rcp);
+ } else {
+ // Reject connection
+ QPID_LOG(warning, "Rdma: rejecting connection attempt: application policy");
+ id->reject();
+ }
+ break;
+ }
+ case RDMA_CM_EVENT_ESTABLISHED:
+ establishedCallback(id);
+ break;
+ case RDMA_CM_EVENT_DISCONNECTED:
+ disconnectedCallback(id);
+ // Poison the id context so that we do no more callbacks on it
+ id->removeContext();
+ id->addContext(reinterpret_cast<void*>(PoisonContext));
+ break;
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ errorCallback(id, CONNECT_ERROR);
+ break;
+ default:
+ // Unexpected response
+ errorCallback(id, UNKNOWN);
+ //std::cerr << "Warning: unexpected response to listen - " << eventType << "\n";
+ }
+ }
+
+ Connector::Connector(
+ const ConnectionParams& cp,
+ ConnectedCallback cc,
+ ErrorCallback errc,
+ DisconnectedCallback dc,
+ RejectedCallback rc
+ ) :
+ ConnectionManager(errc, dc),
+ connectionParams(cp),
+ rejectedCallback(rc),
+ connectedCallback(cc)
+ {
+ }
+
+ void Connector::startConnection(Connection::intrusive_ptr ci, const qpid::sys::SocketAddress& addr) {
+ ci->resolve_addr(addr);
+ }
+
+ void Connector::connectionEvent(Connection::intrusive_ptr ci) {
+ ConnectionEvent e(ci->getNextEvent());
+
+ // If (for whatever reason) there was no event do nothing
+ if (!e)
+ return;
+
+ ::rdma_cm_event_type eventType = e.getEventType();
+ ::rdma_conn_param conn_param = e.getConnectionParam();
+ Rdma::Connection::intrusive_ptr id = e.getConnection();
+ switch (eventType) {
+ case RDMA_CM_EVENT_ADDR_RESOLVED:
+ // RESOLVE_ADDR
+ ci->resolve_route();
+ break;
+ case RDMA_CM_EVENT_ADDR_ERROR:
+ // RESOLVE_ADDR
+ errorCallback(ci, ADDR_ERROR);
+ break;
+ case RDMA_CM_EVENT_ROUTE_RESOLVED: {
+ // RESOLVE_ROUTE:
+ NConnectionParams rcp(connectionParams);
+ ci->connect(&rcp);
+ break;
+ }
+ case RDMA_CM_EVENT_ROUTE_ERROR:
+ // RESOLVE_ROUTE:
+ errorCallback(ci, ROUTE_ERROR);
+ break;
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ // CONNECTING
+ errorCallback(ci, CONNECT_ERROR);
+ break;
+ case RDMA_CM_EVENT_UNREACHABLE:
+ // CONNECTING
+ errorCallback(ci, UNREACHABLE);
+ break;
+ case RDMA_CM_EVENT_REJECTED: {
+ // CONNECTING
+
+ // We can get this event if our peer is not running on the other side
+ // in this case we could get nearly anything in the private data:
+ // From private_data == 0 && private_data_len == 0 (Chelsio iWarp)
+ // to 148 bytes of zeros (Mellanox IB)
+ //
+ // So assume that if the the private data is absent or not the size of
+ // the connection parameters it isn't valid
+ ConnectionParams cp(0, 0, 0);
+ if (conn_param.private_data && conn_param.private_data_len == sizeof(NConnectionParams)) {
+ // Extract private data from event
+ const NConnectionParams* rcp = static_cast<const NConnectionParams*>(conn_param.private_data);
+ cp = *rcp;
+ }
+ rejectedCallback(ci, cp);
+ break;
+ }
+ case RDMA_CM_EVENT_ESTABLISHED: {
+ // CONNECTING
+ // Extract private data from event
+ assert(conn_param.private_data && conn_param.private_data_len >= sizeof(NConnectionParams));
+ const NConnectionParams* rcp = static_cast<const NConnectionParams*>(conn_param.private_data);
+ ConnectionParams cp = *rcp;
+ connectedCallback(ci, cp);
+ break;
+ }
+ case RDMA_CM_EVENT_DISCONNECTED:
+ // ESTABLISHED
+ disconnectedCallback(ci);
+ break;
+ default:
+ QPID_LOG(warning, "RDMA: Unexpected event in connect: " << eventType);
+ }
+ }
+}
diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h
new file mode 100644
index 0000000000..ec9caaf08d
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/RdmaIO.h
@@ -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.
+ *
+ */
+#ifndef Rdma_Acceptor_h
+#define Rdma_Acceptor_h
+
+#include "qpid/sys/rdma/rdma_wrap.h"
+
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/SocketAddress.h"
+
+#include <netinet/in.h>
+
+#include <boost/function.hpp>
+
+namespace Rdma {
+
+ class Connection;
+
+ class AsynchIO
+ {
+ typedef boost::function1<void, AsynchIO&> ErrorCallback;
+ typedef boost::function2<void, AsynchIO&, Buffer*> ReadCallback;
+ typedef boost::function1<void, AsynchIO&> IdleCallback;
+ typedef boost::function2<void, AsynchIO&, Buffer*> FullCallback;
+ typedef boost::function1<void, AsynchIO&> NotifyCallback;
+
+ int protocolVersion;
+ int bufferSize;
+ int recvCredit;
+ int xmitCredit;
+ int recvBufferCount;
+ int xmitBufferCount;
+ int outstandingWrites;
+ bool draining;
+ enum State {IDLE, NOTIFY, NOTIFY_PENDING, STOPPED};
+ State state;
+ qpid::sys::Mutex stateLock;
+ QueuePair::intrusive_ptr qp;
+ qpid::sys::DispatchHandleRef dataHandle;
+
+ ReadCallback readCallback;
+ IdleCallback idleCallback;
+ FullCallback fullCallback;
+ ErrorCallback errorCallback;
+ NotifyCallback notifyCallback;
+ qpid::sys::DispatchHandle::Callback pendingWriteAction;
+
+ public:
+ typedef boost::function1<void, AsynchIO&> RequestCallback;
+
+ // TODO: Instead of specifying a buffer size specify the amount of memory the AsynchIO class can use
+ // for buffers both read and write (allocate half to each up front) and fail if we cannot allocate that much
+ // locked memory
+ AsynchIO(
+ QueuePair::intrusive_ptr q,
+ int version,
+ int size,
+ int xCredit,
+ int rCount,
+ ReadCallback rc,
+ IdleCallback ic,
+ FullCallback fc,
+ ErrorCallback ec
+ );
+ ~AsynchIO();
+
+ void start(qpid::sys::Poller::shared_ptr poller);
+ bool writable() const;
+ void queueWrite(Buffer* buff);
+ void notifyPendingWrite();
+ void drainWriteQueue(NotifyCallback);
+ void stop(NotifyCallback);
+ void requestCallback(RequestCallback);
+ int incompletedWrites() const;
+ Buffer* getSendBuffer();
+ void returnSendBuffer(Buffer*);
+
+ private:
+ const static int maxSupportedProtocolVersion = 1;
+
+ // Constants for the peer-peer command messages
+ // These are sent in the high bits if the imm data of an rdma message
+ // The low bits are used to send the credit
+ const static int FlagsMask = 0xF0000000; // Mask for all flag bits - be sure to update this if you add more command bits
+ const static int IgnoreData = 0x10000000; // Message contains no application data
+
+ void dataEvent();
+ void writeEvent();
+ void processCompletions();
+ void doWriteCallback();
+ void checkDrained();
+ void doStoppedCallback();
+
+ void queueBuffer(Buffer* buff, int credit);
+ Buffer* extractBuffer(const QueuePairEvent& e);
+ };
+
+ // We're only writable if:
+ // * not draining write queue
+ // * we've got space in the transmit queue
+ // * we've got credit to transmit
+ // * if there's only 1 transmit credit we must send some credit
+ inline bool AsynchIO::writable() const {
+ assert(xmitCredit>=0);
+ return !draining &&
+ outstandingWrites < xmitBufferCount &&
+ xmitCredit > 0 &&
+ ( xmitCredit > 1 || recvCredit > 0);
+ }
+
+ inline int AsynchIO::incompletedWrites() const {
+ return outstandingWrites;
+ }
+
+ inline Buffer* AsynchIO::getSendBuffer() {
+ return qp->getSendBuffer();
+ }
+
+ inline void AsynchIO::returnSendBuffer(Buffer* b) {
+ qp->returnSendBuffer(b);
+ }
+
+ // These are the parameters necessary to start the conversation
+ // * Each peer HAS to allocate buffers of the size of the maximum receive from its peer
+ // * Each peer HAS to know the initial "credit" it has for transmitting to its peer
+ struct ConnectionParams {
+ uint32_t maxRecvBufferSize;
+ uint16_t initialXmitCredit;
+ uint16_t rdmaProtocolVersion;
+
+ // Default to protocol version 1
+ ConnectionParams(uint32_t s, uint16_t c, uint16_t v = 1) :
+ maxRecvBufferSize(s),
+ initialXmitCredit(c),
+ rdmaProtocolVersion(v)
+ {}
+ };
+
+ enum ErrorType {
+ ADDR_ERROR,
+ ROUTE_ERROR,
+ CONNECT_ERROR,
+ UNREACHABLE,
+ UNKNOWN
+ };
+
+ typedef boost::function2<void, Rdma::Connection::intrusive_ptr, ErrorType> ErrorCallback;
+ typedef boost::function1<void, Rdma::Connection::intrusive_ptr> DisconnectedCallback;
+
+ class ConnectionManager {
+ typedef boost::function1<void, ConnectionManager&> NotifyCallback;
+
+ enum State {IDLE, STOPPED};
+ qpid::sys::AtomicValue<State> state;
+ Connection::intrusive_ptr ci;
+ qpid::sys::DispatchHandleRef handle;
+ NotifyCallback notifyCallback;
+
+ protected:
+ ErrorCallback errorCallback;
+ DisconnectedCallback disconnectedCallback;
+
+ public:
+ ConnectionManager(
+ ErrorCallback errc,
+ DisconnectedCallback dc
+ );
+
+ virtual ~ConnectionManager();
+
+ void start(qpid::sys::Poller::shared_ptr poller, const qpid::sys::SocketAddress& addr);
+ void stop(NotifyCallback);
+
+ private:
+ void event(qpid::sys::DispatchHandle& handle);
+ void doStoppedCallback();
+
+ virtual void startConnection(Connection::intrusive_ptr ci, const qpid::sys::SocketAddress& addr) = 0;
+ virtual void connectionEvent(Connection::intrusive_ptr ci) = 0;
+ };
+
+ typedef boost::function2<bool, Rdma::Connection::intrusive_ptr, const ConnectionParams&> ConnectionRequestCallback;
+ typedef boost::function1<void, Rdma::Connection::intrusive_ptr> EstablishedCallback;
+
+ class Listener : public ConnectionManager
+ {
+ ConnectionParams checkConnectionParams;
+ ConnectionRequestCallback connectionRequestCallback;
+ EstablishedCallback establishedCallback;
+
+ public:
+ Listener(
+ const ConnectionParams& cp,
+ EstablishedCallback ec,
+ ErrorCallback errc,
+ DisconnectedCallback dc,
+ ConnectionRequestCallback crc = 0
+ );
+
+ private:
+ void startConnection(Connection::intrusive_ptr ci, const qpid::sys::SocketAddress& addr);
+ void connectionEvent(Connection::intrusive_ptr ci);
+ };
+
+ typedef boost::function2<void, Rdma::Connection::intrusive_ptr, const ConnectionParams&> RejectedCallback;
+ typedef boost::function2<void, Rdma::Connection::intrusive_ptr, const ConnectionParams&> ConnectedCallback;
+
+ class Connector : public ConnectionManager
+ {
+ ConnectionParams connectionParams;
+ RejectedCallback rejectedCallback;
+ ConnectedCallback connectedCallback;
+
+ public:
+ Connector(
+ const ConnectionParams& cp,
+ ConnectedCallback cc,
+ ErrorCallback errc,
+ DisconnectedCallback dc,
+ RejectedCallback rc = 0
+ );
+
+ private:
+ void startConnection(Connection::intrusive_ptr ci, const qpid::sys::SocketAddress& addr);
+ void connectionEvent(Connection::intrusive_ptr ci);
+ };
+}
+
+#endif // Rdma_Acceptor_h
diff --git a/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp
new file mode 100644
index 0000000000..9b0710fd8f
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/RdmaServer.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/rdma/RdmaIO.h"
+#include "qpid/sys/rdma/rdma_exception.h"
+
+#include <arpa/inet.h>
+
+#include <vector>
+#include <queue>
+#include <string>
+#include <iostream>
+
+#include <boost/bind.hpp>
+
+using std::vector;
+using std::queue;
+using std::string;
+using std::cout;
+using std::cerr;
+
+using qpid::sys::Thread;
+using qpid::sys::SocketAddress;
+using qpid::sys::Poller;
+
+// All the accepted connections
+namespace qpid {
+namespace tests {
+
+struct Buffer {
+ char* bytes() const {return bytes_;}
+ int32_t byteCount() const {return size;}
+
+ Buffer(const int32_t s):
+ bytes_(new char[s]),
+ size(s)
+ {
+ }
+
+ ~Buffer() {
+ delete [] bytes_;
+ }
+private:
+ char* bytes_;
+ int32_t size;
+};
+
+struct ConRec {
+ Rdma::Connection::intrusive_ptr connection;
+ Rdma::AsynchIO* data;
+ queue<Buffer*> queuedWrites;
+
+ ConRec(Rdma::Connection::intrusive_ptr c) :
+ connection(c)
+ {}
+};
+
+void dataError(Rdma::AsynchIO&) {
+ cout << "Data error:\n";
+}
+
+void idle(ConRec* cr, Rdma::AsynchIO& a) {
+ // Need to make sure full is not called as it would reorder messages
+ while (!cr->queuedWrites.empty() && a.writable()) {
+ Rdma::Buffer* rbuf = a.getSendBuffer();
+ if (!rbuf) break;
+ Buffer* buf = cr->queuedWrites.front();
+ cr->queuedWrites.pop();
+ std::copy(buf->bytes(), buf->bytes()+buf->byteCount(), rbuf->bytes());
+ rbuf->dataCount(buf->byteCount());
+ delete buf;
+ a.queueWrite(rbuf);
+ }
+}
+
+void data(ConRec* cr, Rdma::AsynchIO& a, Rdma::Buffer* b) {
+ // Echo data back
+ Rdma::Buffer* buf = 0;
+ if (cr->queuedWrites.empty() && a.writable()) {
+ buf = a.getSendBuffer();
+ }
+ if (buf) {
+ std::copy(b->bytes(), b->bytes()+b->dataCount(), buf->bytes());
+ buf->dataCount(b->dataCount());
+ a.queueWrite(buf);
+ } else {
+ Buffer* buf = new Buffer(b->dataCount());
+ std::copy(b->bytes(), b->bytes()+b->dataCount(), buf->bytes());
+ cr->queuedWrites.push(buf);
+ // Try to empty queue
+ idle(cr, a);
+ }
+}
+
+void full(ConRec*, Rdma::AsynchIO&, Rdma::Buffer*) {
+ // Shouldn't ever be called
+ cout << "!";
+}
+
+void drained(Rdma::AsynchIO&) {
+ cout << "Drained:\n";
+}
+
+void disconnected(Rdma::Connection::intrusive_ptr& ci) {
+ ConRec* cr = ci->getContext<ConRec>();
+ cr->connection->disconnect();
+ cr->data->drainWriteQueue(drained);
+ delete cr;
+ cout << "Disconnected: " << cr << "\n";
+}
+
+void connectionError(Rdma::Connection::intrusive_ptr& ci, Rdma::ErrorType) {
+ ConRec* cr = ci->getContext<ConRec>();
+ cr->connection->disconnect();
+ if (cr) {
+ cr->data->drainWriteQueue(drained);
+ delete cr;
+ }
+ cout << "Connection error: " << cr << "\n";
+}
+
+bool connectionRequest(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) {
+ cout << "Incoming connection: ";
+
+ // For fun reject alternate connection attempts
+ static bool x = false;
+ x = true;
+
+ // Must create aio here so as to prepost buffers *before* we accept connection
+ if (x) {
+ ConRec* cr = new ConRec(ci);
+ Rdma::AsynchIO* aio =
+ new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.rdmaProtocolVersion,
+ cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES,
+ boost::bind(data, cr, _1, _2),
+ boost::bind(idle, cr, _1),
+ boost::bind(full, cr, _1, _2),
+ dataError);
+ ci->addContext(cr);
+ cr->data = aio;
+ cout << "Accept=>" << cr << "\n";
+ } else {
+ cout << "Reject\n";
+ }
+
+ return x;
+}
+
+void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) {
+ static int cnt = 0;
+ ConRec* cr = ci->getContext<ConRec>();
+ cout << "Connected: " << cr << "(" << ++cnt << ")\n";
+
+ cr->data->start(poller);
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char* argv[]) {
+ vector<string> args(&argv[0], &argv[argc]);
+
+ std::string port = (args.size() < 2) ? "20079" : args[1];
+ cout << "Listening on port: " << port << "\n";
+
+ try {
+ boost::shared_ptr<Poller> p(new Poller());
+
+ Rdma::Listener a(
+ Rdma::ConnectionParams(16384, Rdma::DEFAULT_WR_ENTRIES),
+ boost::bind(connected, p, _1),
+ connectionError,
+ disconnected,
+ connectionRequest);
+
+
+ SocketAddress sa("", port);
+ a.start(p, sa);
+
+ // The poller loop blocks all signals so run in its own thread
+ Thread t(*p);
+
+ ::pause();
+ p->shutdown();
+ t.join();
+ } catch (Rdma::Exception& e) {
+ int err = e.getError();
+ cerr << "Error: " << e.what() << "(" << err << ")\n";
+ }
+}
diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h b/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h
new file mode 100644
index 0000000000..a3a289e38a
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/rdma_exception.h
@@ -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.
+ *
+ */
+#ifndef RDMA_EXCEPTION_H
+#define RDMA_EXCEPTION_H
+
+#include <exception>
+
+#include <errno.h>
+#include <string.h>
+
+namespace Rdma {
+ static __thread char s[50];
+ class Exception : public std::exception {
+ int err;
+
+ public:
+ Exception(int e) : err(e) {}
+ int getError() { return err; }
+ const char* what() const throw() {
+ return ::strerror_r(err, s, 50);
+ }
+ };
+
+ inline void THROW_ERRNO() {
+ throw Rdma::Exception(errno);
+ }
+
+ inline void CHECK(int rc) {
+ if (rc != 0)
+ throw Rdma::Exception((rc == -1) ? errno : rc >0 ? rc : -rc);
+ }
+
+ inline int GETERR(int rc) {
+ return (rc == -1) ? errno : rc > 0 ? rc : -rc;
+ }
+
+ inline void CHECK_IBV(int rc) {
+ if (rc != 0)
+ throw Rdma::Exception(rc);
+ }
+
+ template <typename T>
+ inline
+ T* CHECK_NULL(T* rc) {
+ if (rc == 0)
+ THROW_ERRNO();
+ return rc;
+ }
+}
+
+#endif // RDMA_EXCEPTION_H
diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp b/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp
new file mode 100644
index 0000000000..a66f5b4035
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/rdma_factories.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/rdma/rdma_factories.h"
+
+#include "qpid/sys/rdma/rdma_exception.h"
+
+
+namespace Rdma {
+ // Intentionally ignore return values for these functions
+ // - we can't do anything about then anyway
+ void acker(::rdma_cm_event* e) throw () {
+ if (e) (void) ::rdma_ack_cm_event(e);
+ }
+
+ void destroyEChannel(::rdma_event_channel* c) throw () {
+ if (c) (void) ::rdma_destroy_event_channel(c);
+ }
+
+ void destroyId(::rdma_cm_id* i) throw () {
+ if (i) (void) ::rdma_destroy_id(i);
+ }
+
+ void deallocPd(::ibv_pd* p) throw () {
+ if (p) (void) ::ibv_dealloc_pd(p);
+ }
+
+ void deregMr(::ibv_mr* mr) throw () {
+ if (mr) (void) ::ibv_dereg_mr(mr);
+ }
+
+ void destroyCChannel(::ibv_comp_channel* c) throw () {
+ if (c) (void) ::ibv_destroy_comp_channel(c);
+ }
+
+ void destroyCq(::ibv_cq* cq) throw () {
+ if (cq) (void) ::ibv_destroy_cq(cq);
+ }
+
+ void destroyQp(::ibv_qp* qp) throw () {
+ if (qp) (void) ::ibv_destroy_qp(qp);
+ }
+
+ boost::shared_ptr< ::rdma_cm_id > mkId(::rdma_cm_id* i) {
+ return boost::shared_ptr< ::rdma_cm_id >(i, destroyId);
+ }
+
+ boost::shared_ptr< ::rdma_cm_event > mkEvent(::rdma_cm_event* e) {
+ return boost::shared_ptr< ::rdma_cm_event >(e, acker);
+ }
+
+ boost::shared_ptr< ::ibv_qp > mkQp(::ibv_qp* qp) {
+ return boost::shared_ptr< ::ibv_qp > (qp, destroyQp);
+ }
+
+ boost::shared_ptr< ::rdma_event_channel > mkEChannel() {
+ ::rdma_event_channel* c = CHECK_NULL(::rdma_create_event_channel());
+ return boost::shared_ptr< ::rdma_event_channel >(c, destroyEChannel);
+ }
+
+ boost::shared_ptr< ::rdma_cm_id >
+ mkId(::rdma_event_channel* ec, void* context, ::rdma_port_space ps) {
+ ::rdma_cm_id* i;
+ CHECK(::rdma_create_id(ec, &i, context, ps));
+ return mkId(i);
+ }
+
+ boost::shared_ptr< ::ibv_pd > allocPd(::ibv_context* c) {
+ ::ibv_pd* pd = CHECK_NULL(::ibv_alloc_pd(c));
+ return boost::shared_ptr< ::ibv_pd >(pd, deallocPd);
+ }
+
+ boost::shared_ptr< ::ibv_mr > regMr(::ibv_pd* pd, void* addr, size_t length, ::ibv_access_flags access) {
+ ::ibv_mr* mr = CHECK_NULL(::ibv_reg_mr(pd, addr, length, access));
+ return boost::shared_ptr< ::ibv_mr >(mr, deregMr);
+ }
+
+ boost::shared_ptr< ::ibv_comp_channel > mkCChannel(::ibv_context* c) {
+ ::ibv_comp_channel* cc = CHECK_NULL(::ibv_create_comp_channel(c));
+ return boost::shared_ptr< ::ibv_comp_channel >(cc, destroyCChannel);
+ }
+
+ boost::shared_ptr< ::ibv_cq >
+ mkCq(::ibv_context* c, int cqe, void* context, ::ibv_comp_channel* cc) {
+ ::ibv_cq* cq = CHECK_NULL(::ibv_create_cq(c, cqe, context, cc, 0));
+ return boost::shared_ptr< ::ibv_cq >(cq, destroyCq);
+ }
+}
diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h b/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h
new file mode 100644
index 0000000000..bfca71fc7e
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/rdma_factories.h
@@ -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.
+ *
+ */
+#ifndef RDMA_FACTORIES_H
+#define RDMA_FACTORIES_H
+
+#include <rdma/rdma_cma.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace Rdma {
+ boost::shared_ptr< ::rdma_event_channel > mkEChannel();
+ boost::shared_ptr< ::rdma_cm_id > mkId(::rdma_event_channel* ec, void* context, ::rdma_port_space ps);
+ boost::shared_ptr< ::rdma_cm_id > mkId(::rdma_cm_id* i);
+ boost::shared_ptr< ::rdma_cm_event > mkEvent(::rdma_cm_event* e);
+ boost::shared_ptr< ::ibv_qp > mkQp(::ibv_qp* qp);
+ boost::shared_ptr< ::ibv_pd > allocPd(::ibv_context* c);
+ boost::shared_ptr< ::ibv_mr > regMr(::ibv_pd* pd, void* addr, size_t length, ::ibv_access_flags access);
+ boost::shared_ptr< ::ibv_comp_channel > mkCChannel(::ibv_context* c);
+ boost::shared_ptr< ::ibv_cq > mkCq(::ibv_context* c, int cqe, void* context, ::ibv_comp_channel* cc);
+}
+
+#endif // RDMA_FACTORIES_H
diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
new file mode 100644
index 0000000000..efe454c5be
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
@@ -0,0 +1,566 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/rdma/rdma_wrap.h"
+
+#include "qpid/sys/rdma/rdma_factories.h"
+#include "qpid/sys/rdma/rdma_exception.h"
+
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+
+#include <iostream>
+#include <stdexcept>
+
+namespace Rdma {
+ const ::rdma_conn_param DEFAULT_CONNECT_PARAM = {
+ 0, // .private_data
+ 0, // .private_data_len
+ 4, // .responder_resources
+ 4, // .initiator_depth
+ 0, // .flow_control
+ 5, // .retry_count
+ 7 // .rnr_retry_count
+ };
+
+ // This is moderately inefficient so don't use in a critical path
+ int deviceCount() {
+ int count;
+ ::ibv_free_device_list(::ibv_get_device_list(&count));
+ return count;
+ }
+
+ Buffer::Buffer(uint32_t lkey, char* bytes, const int32_t byteCount,
+ const int32_t reserve) :
+ bufferSize(byteCount + reserve), reserved(reserve)
+ {
+ sge.addr = (uintptr_t) bytes;
+ sge.length = 0;
+ sge.lkey = lkey;
+ }
+
+ QueuePairEvent::QueuePairEvent() :
+ dir(NONE)
+ {}
+
+ QueuePairEvent::QueuePairEvent(
+ const ::ibv_wc& w,
+ boost::shared_ptr< ::ibv_cq > c,
+ QueueDirection d) :
+ cq(c),
+ wc(w),
+ dir(d)
+ {
+ assert(dir != NONE);
+ }
+
+ QueuePairEvent::operator bool() const {
+ return dir != NONE;
+ }
+
+ bool QueuePairEvent::immPresent() const {
+ return wc.wc_flags & IBV_WC_WITH_IMM;
+ }
+
+ uint32_t QueuePairEvent::getImm() const {
+ return ntohl(wc.imm_data);
+ }
+
+ QueueDirection QueuePairEvent::getDirection() const {
+ return dir;
+ }
+
+ ::ibv_wc_opcode QueuePairEvent::getEventType() const {
+ return wc.opcode;
+ }
+
+ ::ibv_wc_status QueuePairEvent::getEventStatus() const {
+ return wc.status;
+ }
+
+ Buffer* QueuePairEvent::getBuffer() const {
+ Buffer* b = reinterpret_cast<Buffer*>(wc.wr_id);
+ b->dataCount(wc.byte_len);
+ return b;
+ }
+
+ QueuePair::QueuePair(boost::shared_ptr< ::rdma_cm_id > i) :
+ qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ pd(allocPd(i->verbs)),
+ cchannel(mkCChannel(i->verbs)),
+ scq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())),
+ rcq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())),
+ outstandingSendEvents(0),
+ outstandingRecvEvents(0)
+ {
+ impl->fd = cchannel->fd;
+
+ // Set cq context to this QueuePair object so we can find
+ // ourselves again
+ scq->cq_context = this;
+ rcq->cq_context = this;
+
+ ::ibv_device_attr dev_attr;
+ CHECK(::ibv_query_device(i->verbs, &dev_attr));
+
+ ::ibv_qp_init_attr qp_attr = {};
+
+ // TODO: make a default struct for this
+ qp_attr.cap.max_send_wr = DEFAULT_WR_ENTRIES;
+ qp_attr.cap.max_send_sge = 1;
+ qp_attr.cap.max_recv_wr = DEFAULT_WR_ENTRIES;
+ qp_attr.cap.max_recv_sge = 1;
+
+ qp_attr.send_cq = scq.get();
+ qp_attr.recv_cq = rcq.get();
+ qp_attr.qp_type = IBV_QPT_RC;
+
+ CHECK(::rdma_create_qp(i.get(), pd.get(), &qp_attr));
+ qp = mkQp(i->qp);
+
+ // Set the qp context to this so we can find ourselves again
+ qp->qp_context = this;
+ }
+
+ QueuePair::~QueuePair() {
+ // Reset back pointer in case someone else has the qp
+ qp->qp_context = 0;
+
+ // Dispose queue pair before we ack events
+ qp.reset();
+
+ if (outstandingSendEvents > 0)
+ ::ibv_ack_cq_events(scq.get(), outstandingSendEvents);
+ if (outstandingRecvEvents > 0)
+ ::ibv_ack_cq_events(rcq.get(), outstandingRecvEvents);
+
+ // Deallocate recv buffer memory
+ if (rmr) delete [] static_cast<char*>(rmr->addr);
+
+ // Deallocate recv buffer memory
+ if (smr) delete [] static_cast<char*>(smr->addr);
+
+ // The buffers vectors automatically deletes all the buffers we've allocated
+ }
+
+ // Create buffers to use for writing
+ void QueuePair::createSendBuffers(int sendBufferCount, int bufferSize, int reserved)
+ {
+ assert(!smr);
+
+ // Round up buffersize to cacheline (64 bytes)
+ int dataLength = (bufferSize+reserved+63) & (~63);
+
+ // Allocate memory block for all receive buffers
+ char* mem = new char [sendBufferCount * dataLength];
+ smr = regMr(pd.get(), mem, sendBufferCount * dataLength, ::IBV_ACCESS_LOCAL_WRITE);
+ sendBuffers.reserve(sendBufferCount);
+ freeBuffers.reserve(sendBufferCount);
+ for (int i = 0; i<sendBufferCount; ++i) {
+ // Allocate xmit buffer
+ sendBuffers.push_back(Buffer(smr->lkey, &mem[i*dataLength], bufferSize, reserved));
+ freeBuffers.push_back(i);
+ }
+ }
+
+ Buffer* QueuePair::getSendBuffer() {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferLock);
+ if (freeBuffers.empty())
+ return 0;
+ int i = freeBuffers.back();
+ freeBuffers.pop_back();
+ assert(i >= 0 && i < int(sendBuffers.size()));
+ Buffer* b = &sendBuffers[i];
+ b->dataCount(0);
+ return b;
+ }
+
+ void QueuePair::returnSendBuffer(Buffer* b) {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferLock);
+ int i = b - &sendBuffers[0];
+ assert(i >= 0 && i < int(sendBuffers.size()));
+ freeBuffers.push_back(i);
+ }
+
+ void QueuePair::allocateRecvBuffers(int recvBufferCount, int bufferSize)
+ {
+ assert(!rmr);
+
+ // Round up buffersize to cacheline (64 bytes)
+ bufferSize = (bufferSize+63) & (~63);
+
+ // Allocate memory block for all receive buffers
+ char* mem = new char [recvBufferCount * bufferSize];
+ rmr = regMr(pd.get(), mem, recvBufferCount * bufferSize, ::IBV_ACCESS_LOCAL_WRITE);
+ recvBuffers.reserve(recvBufferCount);
+ for (int i = 0; i<recvBufferCount; ++i) {
+ // Allocate recv buffer
+ recvBuffers.push_back(Buffer(rmr->lkey, &mem[i*bufferSize], bufferSize));
+ postRecv(&recvBuffers[i]);
+ }
+ }
+
+ // Make channel non-blocking by making
+ // associated fd nonblocking
+ void QueuePair::nonblocking() {
+ ::fcntl(cchannel->fd, F_SETFL, O_NONBLOCK);
+ }
+
+ // If we get EAGAIN because the channel has been set non blocking
+ // and we'd have to wait then return an empty event
+ QueuePair::intrusive_ptr QueuePair::getNextChannelEvent() {
+ // First find out which cq has the event
+ ::ibv_cq* cq;
+ void* ctx;
+ int rc = ::ibv_get_cq_event(cchannel.get(), &cq, &ctx);
+ if (rc == -1 && errno == EAGAIN)
+ return 0;
+ CHECK(rc);
+
+ // Batch acknowledge the event
+ if (cq == scq.get()) {
+ if (++outstandingSendEvents > DEFAULT_CQ_ENTRIES / 2) {
+ ::ibv_ack_cq_events(cq, outstandingSendEvents);
+ outstandingSendEvents = 0;
+ }
+ } else if (cq == rcq.get()) {
+ if (++outstandingRecvEvents > DEFAULT_CQ_ENTRIES / 2) {
+ ::ibv_ack_cq_events(cq, outstandingRecvEvents);
+ outstandingRecvEvents = 0;
+ }
+ }
+
+ return static_cast<QueuePair*>(ctx);
+ }
+
+ QueuePairEvent QueuePair::getNextEvent() {
+ ::ibv_wc w;
+ if (::ibv_poll_cq(scq.get(), 1, &w) == 1)
+ return QueuePairEvent(w, scq, SEND);
+ else if (::ibv_poll_cq(rcq.get(), 1, &w) == 1)
+ return QueuePairEvent(w, rcq, RECV);
+ else
+ return QueuePairEvent();
+ }
+
+ void QueuePair::notifyRecv() {
+ CHECK_IBV(ibv_req_notify_cq(rcq.get(), 0));
+ }
+
+ void QueuePair::notifySend() {
+ CHECK_IBV(ibv_req_notify_cq(scq.get(), 0));
+ }
+
+ void QueuePair::postRecv(Buffer* buf) {
+ ::ibv_recv_wr rwr = {};
+
+ rwr.wr_id = reinterpret_cast<uint64_t>(buf);
+ // We are given the whole buffer
+ buf->dataCount(buf->byteCount());
+ rwr.sg_list = &buf->sge;
+ rwr.num_sge = 1;
+
+ ::ibv_recv_wr* badrwr = 0;
+ CHECK(::ibv_post_recv(qp.get(), &rwr, &badrwr));
+ if (badrwr)
+ throw std::logic_error("ibv_post_recv(): Bad rwr");
+ }
+
+ void QueuePair::postSend(Buffer* buf) {
+ ::ibv_send_wr swr = {};
+
+ swr.wr_id = reinterpret_cast<uint64_t>(buf);
+ swr.opcode = IBV_WR_SEND;
+ swr.send_flags = IBV_SEND_SIGNALED;
+ swr.sg_list = &buf->sge;
+ swr.num_sge = 1;
+
+ ::ibv_send_wr* badswr = 0;
+ CHECK(::ibv_post_send(qp.get(), &swr, &badswr));
+ if (badswr)
+ throw std::logic_error("ibv_post_send(): Bad swr");
+ }
+
+ void QueuePair::postSend(uint32_t imm, Buffer* buf) {
+ ::ibv_send_wr swr = {};
+
+ swr.wr_id = reinterpret_cast<uint64_t>(buf);
+ swr.imm_data = htonl(imm);
+ swr.opcode = IBV_WR_SEND_WITH_IMM;
+ swr.send_flags = IBV_SEND_SIGNALED;
+ swr.sg_list = &buf->sge;
+ swr.num_sge = 1;
+
+ ::ibv_send_wr* badswr = 0;
+ CHECK(::ibv_post_send(qp.get(), &swr, &badswr));
+ if (badswr)
+ throw std::logic_error("ibv_post_send(): Bad swr");
+ }
+
+ ConnectionEvent::ConnectionEvent(::rdma_cm_event* e) :
+ id((e->event != RDMA_CM_EVENT_CONNECT_REQUEST) ?
+ Connection::find(e->id) : new Connection(e->id)),
+ listen_id(Connection::find(e->listen_id)),
+ event(mkEvent(e))
+ {}
+
+ ConnectionEvent::operator bool() const {
+ return event;
+ }
+
+ ::rdma_cm_event_type ConnectionEvent::getEventType() const {
+ return event->event;
+ }
+
+ ::rdma_conn_param ConnectionEvent::getConnectionParam() const {
+ // It's badly documented, but it seems from the librdma source code that all the following
+ // event types have a valid param.conn
+ switch (event->event) {
+ case RDMA_CM_EVENT_CONNECT_REQUEST:
+ case RDMA_CM_EVENT_ESTABLISHED:
+ case RDMA_CM_EVENT_REJECTED:
+ case RDMA_CM_EVENT_DISCONNECTED:
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ return event->param.conn;
+ default:
+ ::rdma_conn_param p = {};
+ return p;
+ }
+ }
+
+ boost::intrusive_ptr<Connection> ConnectionEvent::getConnection () const {
+ return id;
+ }
+
+ boost::intrusive_ptr<Connection> ConnectionEvent::getListenId() const {
+ return listen_id;
+ }
+
+ // Wrap the passed in rdma_cm_id with a Connection
+ // this basically happens only on connection request
+ Connection::Connection(::rdma_cm_id* i) :
+ qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ id(mkId(i)),
+ context(0)
+ {
+ impl->fd = id->channel->fd;
+
+ // Just overwrite the previous context as it will
+ // have come from the listening connection
+ if (i)
+ i->context = this;
+ }
+
+ Connection::Connection() :
+ qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate),
+ channel(mkEChannel()),
+ id(mkId(channel.get(), this, RDMA_PS_TCP)),
+ context(0)
+ {
+ impl->fd = channel->fd;
+ }
+
+ Connection::~Connection() {
+ // Reset the id context in case someone else has it
+ id->context = 0;
+ }
+
+ void Connection::ensureQueuePair() {
+ assert(id.get());
+
+ // Only allocate a queue pair if there isn't one already
+ if (qp)
+ return;
+
+ qp = new QueuePair(id);
+ }
+
+ Connection::intrusive_ptr Connection::make() {
+ return new Connection();
+ }
+
+ Connection::intrusive_ptr Connection::find(::rdma_cm_id* i) {
+ if (!i)
+ return 0;
+ Connection* id = static_cast< Connection* >(i->context);
+ if (!id)
+ throw std::logic_error("Couldn't find existing Connection");
+ return id;
+ }
+
+ // Make channel non-blocking by making
+ // associated fd nonblocking
+ void Connection::nonblocking() {
+ assert(id.get());
+ ::fcntl(id->channel->fd, F_SETFL, O_NONBLOCK);
+ }
+
+ // If we get EAGAIN because the channel has been set non blocking
+ // and we'd have to wait then return an empty event
+ ConnectionEvent Connection::getNextEvent() {
+ assert(id.get());
+ ::rdma_cm_event* e;
+ int rc = ::rdma_get_cm_event(id->channel, &e);
+ if (GETERR(rc) == EAGAIN)
+ return ConnectionEvent();
+ CHECK(rc);
+ return ConnectionEvent(e);
+ }
+
+ void Connection::bind(const qpid::sys::SocketAddress& src_addr) const {
+ assert(id.get());
+ CHECK(::rdma_bind_addr(id.get(), getAddrInfo(src_addr).ai_addr));
+ }
+
+ void Connection::listen(int backlog) const {
+ assert(id.get());
+ CHECK(::rdma_listen(id.get(), backlog));
+ }
+
+ void Connection::resolve_addr(
+ const qpid::sys::SocketAddress& dst_addr,
+ int timeout_ms) const
+ {
+ assert(id.get());
+ CHECK(::rdma_resolve_addr(id.get(), 0, getAddrInfo(dst_addr).ai_addr, timeout_ms));
+ }
+
+ void Connection::resolve_route(int timeout_ms) const {
+ assert(id.get());
+ CHECK(::rdma_resolve_route(id.get(), timeout_ms));
+ }
+
+ void Connection::disconnect() const {
+ assert(id.get());
+ int rc = ::rdma_disconnect(id.get());
+ // iWarp doesn't let you disconnect a disconnected connection
+ // but Infiniband can do so it's okay to call rdma_disconnect()
+ // in response to a disconnect event, but we may get an error
+ if (GETERR(rc) == EINVAL)
+ return;
+ CHECK(rc);
+ }
+
+ // TODO: Currently you can only connect with the default connection parameters
+ void Connection::connect(const void* data, size_t len) {
+ assert(id.get());
+ // Need to have a queue pair before we can connect
+ ensureQueuePair();
+
+ ::rdma_conn_param p = DEFAULT_CONNECT_PARAM;
+ p.private_data = data;
+ p.private_data_len = len;
+ CHECK(::rdma_connect(id.get(), &p));
+ }
+
+ void Connection::connect() {
+ connect(0, 0);
+ }
+
+ void Connection::accept(const ::rdma_conn_param& param, const void* data, size_t len) {
+ assert(id.get());
+ // Need to have a queue pair before we can accept
+ ensureQueuePair();
+
+ ::rdma_conn_param p = param;
+ p.private_data = data;
+ p.private_data_len = len;
+ CHECK(::rdma_accept(id.get(), &p));
+ }
+
+ void Connection::accept(const ::rdma_conn_param& param) {
+ accept(param, 0, 0);
+ }
+
+ void Connection::reject(const void* data, size_t len) const {
+ assert(id.get());
+ CHECK(::rdma_reject(id.get(), data, len));
+ }
+
+ void Connection::reject() const {
+ assert(id.get());
+ CHECK(::rdma_reject(id.get(), 0, 0));
+ }
+
+ QueuePair::intrusive_ptr Connection::getQueuePair() {
+ assert(id.get());
+
+ ensureQueuePair();
+
+ return qp;
+ }
+
+ std::string Connection::getLocalName() const {
+ ::sockaddr* addr = ::rdma_get_local_addr(id.get());
+ char hostName[NI_MAXHOST];
+ char portName[NI_MAXSERV];
+ CHECK_IBV(::getnameinfo(
+ addr, sizeof(::sockaddr_storage),
+ hostName, sizeof(hostName),
+ portName, sizeof(portName),
+ NI_NUMERICHOST | NI_NUMERICSERV));
+ std::string r(hostName);
+ r += ":";
+ r += portName;
+ return r;
+ }
+
+ std::string Connection::getPeerName() const {
+ ::sockaddr* addr = ::rdma_get_peer_addr(id.get());
+ char hostName[NI_MAXHOST];
+ char portName[NI_MAXSERV];
+ CHECK_IBV(::getnameinfo(
+ addr, sizeof(::sockaddr_storage),
+ hostName, sizeof(hostName),
+ portName, sizeof(portName),
+ NI_NUMERICHOST | NI_NUMERICSERV));
+ std::string r(hostName);
+ r += ":";
+ r += portName;
+ return r;
+ }
+}
+
+std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t) {
+# define CHECK_TYPE(t) case t: o << #t; break;
+ switch(t) {
+ CHECK_TYPE(RDMA_CM_EVENT_ADDR_RESOLVED)
+ CHECK_TYPE(RDMA_CM_EVENT_ADDR_ERROR)
+ CHECK_TYPE(RDMA_CM_EVENT_ROUTE_RESOLVED)
+ CHECK_TYPE(RDMA_CM_EVENT_ROUTE_ERROR)
+ CHECK_TYPE(RDMA_CM_EVENT_CONNECT_REQUEST)
+ CHECK_TYPE(RDMA_CM_EVENT_CONNECT_RESPONSE)
+ CHECK_TYPE(RDMA_CM_EVENT_CONNECT_ERROR)
+ CHECK_TYPE(RDMA_CM_EVENT_UNREACHABLE)
+ CHECK_TYPE(RDMA_CM_EVENT_REJECTED)
+ CHECK_TYPE(RDMA_CM_EVENT_ESTABLISHED)
+ CHECK_TYPE(RDMA_CM_EVENT_DISCONNECTED)
+ CHECK_TYPE(RDMA_CM_EVENT_DEVICE_REMOVAL)
+ CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_JOIN)
+ CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_ERROR)
+ default:
+ o << "UNKNOWN_EVENT";
+ }
+# undef CHECK_TYPE
+ return o;
+}
diff --git a/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h
new file mode 100644
index 0000000000..8e3429027b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/rdma/rdma_wrap.h
@@ -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.
+ *
+ */
+#ifndef RDMA_WRAP_H
+#define RDMA_WRAP_H
+
+#include <rdma/rdma_cma.h>
+
+#include "qpid/RefCounted.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/ptr_container/ptr_deque.hpp>
+
+#include <vector>
+
+namespace qpid {
+namespace sys {
+ class SocketAddress;
+}}
+
+namespace Rdma {
+ const int DEFAULT_TIMEOUT = 2000; // 2 secs
+ const int DEFAULT_BACKLOG = 100;
+ const int DEFAULT_CQ_ENTRIES = 256;
+ const int DEFAULT_WR_ENTRIES = 64;
+ extern const ::rdma_conn_param DEFAULT_CONNECT_PARAM;
+
+ int deviceCount();
+
+ struct Buffer {
+ friend class QueuePair;
+ friend class QueuePairEvent;
+
+ char* bytes() const;
+ int32_t byteCount() const;
+ int32_t dataCount() const;
+ void dataCount(int32_t);
+
+ private:
+ Buffer(uint32_t lkey, char* bytes, const int32_t byteCount, const int32_t reserve=0);
+ int32_t bufferSize;
+ int32_t reserved; // for framing header
+ ::ibv_sge sge;
+ };
+
+ inline char* Buffer::bytes() const {
+ return (char*) sge.addr;
+ }
+
+ /** return the number of bytes available for application data */
+ inline int32_t Buffer::byteCount() const {
+ return bufferSize - reserved;
+ }
+
+ inline int32_t Buffer::dataCount() const {
+ return sge.length;
+ }
+
+ inline void Buffer::dataCount(int32_t s) {
+ // catch any attempt to overflow a buffer
+ assert(s <= bufferSize + reserved);
+ sge.length = s;
+ }
+
+ class Connection;
+
+ enum QueueDirection {
+ NONE,
+ SEND,
+ RECV
+ };
+
+ class QueuePairEvent {
+ boost::shared_ptr< ::ibv_cq > cq;
+ ::ibv_wc wc;
+ QueueDirection dir;
+
+ friend class QueuePair;
+
+ QueuePairEvent();
+ QueuePairEvent(
+ const ::ibv_wc& w,
+ boost::shared_ptr< ::ibv_cq > c,
+ QueueDirection d);
+
+ public:
+ operator bool() const;
+ bool immPresent() const;
+ uint32_t getImm() const;
+ QueueDirection getDirection() const;
+ ::ibv_wc_opcode getEventType() const;
+ ::ibv_wc_status getEventStatus() const;
+ Buffer* getBuffer() const;
+ };
+
+ // Wrapper for a queue pair - this has the functionality for
+ // putting buffers on the receive queue and for sending buffers
+ // to the other end of the connection.
+ class QueuePair : public qpid::sys::IOHandle, public qpid::RefCounted {
+ friend class Connection;
+
+ boost::shared_ptr< ::ibv_pd > pd;
+ boost::shared_ptr< ::ibv_mr > smr;
+ boost::shared_ptr< ::ibv_mr > rmr;
+ boost::shared_ptr< ::ibv_comp_channel > cchannel;
+ boost::shared_ptr< ::ibv_cq > scq;
+ boost::shared_ptr< ::ibv_cq > rcq;
+ boost::shared_ptr< ::ibv_qp > qp;
+ int outstandingSendEvents;
+ int outstandingRecvEvents;
+ std::vector<Buffer> sendBuffers;
+ std::vector<Buffer> recvBuffers;
+ qpid::sys::Mutex bufferLock;
+ std::vector<int> freeBuffers;
+
+ QueuePair(boost::shared_ptr< ::rdma_cm_id > id);
+ ~QueuePair();
+
+ public:
+ typedef boost::intrusive_ptr<QueuePair> intrusive_ptr;
+
+ // Create a buffers to use for writing
+ void createSendBuffers(int sendBufferCount, int dataSize, int headerSize);
+
+ // Get a send buffer
+ Buffer* getSendBuffer();
+
+ // Return buffer to pool after use
+ void returnSendBuffer(Buffer* b);
+
+ // Create and post recv buffers
+ void allocateRecvBuffers(int recvBufferCount, int bufferSize);
+
+ // Make channel non-blocking by making
+ // associated fd nonblocking
+ void nonblocking();
+
+ // If we get EAGAIN because the channel has been set non blocking
+ // and we'd have to wait then return an empty event
+ QueuePair::intrusive_ptr getNextChannelEvent();
+
+ QueuePairEvent getNextEvent();
+
+ void postRecv(Buffer* buf);
+ void postSend(Buffer* buf);
+ void postSend(uint32_t imm, Buffer* buf);
+ void notifyRecv();
+ void notifySend();
+ };
+
+ class ConnectionEvent {
+ friend class Connection;
+
+ // The order of the members is important as we have to acknowledge
+ // the event before destroying the ids on destruction
+ boost::intrusive_ptr<Connection> id;
+ boost::intrusive_ptr<Connection> listen_id;
+ boost::shared_ptr< ::rdma_cm_event > event;
+
+ ConnectionEvent() {}
+ ConnectionEvent(::rdma_cm_event* e);
+
+ // Default copy, assignment and destructor ok
+ public:
+ operator bool() const;
+ ::rdma_cm_event_type getEventType() const;
+ ::rdma_conn_param getConnectionParam() const;
+ boost::intrusive_ptr<Connection> getConnection () const;
+ boost::intrusive_ptr<Connection> getListenId() const;
+ };
+
+ // For the moment this is a fairly simple wrapper for rdma_cm_id.
+ //
+ // NB: It allocates a protection domain (pd) per connection which means that
+ // registered buffers can't be shared between different connections
+ // (this can only happen between connections on the same controller in any case,
+ // so needs careful management if used)
+ class Connection : public qpid::sys::IOHandle, public qpid::RefCounted {
+ boost::shared_ptr< ::rdma_event_channel > channel;
+ boost::shared_ptr< ::rdma_cm_id > id;
+ QueuePair::intrusive_ptr qp;
+
+ void* context;
+
+ friend class ConnectionEvent;
+ friend class QueuePair;
+
+ // Wrap the passed in rdma_cm_id with a Connection
+ // this basically happens only on connection request
+ Connection(::rdma_cm_id* i);
+ Connection();
+ ~Connection();
+
+ void ensureQueuePair();
+
+ public:
+ typedef boost::intrusive_ptr<Connection> intrusive_ptr;
+
+ static intrusive_ptr make();
+ static intrusive_ptr find(::rdma_cm_id* i);
+
+ template <typename T>
+ void addContext(T* c) {
+ // Don't allow replacing context
+ if (!context)
+ context = c;
+ }
+
+ void removeContext() {
+ context = 0;
+ }
+
+ template <typename T>
+ T* getContext() {
+ return static_cast<T*>(context);
+ }
+
+ // Make channel non-blocking by making
+ // associated fd nonblocking
+ void nonblocking();
+
+ // If we get EAGAIN because the channel has been set non blocking
+ // and we'd have to wait then return an empty event
+ ConnectionEvent getNextEvent();
+
+ void bind(const qpid::sys::SocketAddress& src_addr) const;
+ void listen(int backlog = DEFAULT_BACKLOG) const;
+ void resolve_addr(
+ const qpid::sys::SocketAddress& dst_addr,
+ int timeout_ms = DEFAULT_TIMEOUT) const;
+ void resolve_route(int timeout_ms = DEFAULT_TIMEOUT) const;
+ void disconnect() const;
+
+ // TODO: Currently you can only connect with the default connection parameters
+ void connect(const void* data, size_t len);
+ void connect();
+ template <typename T>
+ void connect(const T* data) {
+ connect(data, sizeof(T));
+ }
+
+ // TODO: Not sure how to default accept params - they come from the connection request
+ // event
+ void accept(const ::rdma_conn_param& param, const void* data, size_t len);
+ void accept(const ::rdma_conn_param& param);
+ template <typename T>
+ void accept(const ::rdma_conn_param& param, const T* data) {
+ accept(param, data, sizeof(T));
+ }
+
+ void reject(const void* data, size_t len) const;
+ void reject() const;
+ template <typename T>
+ void reject(const T* data) const {
+ reject(data, sizeof(T));
+ }
+
+ QueuePair::intrusive_ptr getQueuePair();
+ std::string getLocalName() const;
+ std::string getPeerName() const;
+ std::string getFullName() const { return getLocalName()+"-"+getPeerName(); }
+ };
+}
+
+std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t);
+
+#endif // RDMA_WRAP_H
diff --git a/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp b/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp
new file mode 100644
index 0000000000..06d542c938
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/solaris/ECFPoller.cpp
@@ -0,0 +1,444 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/DeletionManager.h"
+#include "qpid/sys/posix/check.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include <assert.h>
+#include <queue>
+#include <exception>
+
+
+//TODO: Remove this
+#include "qpid/sys/Dispatcher.h"
+
+namespace qpid {
+namespace sys {
+
+// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used
+DeletionManager<PollerHandlePrivate> PollerHandleDeletionManager;
+
+// Instantiate (and define) class static for DeletionManager
+template <>
+DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerHandlePrivate>::allThreadsStatuses(0);
+
+class PollerHandlePrivate {
+ friend class Poller;
+ friend class PollerHandle;
+
+ enum FDStat {
+ ABSENT,
+ MONITORED,
+ INACTIVE,
+ HUNGUP,
+ MONITORED_HUNGUP,
+ DELETED
+ };
+
+ int fd;
+ uint32_t events;
+ FDStat stat;
+ Mutex lock;
+
+ PollerHandlePrivate(int f) :
+ fd(f),
+ events(0),
+ stat(ABSENT) {
+ }
+
+ bool isActive() const {
+ return stat == MONITORED || stat == MONITORED_HUNGUP;
+ }
+
+ void setActive() {
+ stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED;
+ }
+
+ bool isInactive() const {
+ return stat == INACTIVE || stat == HUNGUP;
+ }
+
+ void setInactive() {
+ stat = INACTIVE;
+ }
+
+ bool isIdle() const {
+ return stat == ABSENT;
+ }
+
+ void setIdle() {
+ stat = ABSENT;
+ }
+
+ bool isHungup() const {
+ return stat == MONITORED_HUNGUP || stat == HUNGUP;
+ }
+
+ void setHungup() {
+ assert(stat == MONITORED);
+ stat = HUNGUP;
+ }
+
+ bool isDeleted() const {
+ return stat == DELETED;
+ }
+
+ void setDeleted() {
+ stat = DELETED;
+ }
+};
+
+PollerHandle::PollerHandle(const IOHandle& h) :
+ impl(new PollerHandlePrivate(toFd(h.impl)))
+{}
+
+PollerHandle::~PollerHandle() {
+ {
+ ScopedLock<Mutex> l(impl->lock);
+ if (impl->isDeleted()) {
+ return;
+ }
+ if (impl->isActive()) {
+ impl->setDeleted();
+ }
+ }
+ PollerHandleDeletionManager.markForDeletion(impl);
+}
+
+/**
+ * Concrete implementation of Poller to use the Solaris Event Completion
+ * Framework interface
+ */
+class PollerPrivate {
+ friend class Poller;
+
+ class InterruptHandle: public PollerHandle {
+ std::queue<PollerHandle*> handles;
+
+ void processEvent(Poller::EventType) {
+ PollerHandle* handle = handles.front();
+ handles.pop();
+ assert(handle);
+
+ //Synthesise event
+ Poller::Event event(handle, Poller::INTERRUPTED);
+
+ //Process synthesised event
+ event.process();
+ }
+
+ public:
+ InterruptHandle() : PollerHandle(DummyIOHandle) {}
+
+ void addHandle(PollerHandle& h) {
+ handles.push(&h);
+ }
+
+ PollerHandle *getHandle() {
+ PollerHandle* handle = handles.front();
+ handles.pop();
+ return handle;
+ }
+
+ bool queuedHandles() {
+ return handles.size() > 0;
+ }
+ };
+
+ const int portId;
+ bool isShutdown;
+ InterruptHandle interruptHandle;
+
+ static uint32_t directionToPollEvent(Poller::Direction dir) {
+ switch (dir) {
+ case Poller::INPUT: return POLLIN;
+ case Poller::OUTPUT: return POLLOUT;
+ case Poller::INOUT: return POLLIN | POLLOUT;
+ default: return 0;
+ }
+ }
+
+ static Poller::EventType pollToDirection(uint32_t events) {
+ uint32_t e = events & (POLLIN | POLLOUT);
+ switch (e) {
+ case POLLIN: return Poller::READABLE;
+ case POLLOUT: return Poller::WRITABLE;
+ case POLLIN | POLLOUT: return Poller::READ_WRITABLE;
+ default:
+ return (events & (POLLHUP | POLLERR)) ?
+ Poller::DISCONNECTED : Poller::INVALID;
+ }
+ }
+
+ PollerPrivate() :
+ portId(::port_create()),
+ isShutdown(false) {
+ QPID_POSIX_CHECK(portId);
+ QPID_LOG(trace, "port_create returned port Id: " << portId);
+ }
+
+ ~PollerPrivate() {
+ }
+
+ void interrupt() {
+ //Send an Alarm to the port
+ //We need to send a nonzero event mask, using POLLHUP,
+ //nevertheless the wait method will only look for a PORT_ALERT_SET
+ QPID_LOG(trace, "Sending a port_alert to " << portId);
+ QPID_POSIX_CHECK(::port_alert(portId, PORT_ALERT_SET, POLLHUP,
+ &static_cast<PollerHandle&>(interruptHandle)));
+ }
+};
+
+void Poller::addFd(PollerHandle& handle, Direction dir) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+
+ uint32_t events = 0;
+
+ if (eh.isIdle()) {
+ events = PollerPrivate::directionToPollEvent(dir);
+ } else {
+ assert(eh.isActive());
+ events = eh.events | PollerPrivate::directionToPollEvent(dir);
+ }
+
+ //port_associate can be used to add an association or modify an
+ //existing one
+ QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, events, &handle));
+ eh.events = events;
+ eh.setActive();
+ QPID_LOG(trace, "Poller::addFd(handle=" << &handle
+ << "[" << typeid(&handle).name()
+ << "], fd=" << eh.fd << ")");
+}
+
+void Poller::delFd(PollerHandle& handle) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isIdle());
+ int rc = ::port_dissociate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd);
+ //Allow closing an invalid fd, allowing users to close fd before
+ //doing delFd()
+ if (rc == -1 && errno != EBADFD) {
+ QPID_POSIX_CHECK(rc);
+ }
+ eh.setIdle();
+ QPID_LOG(trace, "Poller::delFd(handle=" << &handle
+ << ", fd=" << eh.fd << ")");
+}
+
+// modFd is equivalent to delFd followed by addFd
+void Poller::modFd(PollerHandle& handle, Direction dir) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(!eh.isIdle());
+
+ eh.events = PollerPrivate::directionToPollEvent(dir);
+
+ //If fd is already associated, events and user arguments are updated
+ //So, no need to check if fd is already associated
+ QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle));
+ eh.setActive();
+ QPID_LOG(trace, "Poller::modFd(handle=" << &handle
+ << ", fd=" << eh.fd << ")");
+}
+
+void Poller::rearmFd(PollerHandle& handle) {
+ PollerHandlePrivate& eh = *handle.impl;
+ ScopedLock<Mutex> l(eh.lock);
+ assert(eh.isInactive());
+
+ QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle));
+ eh.setActive();
+ QPID_LOG(trace, "Poller::rearmdFd(handle=" << &handle
+ << ", fd=" << eh.fd << ")");
+}
+
+void Poller::shutdown() {
+ //Allow sloppy code to shut us down more than once
+ if (impl->isShutdown)
+ return;
+
+ impl->isShutdown = true;
+ impl->interrupt();
+}
+
+bool Poller::hasShutdown()
+{
+ return impl->isShutdown;
+}
+
+bool Poller::interrupt(PollerHandle& handle) {
+ PollerPrivate::InterruptHandle& ih = impl->interruptHandle;
+ PollerHandlePrivate& eh = *static_cast<PollerHandle&>(ih).impl;
+ ScopedLock<Mutex> l(eh.lock);
+ ih.addHandle(handle);
+ impl->interrupt();
+ eh.setActive();
+ return true;
+}
+
+void Poller::run() {
+ // Make sure we can't be interrupted by signals at a bad time
+ ::sigset_t ss;
+ ::sigfillset(&ss);
+ ::pthread_sigmask(SIG_SETMASK, &ss, 0);
+
+ do {
+ Event event = wait();
+
+ // If can read/write then dispatch appropriate callbacks
+ if (event.handle) {
+ event.process();
+ } else {
+ // Handle shutdown
+ switch (event.type) {
+ case SHUTDOWN:
+ return;
+ default:
+ // This should be impossible
+ assert(false);
+ }
+ }
+ } while (true);
+}
+
+Poller::Event Poller::wait(Duration timeout) {
+ timespec_t tout;
+ timespec_t* ptout = NULL;
+ port_event_t pe;
+
+ AbsTime targetTimeout = (timeout == TIME_INFINITE) ? FAR_FUTURE :
+ AbsTime(now(), timeout);
+
+ if (timeout != TIME_INFINITE) {
+ tout.tv_sec = 0;
+ tout.tv_nsec = timeout;
+ ptout = &tout;
+ }
+
+ do {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ QPID_LOG(trace, "About to enter port_get on " << impl->portId
+ << ". Thread " << pthread_self()
+ << ", timeout=" << timeout);
+
+
+ int rc = ::port_get(impl->portId, &pe, ptout);
+
+ QPID_LOG(trace, "port_get on " << impl->portId
+ << " returned " << rc);
+
+ if (impl->isShutdown) {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ return Event(0, SHUTDOWN);
+ }
+
+ if (rc < 0) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case ETIME:
+ return Event(0, TIMEOUT);
+ default:
+ QPID_POSIX_CHECK(rc);
+ }
+ } else {
+ PollerHandle* handle = static_cast<PollerHandle*>(pe.portev_user);
+ PollerHandlePrivate& eh = *handle->impl;
+ ScopedLock<Mutex> l(eh.lock);
+
+ if (eh.isActive()) {
+ QPID_LOG(trace, "Handle is active");
+ //We use alert mode to notify interrupts
+ if (pe.portev_source == PORT_SOURCE_ALERT &&
+ handle == &impl->interruptHandle) {
+ QPID_LOG(trace, "Interrupt notified");
+
+ PollerHandle* wrappedHandle = impl->interruptHandle.getHandle();
+
+ if (impl->interruptHandle.queuedHandles()) {
+ impl->interrupt();
+ eh.setActive();
+ } else {
+ eh.setInactive();
+ }
+ return Event(wrappedHandle, INTERRUPTED);
+ }
+
+ if (pe.portev_source == PORT_SOURCE_FD) {
+ QPID_LOG(trace, "About to send handle: " << handle);
+ if (pe.portev_events & POLLHUP) {
+ if (eh.isHungup()) {
+ return Event(handle, DISCONNECTED);
+ }
+ eh.setHungup();
+ } else {
+ eh.setInactive();
+ }
+ QPID_LOG(trace, "Sending event (thread: "
+ << pthread_self() << ") for handle " << handle
+ << ", direction= "
+ << PollerPrivate::pollToDirection(pe.portev_events));
+ return Event(handle, PollerPrivate::pollToDirection(pe.portev_events));
+ }
+ } else if (eh.isDeleted()) {
+ //Remove the handle from the poller
+ int rc = ::port_dissociate(impl->portId, PORT_SOURCE_FD,
+ (uintptr_t) eh.fd);
+ if (rc == -1 && errno != EBADFD) {
+ QPID_POSIX_CHECK(rc);
+ }
+ }
+ }
+
+ if (timeout == TIME_INFINITE) {
+ continue;
+ }
+ if (rc == 0 && now() > targetTimeout) {
+ PollerHandleDeletionManager.markAllUnusedInThisThread();
+ return Event(0, TIMEOUT);
+ }
+ } while (true);
+}
+
+// Concrete constructors
+Poller::Poller() :
+ impl(new PollerPrivate())
+{}
+
+Poller::~Poller() {
+ delete impl;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/solaris/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/solaris/SystemInfo.cpp
new file mode 100755
index 0000000000..765e5a7eb0
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/solaris/SystemInfo.cpp
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/SystemInfo.h"
+
+#define BSD_COMP
+#include <sys/ioctl.h>
+#include <netdb.h>
+#undef BDS_COMP
+
+
+#include <unistd.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <procfs.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+using namespace std;
+
+namespace qpid {
+namespace sys {
+
+long SystemInfo::concurrency() {
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+bool SystemInfo::getLocalHostname(Address &address) {
+ char name[MAXHOSTNAMELEN];
+ if (::gethostname(name, sizeof(name)) != 0)
+ return false;
+ address.host = name;
+ return true;
+}
+
+static const string LOCALHOST("127.0.0.1");
+static const string TCP("tcp");
+
+void SystemInfo::getLocalIpAddresses(uint16_t port,
+ std::vector<Address> &addrList) {
+ int s = socket(PF_INET, SOCK_STREAM, 0);
+ for (int i=1;;i++) {
+ struct lifreq ifr;
+ ifr.lifr_index = i;
+ if (::ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+ break;
+ }
+ struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.lifr_addr;
+ std::string addr(inet_ntoa(sin->sin_addr));
+ if (addr != LOCALHOST)
+ addrList.push_back(Address(TCP, addr, port));
+ }
+ if (addrList.empty()) {
+ addrList.push_back(Address(TCP, LOCALHOST, port));
+ }
+ close (s);
+}
+
+void SystemInfo::getSystemId(std::string &osName,
+ std::string &nodeName,
+ std::string &release,
+ std::string &version,
+ std::string &machine) {
+ struct utsname _uname;
+ if (uname (&_uname) == 0) {
+ osName = _uname.sysname;
+ nodeName = _uname.nodename;
+ release = _uname.release;
+ version = _uname.version;
+ machine = _uname.machine;
+ }
+}
+
+uint32_t SystemInfo::getProcessId()
+{
+ return (uint32_t) ::getpid();
+}
+
+uint32_t SystemInfo::getParentProcessId()
+{
+ return (uint32_t) ::getppid();
+}
+
+string SystemInfo::getProcessName()
+{
+ psinfo processInfo;
+ char procfile[PATH_MAX];
+ int fd;
+ string value;
+
+ snprintf(procfile, PATH_MAX, "/proc/%d/psinfo", getProcessId());
+ if ((fd = open(procfile, O_RDONLY)) >= 0) {
+ if (read(fd, (void *) &processInfo, sizeof(processInfo)) == sizeof(processInfo)) {
+ value = processInfo.pr_fname;
+ }
+ }
+ return value;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp
new file mode 100644
index 0000000000..5516d72065
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/ssl/SslHandler.h"
+
+#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+
+// Buffer definition
+struct Buff : public SslIO::BufferBase {
+ Buff() :
+ SslIO::BufferBase(new char[65536], 65536)
+ {}
+ ~Buff()
+ { delete [] bytes;}
+};
+
+SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f, bool _nodict) :
+ identifier(id),
+ aio(0),
+ factory(f),
+ codec(0),
+ readError(false),
+ isClient(false),
+ nodict(_nodict)
+{}
+
+SslHandler::~SslHandler() {
+ if (codec)
+ codec->closed();
+ delete codec;
+}
+
+void SslHandler::init(SslIO* a, int numBuffs) {
+ aio = a;
+
+ // Give connection some buffers to use
+ for (int i = 0; i < numBuffs; i++) {
+ aio->queueReadBuffer(new Buff);
+ }
+}
+
+void SslHandler::write(const framing::ProtocolInitiation& data)
+{
+ QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")");
+ SslIO::BufferBase* buff = aio->getQueuedBuffer();
+ if (!buff)
+ buff = new Buff;
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void SslHandler::abort() {
+ // TODO: can't implement currently as underlying functionality not implemented
+ // aio->requestCallback(boost::bind(&SslHandler::eof, this, _1));
+}
+void SslHandler::activateOutput() {
+ aio->notifyPendingWrite();
+}
+
+void SslHandler::giveReadCredit(int32_t) {
+ // FIXME aconway 2008-12-05: not yet implemented.
+}
+
+// Input side
+void SslHandler::readbuff(SslIO& , SslIO::BufferBase* buff) {
+ if (readError) {
+ return;
+ }
+ size_t decoded = 0;
+ if (codec) { // Already initiated
+ try {
+ decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount);
+ }catch(const std::exception& e){
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+ }else{
+ framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount);
+ framing::ProtocolInitiation protocolInit;
+ if (protocolInit.decode(in)) {
+ decoded = in.getPosition();
+ QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")");
+ try {
+ codec = factory->create(protocolInit.getVersion(), *this, identifier, getSecuritySettings(aio));
+ if (!codec) {
+ //TODO: may still want to revise this...
+ //send valid version header & close connection.
+ write(framing::ProtocolInitiation(framing::highestProtocolVersion));
+ readError = true;
+ aio->queueWriteClose();
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+ }
+ }
+ // TODO: unreading needs to go away, and when we can cope
+ // with multiple sub-buffers in the general buffer scheme, it will
+ if (decoded != size_t(buff->dataCount)) {
+ // Adjust buffer for used bytes and then "unread them"
+ buff->dataStart += decoded;
+ buff->dataCount -= decoded;
+ aio->unread(buff);
+ } else {
+ // Give whole buffer back to aio subsystem
+ aio->queueReadBuffer(buff);
+ }
+}
+
+void SslHandler::eof(SslIO&) {
+ QPID_LOG(debug, "DISCONNECTED [" << identifier << "]");
+ if (codec) codec->closed();
+ aio->queueWriteClose();
+}
+
+void SslHandler::closedSocket(SslIO&, const SslSocket& s) {
+ // If we closed with data still to send log a warning
+ if (!aio->writeQueueEmpty()) {
+ QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)");
+ }
+ delete &s;
+ aio->queueForDeletion();
+ delete this;
+}
+
+void SslHandler::disconnect(SslIO& a) {
+ // treat the same as eof
+ eof(a);
+}
+
+// Notifications
+void SslHandler::nobuffs(SslIO&) {
+}
+
+void SslHandler::idle(SslIO&){
+ if (isClient && codec == 0) {
+ codec = factory->create(*this, identifier, getSecuritySettings(aio));
+ write(framing::ProtocolInitiation(codec->getVersion()));
+ return;
+ }
+ if (codec == 0) return;
+ if (codec->canEncode()) {
+ // Try and get a queued buffer if not then construct new one
+ SslIO::BufferBase* buff = aio->getQueuedBuffer();
+ if (!buff) buff = new Buff;
+ size_t encoded=codec->encode(buff->bytes, buff->byteCount);
+ buff->dataCount = encoded;
+ aio->queueWrite(buff);
+ }
+ if (codec->isClosed())
+ aio->queueWriteClose();
+}
+
+SecuritySettings SslHandler::getSecuritySettings(SslIO* aio)
+{
+ SecuritySettings settings = aio->getSecuritySettings();
+ settings.nodict = nodict;
+ return settings;
+}
+
+
+}}} // namespace qpid::sys::ssl
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.h b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h
new file mode 100644
index 0000000000..400fa317fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h
@@ -0,0 +1,78 @@
+#ifndef QPID_SYS_SSL_SSLHANDLER_H
+#define QPID_SYS_SSL_SSLHANDLER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/OutputControl.h"
+
+namespace qpid {
+
+namespace framing {
+ class ProtocolInitiation;
+}
+
+namespace sys {
+namespace ssl {
+
+class SslIO;
+struct SslIOBufferBase;
+class SslSocket;
+
+class SslHandler : public OutputControl {
+ std::string identifier;
+ SslIO* aio;
+ ConnectionCodec::Factory* factory;
+ ConnectionCodec* codec;
+ bool readError;
+ bool isClient;
+ bool nodict;
+
+ void write(const framing::ProtocolInitiation&);
+ qpid::sys::SecuritySettings getSecuritySettings(SslIO* aio);
+
+ public:
+ SslHandler(std::string id, ConnectionCodec::Factory* f, bool nodict);
+ ~SslHandler();
+ void init(SslIO* a, int numBuffs);
+
+ void setClient() { isClient = true; }
+
+ // Output side
+ void abort();
+ void activateOutput();
+ void giveReadCredit(int32_t);
+
+ // Input side
+ void readbuff(SslIO& aio, SslIOBufferBase* buff);
+ void eof(SslIO& aio);
+ void disconnect(SslIO& aio);
+
+ // Notifications
+ void nobuffs(SslIO& aio);
+ void idle(SslIO& aio);
+ void closedSocket(SslIO& aio, const SslSocket& s);
+};
+
+}}} // namespace qpid::sys::ssl
+
+#endif /*!QPID_SYS_SSL_SSLHANDLER_H*/
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp
new file mode 100644
index 0000000000..734ebb483a
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp
@@ -0,0 +1,447 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ssl/SslIo.h"
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/ssl/check.h"
+
+#include "qpid/sys/Time.h"
+#include "qpid/sys/posix/check.h"
+#include "qpid/log/Statement.h"
+
+// TODO The basic algorithm here is not really POSIX specific and with a bit more abstraction
+// could (should) be promoted to be platform portable
+#include <unistd.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <boost/bind.hpp>
+
+using namespace qpid::sys;
+using namespace qpid::sys::ssl;
+
+namespace {
+
+/*
+ * Make *process* not generate SIGPIPE when writing to closed
+ * pipe/socket (necessary as default action is to terminate process)
+ */
+void ignoreSigpipe() {
+ ::signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * We keep per thread state to avoid locking overhead. The assumption is that
+ * on average all the connections are serviced by all the threads so the state
+ * recorded in each thread is about the same. If this turns out not to be the
+ * case we could rebalance the info occasionally.
+ */
+__thread int threadReadTotal = 0;
+__thread int threadMaxRead = 0;
+__thread int threadReadCount = 0;
+__thread int threadWriteTotal = 0;
+__thread int threadWriteCount = 0;
+__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms
+}
+
+/*
+ * Asynch Acceptor
+ */
+
+SslAcceptor::SslAcceptor(const SslSocket& s, Callback callback) :
+ acceptedCallback(callback),
+ handle(s, boost::bind(&SslAcceptor::readable, this, _1), 0, 0),
+ socket(s) {
+
+ s.setNonblocking();
+ ignoreSigpipe();
+}
+
+SslAcceptor::~SslAcceptor()
+{
+ handle.stopWatch();
+}
+
+void SslAcceptor::start(Poller::shared_ptr poller) {
+ handle.startWatch(poller);
+}
+
+/*
+ * We keep on accepting as long as there is something to accept
+ */
+void SslAcceptor::readable(DispatchHandle& h) {
+ SslSocket* s;
+ do {
+ errno = 0;
+ // TODO: Currently we ignore the peers address, perhaps we should
+ // log it or use it for connection acceptance.
+ try {
+ s = socket.accept();
+ if (s) {
+ acceptedCallback(*s);
+ } else {
+ break;
+ }
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Could not accept socket: " << e.what());
+ }
+ } while (true);
+
+ h.rewatch();
+}
+
+/*
+ * Asynch Connector
+ */
+
+SslConnector::SslConnector(const SslSocket& s,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ std::string port,
+ ConnectedCallback connCb,
+ FailedCallback failCb) :
+ DispatchHandle(s,
+ 0,
+ boost::bind(&SslConnector::connComplete, this, _1),
+ boost::bind(&SslConnector::connComplete, this, _1)),
+ connCallback(connCb),
+ failCallback(failCb),
+ socket(s)
+{
+ //TODO: would be better for connect to be performed on a
+ //non-blocking socket, but that doesn't work at present so connect
+ //blocks until complete
+ try {
+ socket.connect(hostname, port);
+ socket.setNonblocking();
+ startWatch(poller);
+ } catch(std::exception& e) {
+ failure(-1, std::string(e.what()));
+ }
+}
+
+void SslConnector::connComplete(DispatchHandle& h)
+{
+ int errCode = socket.getError();
+
+ h.stopWatch();
+ if (errCode == 0) {
+ connCallback(socket);
+ DispatchHandle::doDelete();
+ } else {
+ // TODO: This need to be fixed as strerror isn't thread safe
+ failure(errCode, std::string(::strerror(errCode)));
+ }
+}
+
+void SslConnector::failure(int errCode, std::string message)
+{
+ if (failCallback)
+ failCallback(errCode, message);
+
+ socket.close();
+ delete &socket;
+
+ DispatchHandle::doDelete();
+}
+
+/*
+ * Asynch reader/writer
+ */
+SslIO::SslIO(const SslSocket& s,
+ ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
+ ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) :
+
+ DispatchHandle(s,
+ boost::bind(&SslIO::readable, this, _1),
+ boost::bind(&SslIO::writeable, this, _1),
+ boost::bind(&SslIO::disconnected, this, _1)),
+ readCallback(rCb),
+ eofCallback(eofCb),
+ disCallback(disCb),
+ closedCallback(cCb),
+ emptyCallback(eCb),
+ idleCallback(iCb),
+ socket(s),
+ queuedClose(false),
+ writePending(false) {
+
+ s.setNonblocking();
+}
+
+struct deleter
+{
+ template <typename T>
+ void operator()(T *ptr){ delete ptr;}
+};
+
+SslIO::~SslIO() {
+ std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter());
+ std::for_each( writeQueue.begin(), writeQueue.end(), deleter());
+}
+
+void SslIO::queueForDeletion() {
+ DispatchHandle::doDelete();
+}
+
+void SslIO::start(Poller::shared_ptr poller) {
+ DispatchHandle::startWatch(poller);
+}
+
+void SslIO::queueReadBuffer(BufferBase* buff) {
+ assert(buff);
+ buff->dataStart = 0;
+ buff->dataCount = 0;
+ bufferQueue.push_back(buff);
+ DispatchHandle::rewatchRead();
+}
+
+void SslIO::unread(BufferBase* buff) {
+ assert(buff);
+ if (buff->dataStart != 0) {
+ memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount);
+ buff->dataStart = 0;
+ }
+ bufferQueue.push_front(buff);
+ DispatchHandle::rewatchRead();
+}
+
+void SslIO::queueWrite(BufferBase* buff) {
+ assert(buff);
+ // If we've already closed the socket then throw the write away
+ if (queuedClose) {
+ bufferQueue.push_front(buff);
+ return;
+ } else {
+ writeQueue.push_front(buff);
+ }
+ writePending = false;
+ DispatchHandle::rewatchWrite();
+}
+
+void SslIO::notifyPendingWrite() {
+ writePending = true;
+ DispatchHandle::rewatchWrite();
+}
+
+void SslIO::queueWriteClose() {
+ queuedClose = true;
+ DispatchHandle::rewatchWrite();
+}
+
+/** Return a queued buffer if there are enough
+ * to spare
+ */
+SslIO::BufferBase* SslIO::getQueuedBuffer() {
+ // Always keep at least one buffer (it might have data that was "unread" in it)
+ if (bufferQueue.size()<=1)
+ return 0;
+ BufferBase* buff = bufferQueue.back();
+ assert(buff);
+ buff->dataStart = 0;
+ buff->dataCount = 0;
+ bufferQueue.pop_back();
+ return buff;
+}
+
+/*
+ * We keep on reading as long as we have something to read and a buffer to put
+ * it in
+ */
+void SslIO::readable(DispatchHandle& h) {
+ int readTotal = 0;
+ AbsTime readStartTime = AbsTime::now();
+ do {
+ // (Try to) get a buffer
+ if (!bufferQueue.empty()) {
+ // Read into buffer
+ BufferBase* buff = bufferQueue.front();
+ assert(buff);
+ bufferQueue.pop_front();
+ errno = 0;
+ int readCount = buff->byteCount-buff->dataCount;
+ int rc = socket.read(buff->bytes + buff->dataCount, readCount);
+ if (rc > 0) {
+ buff->dataCount += rc;
+ threadReadTotal += rc;
+ readTotal += rc;
+
+ readCallback(*this, buff);
+ if (rc != readCount) {
+ // If we didn't fill the read buffer then time to stop reading
+ break;
+ }
+
+ // Stop reading if we've overrun our timeslot
+ if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) {
+ break;
+ }
+
+ } else {
+ // Put buffer back (at front so it doesn't interfere with unread buffers)
+ bufferQueue.push_front(buff);
+ assert(buff);
+
+ // Eof or other side has gone away
+ if (rc == 0 || errno == ECONNRESET) {
+ eofCallback(*this);
+ h.unwatchRead();
+ break;
+ } else if (errno == EAGAIN) {
+ // We have just put a buffer back so we know
+ // we can carry on watching for reads
+ break;
+ } else {
+ // Report error then just treat as a socket disconnect
+ QPID_LOG(error, "Error reading socket: " << getErrorString(PR_GetError()));
+ eofCallback(*this);
+ h.unwatchRead();
+ break;
+ }
+ }
+ } else {
+ // Something to read but no buffer
+ if (emptyCallback) {
+ emptyCallback(*this);
+ }
+ // If we still have no buffers we can't do anything more
+ if (bufferQueue.empty()) {
+ h.unwatchRead();
+ break;
+ }
+
+ }
+ } while (true);
+
+ ++threadReadCount;
+ threadMaxRead = std::max(threadMaxRead, readTotal);
+ return;
+}
+
+/*
+ * We carry on writing whilst we have data to write and we can write
+ */
+void SslIO::writeable(DispatchHandle& h) {
+ int writeTotal = 0;
+ do {
+ // See if we've got something to write
+ if (!writeQueue.empty()) {
+ // Write buffer
+ BufferBase* buff = writeQueue.back();
+ writeQueue.pop_back();
+ errno = 0;
+ assert(buff->dataStart+buff->dataCount <= buff->byteCount);
+ int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount);
+ if (rc >= 0) {
+ threadWriteTotal += rc;
+ writeTotal += rc;
+
+ // If we didn't write full buffer put rest back
+ if (rc != buff->dataCount) {
+ buff->dataStart += rc;
+ buff->dataCount -= rc;
+ writeQueue.push_back(buff);
+ break;
+ }
+
+ // Recycle the buffer
+ queueReadBuffer(buff);
+
+ // If we've already written more than the max for reading then stop
+ // (this is to stop writes dominating reads)
+ if (writeTotal > threadMaxRead)
+ break;
+ } else {
+ // Put buffer back
+ writeQueue.push_back(buff);
+ if (errno == ECONNRESET || errno == EPIPE) {
+ // Just stop watching for write here - we'll get a
+ // disconnect callback soon enough
+ h.unwatchWrite();
+ break;
+ } else if (errno == EAGAIN) {
+ // We have just put a buffer back so we know
+ // we can carry on watching for writes
+ break;
+ } else {
+ QPID_LOG(error, "Error writing to socket: " << getErrorString(PR_GetError()));
+ h.unwatchWrite();
+ break;
+ }
+ }
+ } else {
+ // If we're waiting to close the socket then can do it now as there is nothing to write
+ if (queuedClose) {
+ close(h);
+ break;
+ }
+ // Fd is writable, but nothing to write
+ if (idleCallback) {
+ writePending = false;
+ idleCallback(*this);
+ }
+ // If we still have no buffers to write we can't do anything more
+ if (writeQueue.empty() && !writePending && !queuedClose) {
+ h.unwatchWrite();
+ // The following handles the case where writePending is
+ // set to true after the test above; in this case its
+ // possible that the unwatchWrite overwrites the
+ // desired rewatchWrite so we correct that here
+ if (writePending)
+ h.rewatchWrite();
+ break;
+ }
+ }
+ } while (true);
+
+ ++threadWriteCount;
+ return;
+}
+
+void SslIO::disconnected(DispatchHandle& h) {
+ // If we've already queued close do it instead of disconnected callback
+ if (queuedClose) {
+ close(h);
+ } else if (disCallback) {
+ disCallback(*this);
+ h.unwatch();
+ }
+}
+
+/*
+ * Close the socket and callback to say we've done it
+ */
+void SslIO::close(DispatchHandle& h) {
+ h.stopWatch();
+ socket.close();
+ if (closedCallback) {
+ closedCallback(*this, socket);
+ }
+}
+
+SecuritySettings SslIO::getSecuritySettings() {
+ SecuritySettings settings;
+ settings.ssf = socket.getKeyLen();
+ settings.authid = socket.getClientAuthId();
+ return settings;
+}
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.h b/qpid/cpp/src/qpid/sys/ssl/SslIo.h
new file mode 100644
index 0000000000..8785852c24
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.h
@@ -0,0 +1,172 @@
+#ifndef _sys_ssl_SslIO
+#define _sys_ssl_SslIO
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/SecuritySettings.h"
+
+#include <boost/function.hpp>
+#include <deque>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+class SslSocket;
+
+/*
+ * Asynchronous ssl acceptor: accepts connections then does a callback
+ * with the accepted fd
+ */
+class SslAcceptor {
+public:
+ typedef boost::function1<void, const SslSocket&> Callback;
+
+private:
+ Callback acceptedCallback;
+ qpid::sys::DispatchHandle handle;
+ const SslSocket& socket;
+
+public:
+ SslAcceptor(const SslSocket& s, Callback callback);
+ ~SslAcceptor();
+ void start(qpid::sys::Poller::shared_ptr poller);
+
+private:
+ void readable(qpid::sys::DispatchHandle& handle);
+};
+
+/*
+ * Asynchronous ssl connector: starts the process of initiating a
+ * connection and invokes a callback when completed or failed.
+ */
+class SslConnector : private qpid::sys::DispatchHandle {
+public:
+ typedef boost::function1<void, const SslSocket&> ConnectedCallback;
+ typedef boost::function2<void, int, std::string> FailedCallback;
+
+private:
+ ConnectedCallback connCallback;
+ FailedCallback failCallback;
+ const SslSocket& socket;
+
+public:
+ SslConnector(const SslSocket& socket,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ std::string port,
+ ConnectedCallback connCb,
+ FailedCallback failCb = 0);
+
+private:
+ void connComplete(DispatchHandle& handle);
+ void failure(int, std::string);
+};
+
+struct SslIOBufferBase {
+ char* const bytes;
+ const int32_t byteCount;
+ int32_t dataStart;
+ int32_t dataCount;
+
+ SslIOBufferBase(char* const b, const int32_t s) :
+ bytes(b),
+ byteCount(s),
+ dataStart(0),
+ dataCount(0)
+ {}
+
+ virtual ~SslIOBufferBase()
+ {}
+};
+
+/*
+ * Asychronous reader/writer:
+ * Reader accepts buffers to read into; reads into the provided buffers
+ * and then does a callback with the buffer and amount read. Optionally it can callback
+ * when there is something to read but no buffer to read it into.
+ *
+ * Writer accepts a buffer and queues it for writing; can also be given
+ * a callback for when writing is "idle" (ie fd is writable, but nothing to write)
+ *
+ * The class is implemented in terms of DispatchHandle to allow it to be deleted by deleting
+ * the contained DispatchHandle
+ */
+class SslIO : private qpid::sys::DispatchHandle {
+public:
+ typedef SslIOBufferBase BufferBase;
+
+ typedef boost::function2<void, SslIO&, BufferBase*> ReadCallback;
+ typedef boost::function1<void, SslIO&> EofCallback;
+ typedef boost::function1<void, SslIO&> DisconnectCallback;
+ typedef boost::function2<void, SslIO&, const SslSocket&> ClosedCallback;
+ typedef boost::function1<void, SslIO&> BuffersEmptyCallback;
+ typedef boost::function1<void, SslIO&> IdleCallback;
+
+
+private:
+ ReadCallback readCallback;
+ EofCallback eofCallback;
+ DisconnectCallback disCallback;
+ ClosedCallback closedCallback;
+ BuffersEmptyCallback emptyCallback;
+ IdleCallback idleCallback;
+ const SslSocket& socket;
+ std::deque<BufferBase*> bufferQueue;
+ std::deque<BufferBase*> writeQueue;
+ bool queuedClose;
+ /**
+ * This flag is used to detect and handle concurrency between
+ * calls to notifyPendingWrite() (which can be made from any thread) and
+ * the execution of the writeable() method (which is always on the
+ * thread processing this handle.
+ */
+ volatile bool writePending;
+
+public:
+ SslIO(const SslSocket& s,
+ ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
+ ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0);
+ void queueForDeletion();
+
+ void start(qpid::sys::Poller::shared_ptr poller);
+ void queueReadBuffer(BufferBase* buff);
+ void unread(BufferBase* buff);
+ void queueWrite(BufferBase* buff);
+ void notifyPendingWrite();
+ void queueWriteClose();
+ bool writeQueueEmpty() { return writeQueue.empty(); }
+ BufferBase* getQueuedBuffer();
+
+ qpid::sys::SecuritySettings getSecuritySettings();
+
+private:
+ ~SslIO();
+ void readable(qpid::sys::DispatchHandle& handle);
+ void writeable(qpid::sys::DispatchHandle& handle);
+ void disconnected(qpid::sys::DispatchHandle& handle);
+ void close(qpid::sys::DispatchHandle& handle);
+};
+
+}}}
+
+#endif // _sys_ssl_SslIO
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp
new file mode 100644
index 0000000000..f7483a220c
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp
@@ -0,0 +1,360 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/ssl/check.h"
+#include "qpid/sys/ssl/util.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/posix/check.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <cstdlib>
+#include <string.h>
+#include <iostream>
+
+#include <private/pprio.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <ssl.h>
+#include <key.h>
+
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+namespace {
+std::string getName(int fd, bool local, bool includeService = false)
+{
+ ::sockaddr_storage name; // big enough for any socket address
+ ::socklen_t namelen = sizeof(name);
+
+ int result = -1;
+ if (local) {
+ result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
+ } else {
+ result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
+ }
+
+ QPID_POSIX_CHECK(result);
+
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (includeService) {
+ if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ return std::string(dispName) + ":" + std::string(servName);
+
+ } else {
+ if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ return dispName;
+ }
+}
+
+std::string getService(int fd, bool local)
+{
+ ::sockaddr_storage name; // big enough for any socket address
+ ::socklen_t namelen = sizeof(name);
+
+ int result = -1;
+ if (local) {
+ result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
+ } else {
+ result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
+ }
+
+ QPID_POSIX_CHECK(result);
+
+ char servName[NI_MAXSERV];
+ if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0,
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ return servName;
+}
+
+const std::string DOMAIN_SEPARATOR("@");
+const std::string DC_SEPARATOR(".");
+const std::string DC("DC");
+const std::string DN_DELIMS(" ,=");
+
+std::string getDomainFromSubject(std::string subject)
+{
+ std::string::size_type last = subject.find_first_not_of(DN_DELIMS, 0);
+ std::string::size_type i = subject.find_first_of(DN_DELIMS, last);
+
+ std::string domain;
+ bool nextTokenIsDC = false;
+ while (std::string::npos != i || std::string::npos != last)
+ {
+ std::string token = subject.substr(last, i - last);
+ if (nextTokenIsDC) {
+ if (domain.size()) domain += DC_SEPARATOR;
+ domain += token;
+ nextTokenIsDC = false;
+ } else if (token == DC) {
+ nextTokenIsDC = true;
+ }
+ last = subject.find_first_not_of(DN_DELIMS, i);
+ i = subject.find_first_of(DN_DELIMS, last);
+ }
+ return domain;
+}
+
+}
+
+SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0)
+{
+ impl->fd = ::socket (PF_INET, SOCK_STREAM, 0);
+ if (impl->fd < 0) throw QPID_POSIX_ERROR(errno);
+ socket = SSL_ImportFD(0, PR_ImportTCPSocket(impl->fd));
+}
+
+/**
+ * This form of the constructor is used with the server-side sockets
+ * returned from accept. Because we use posix accept rather than
+ * PR_Accept, we have to reset the handshake.
+ */
+SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : IOHandle(ioph), socket(0), prototype(0)
+{
+ socket = SSL_ImportFD(model, PR_ImportTCPSocket(impl->fd));
+ NSS_CHECK(SSL_ResetHandshake(socket, true));
+}
+
+void SslSocket::setNonblocking() const
+{
+ PRSocketOptionData option;
+ option.option = PR_SockOpt_Nonblocking;
+ option.value.non_blocking = true;
+ PR_SetSocketOption(socket, &option);
+}
+
+void SslSocket::connect(const std::string& host, const std::string& port) const
+{
+ std::stringstream namestream;
+ namestream << host << ":" << port;
+ connectname = namestream.str();
+
+ void* arg;
+ // Use the connection's cert-name if it has one; else use global cert-name
+ if (certname != "") {
+ arg = const_cast<char*>(certname.c_str());
+ } else if (SslOptions::global.certName.empty()) {
+ arg = 0;
+ } else {
+ arg = const_cast<char*>(SslOptions::global.certName.c_str());
+ }
+ NSS_CHECK(SSL_GetClientAuthDataHook(socket, NSS_GetClientAuthData, arg));
+ NSS_CHECK(SSL_SetURL(socket, host.data()));
+
+ char hostBuffer[PR_NETDB_BUF_SIZE];
+ PRHostEnt hostEntry;
+ PR_CHECK(PR_GetHostByName(host.data(), hostBuffer, PR_NETDB_BUF_SIZE, &hostEntry));
+ PRNetAddr address;
+ int value = PR_EnumerateHostEnt(0, &hostEntry, boost::lexical_cast<PRUint16>(port), &address);
+ if (value < 0) {
+ throw Exception(QPID_MSG("Error getting address for host: " << ErrorString()));
+ } else if (value == 0) {
+ throw Exception(QPID_MSG("Could not resolve address for host."));
+ }
+ PR_CHECK(PR_Connect(socket, &address, PR_INTERVAL_NO_TIMEOUT));
+ NSS_CHECK(SSL_ForceHandshake(socket));
+}
+
+void SslSocket::close() const
+{
+ if (impl->fd > 0) {
+ PR_Close(socket);
+ impl->fd = -1;
+ }
+}
+
+int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, bool clientAuth) const
+{
+ //configure prototype socket:
+ prototype = SSL_ImportFD(0, PR_NewTCPSocket());
+ if (clientAuth) {
+ NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE));
+ NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE));
+ }
+
+ //get certificate and key (is this the correct way?)
+ CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(certName.c_str()), 0);
+ if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << certName << "'"));
+ SECKEYPrivateKey *key = PK11_FindKeyByAnyCert(cert, 0);
+ if (!key) throw Exception(QPID_MSG("Failed to retrieve private key from certificate"));
+ NSS_CHECK(SSL_ConfigSecureServer(prototype, cert, key, NSS_FindCertKEAType(cert)));
+ SECKEY_DestroyPrivateKey(key);
+ CERT_DestroyCertificate(cert);
+
+ //bind and listen
+ const int& socket = impl->fd;
+ int yes=1;
+ QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
+ struct sockaddr_in name;
+ name.sin_family = AF_INET;
+ name.sin_port = htons(port);
+ name.sin_addr.s_addr = 0;
+ if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0)
+ throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno)));
+ if (::listen(socket, backlog) < 0)
+ throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno)));
+
+ socklen_t namelen = sizeof(name);
+ if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0)
+ throw QPID_POSIX_ERROR(errno);
+
+ return ntohs(name.sin_port);
+}
+
+SslSocket* SslSocket::accept() const
+{
+ int afd = ::accept(impl->fd, 0, 0);
+ if ( afd >= 0) {
+ return new SslSocket(new IOHandlePrivate(afd), prototype);
+ } else if (errno == EAGAIN) {
+ return 0;
+ } else {
+ throw QPID_POSIX_ERROR(errno);
+ }
+}
+
+int SslSocket::read(void *buf, size_t count) const
+{
+ return PR_Read(socket, buf, count);
+}
+
+int SslSocket::write(const void *buf, size_t count) const
+{
+ return PR_Write(socket, buf, count);
+}
+
+std::string SslSocket::getSockname() const
+{
+ return getName(impl->fd, true);
+}
+
+std::string SslSocket::getPeername() const
+{
+ return getName(impl->fd, false);
+}
+
+std::string SslSocket::getPeerAddress() const
+{
+ if (!connectname.empty())
+ return connectname;
+ return getName(impl->fd, false, true);
+}
+
+std::string SslSocket::getLocalAddress() const
+{
+ return getName(impl->fd, true, true);
+}
+
+uint16_t SslSocket::getLocalPort() const
+{
+ return std::atoi(getService(impl->fd, true).c_str());
+}
+
+uint16_t SslSocket::getRemotePort() const
+{
+ return atoi(getService(impl->fd, true).c_str());
+}
+
+int SslSocket::getError() const
+{
+ int result;
+ socklen_t rSize = sizeof (result);
+
+ if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0)
+ throw QPID_POSIX_ERROR(errno);
+
+ return result;
+}
+
+void SslSocket::setTcpNoDelay(bool nodelay) const
+{
+ if (nodelay) {
+ PRSocketOptionData option;
+ option.option = PR_SockOpt_NoDelay;
+ option.value.no_delay = true;
+ PR_SetSocketOption(socket, &option);
+ }
+}
+
+void SslSocket::setCertName(const std::string& name)
+{
+ certname = name;
+}
+
+
+/** get the bit length of the current cipher's key */
+int SslSocket::getKeyLen() const
+{
+ int enabled = 0;
+ int keySize = 0;
+ SECStatus rc;
+
+ rc = SSL_SecurityStatus( socket,
+ &enabled,
+ NULL,
+ NULL,
+ &keySize,
+ NULL, NULL );
+ if (rc == SECSuccess && enabled) {
+ return keySize;
+ }
+ return 0;
+}
+
+std::string SslSocket::getClientAuthId() const
+{
+ std::string authId;
+ CERTCertificate* cert = SSL_PeerCertificate(socket);
+ if (cert) {
+ authId = CERT_GetCommonName(&(cert->subject));
+ /*
+ * The NSS function CERT_GetDomainComponentName only returns
+ * the last component of the domain name, so we have to parse
+ * the subject manually to extract the full domain.
+ */
+ std::string domain = getDomainFromSubject(cert->subjectName);
+ if (!domain.empty()) {
+ authId += DOMAIN_SEPARATOR;
+ authId += domain;
+ }
+ CERT_DestroyCertificate(cert);
+ }
+ return authId;
+}
+
+}}} // namespace qpid::sys::ssl
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.h b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h
new file mode 100644
index 0000000000..993859495b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h
@@ -0,0 +1,132 @@
+#ifndef _sys_ssl_Socket_h
+#define _sys_ssl_Socket_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IOHandle.h"
+#include <nspr.h>
+
+#include <string>
+
+struct sockaddr;
+
+namespace qpid {
+namespace sys {
+
+class Duration;
+
+namespace ssl {
+
+class SslSocket : public qpid::sys::IOHandle
+{
+public:
+ /** Create a socket wrapper for descriptor. */
+ SslSocket();
+
+ /** Set socket non blocking */
+ void setNonblocking() const;
+
+ /** Set tcp-nodelay */
+ void setTcpNoDelay(bool nodelay) const;
+
+ /** Set SSL cert-name. Allows the cert-name to be set per
+ * connection, overriding global cert-name settings from
+ * NSSInit().*/
+ void setCertName(const std::string& certName);
+
+ void connect(const std::string& host, const std::string& port) const;
+
+ void close() const;
+
+ /** Bind to a port and start listening.
+ *@param port 0 means choose an available port.
+ *@param backlog maximum number of pending connections.
+ *@param certName name of certificate to use to identify the server
+ *@return The bound port.
+ */
+ int listen(uint16_t port = 0, int backlog = 10, const std::string& certName = "localhost.localdomain", bool clientAuth = false) const;
+
+ /**
+ * Accept a connection from a socket that is already listening
+ * and has an incoming connection
+ */
+ SslSocket* accept() const;
+
+ // TODO The following are raw operations, maybe they need better wrapping?
+ int read(void *buf, size_t count) const;
+ int write(const void *buf, size_t count) const;
+
+ /** Returns the "socket name" ie the address bound to
+ * the near end of the socket
+ */
+ std::string getSockname() const;
+
+ /** Returns the "peer name" ie the address bound to
+ * the remote end of the socket
+ */
+ std::string getPeername() const;
+
+ /**
+ * Returns an address (host and port) for the remote end of the
+ * socket
+ */
+ std::string getPeerAddress() const;
+ /**
+ * Returns an address (host and port) for the local end of the
+ * socket
+ */
+ std::string getLocalAddress() const;
+
+ /**
+ * Returns the full address of the connection: local and remote host and port.
+ */
+ std::string getFullAddress() const { return getLocalAddress()+"-"+getPeerAddress(); }
+
+ uint16_t getLocalPort() const;
+ uint16_t getRemotePort() const;
+
+ /**
+ * Returns the error code stored in the socket. This may be used
+ * to determine the result of a non-blocking connect.
+ */
+ int getError() const;
+
+ int getKeyLen() const;
+ std::string getClientAuthId() const;
+
+private:
+ mutable std::string connectname;
+ mutable PRFileDesc* socket;
+ std::string certname;
+
+ /**
+ * 'model' socket, with configuration to use when importing
+ * accepted sockets for use as ssl sockets. Set on listen(), used
+ * in accept to pass through to newly created socket instances.
+ */
+ mutable PRFileDesc* prototype;
+
+ SslSocket(IOHandlePrivate* ioph, PRFileDesc* model);
+};
+
+}}}
+#endif /*!_sys_ssl_Socket_h*/
diff --git a/qpid/cpp/src/qpid/sys/ssl/check.cpp b/qpid/cpp/src/qpid/sys/ssl/check.cpp
new file mode 100644
index 0000000000..72a2e265bd
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/check.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/ssl/check.h"
+#include <secerr.h>
+#include <sslerr.h>
+#include <boost/format.hpp>
+
+using boost::format;
+using boost::str;
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+ErrorString::ErrorString() : code(PR_GetError()), buffer(new char[PR_GetErrorTextLength()]), used(PR_GetErrorText(buffer)) {}
+
+ErrorString::~ErrorString()
+{
+ delete[] buffer;
+}
+
+std::string ErrorString::getString() const
+{
+ std::string msg = std::string(buffer, used);
+ if (!used) {
+ //seems most of the NSPR/NSS errors don't have text set for
+ //them, add a few specific ones in here. (TODO: more complete
+ //list?):
+ return getErrorString(code);
+ } else {
+ return str(format("%1% [%2%]") % msg % code);
+ }
+}
+
+std::string getErrorString(int code)
+{
+ std::string msg;
+ switch (code) {
+ case SSL_ERROR_EXPORT_ONLY_SERVER: msg = "Unable to communicate securely. Peer does not support high-grade encryption."; break;
+ case SSL_ERROR_US_ONLY_SERVER: msg = "Unable to communicate securely. Peer requires high-grade encryption which is not supported."; break;
+ case SSL_ERROR_NO_CYPHER_OVERLAP: msg = "Cannot communicate securely with peer: no common encryption algorithm(s)."; break;
+ case SSL_ERROR_NO_CERTIFICATE: msg = "Unable to find the certificate or key necessary for authentication."; break;
+ case SSL_ERROR_BAD_CERTIFICATE: msg = "Unable to communicate securely with peer: peers's certificate was rejected."; break;
+ case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE: msg = "Unsupported certificate type."; break;
+ case SSL_ERROR_WRONG_CERTIFICATE: msg = "Client authentication failed: private key in key database does not correspond to public key in certificate database."; break;
+ case SSL_ERROR_BAD_CERT_DOMAIN: msg = "Unable to communicate securely with peer: requested domain name does not match the server's certificate."; break;
+ case SSL_ERROR_BAD_CERT_ALERT: msg = "SSL peer cannot verify your certificate."; break;
+ case SSL_ERROR_REVOKED_CERT_ALERT: msg = "SSL peer rejected your certificate as revoked."; break;
+ case SSL_ERROR_EXPIRED_CERT_ALERT: msg = "SSL peer rejected your certificate as expired."; break;
+
+ case PR_DIRECTORY_LOOKUP_ERROR: msg = "A directory lookup on a network address has failed"; break;
+ case PR_CONNECT_RESET_ERROR: msg = "TCP connection reset by peer"; break;
+ case PR_END_OF_FILE_ERROR: msg = "Encountered end of file"; break;
+ case SEC_ERROR_EXPIRED_CERTIFICATE: msg = "Peer's certificate has expired"; break;
+ default: msg = (code < -6000) ? "NSS error" : "NSPR error"; break;
+ }
+ return str(format("%1% [%2%]") % msg % code);
+}
+
+std::ostream& operator<<(std::ostream& out, const ErrorString& err)
+{
+ out << err.getString();
+ return out;
+}
+
+
+}}} // namespace qpid::sys::ssl
diff --git a/qpid/cpp/src/qpid/sys/ssl/check.h b/qpid/cpp/src/qpid/sys/ssl/check.h
new file mode 100644
index 0000000000..28d3c74ad0
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/check.h
@@ -0,0 +1,57 @@
+#ifndef QPID_SYS_SSL_CHECK_H
+#define QPID_SYS_SSL_CHECK_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/Msg.h"
+
+#include <iostream>
+#include <string>
+#include <nspr.h>
+#include <nss.h>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+std::string getErrorString(int code);
+
+class ErrorString
+{
+ public:
+ ErrorString();
+ ~ErrorString();
+ std::string getString() const;
+ private:
+ const int code;
+ char* const buffer;
+ const size_t used;
+};
+
+std::ostream& operator<<(std::ostream& out, const ErrorString& err);
+
+}}} // namespace qpid::sys::ssl
+
+
+#define NSS_CHECK(value) if (value != SECSuccess) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); }
+#define PR_CHECK(value) if (value != PR_SUCCESS) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); }
+
+#endif /*!QPID_SYS_SSL_CHECK_H*/
diff --git a/qpid/cpp/src/qpid/sys/ssl/util.cpp b/qpid/cpp/src/qpid/sys/ssl/util.cpp
new file mode 100644
index 0000000000..3078e894df
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/util.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/ssl/util.h"
+#include "qpid/sys/ssl/check.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/SystemInfo.h"
+
+#include <unistd.h>
+#include <nspr.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <ssl.h>
+
+#include <iostream>
+#include <fstream>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+static const std::string LOCALHOST("127.0.0.1");
+
+std::string defaultCertName()
+{
+ Address address;
+ if (SystemInfo::getLocalHostname(address)) {
+ return address.host;
+ } else {
+ return LOCALHOST;
+ }
+}
+
+SslOptions::SslOptions() : qpid::Options("SSL Settings"),
+ certName(defaultCertName()),
+ exportPolicy(false)
+{
+ addOptions()
+ ("ssl-use-export-policy", optValue(exportPolicy), "Use NSS export policy")
+ ("ssl-cert-password-file", optValue(certPasswordFile, "PATH"), "File containing password to use for accessing certificate database")
+ ("ssl-cert-db", optValue(certDbPath, "PATH"), "Path to directory containing certificate database")
+ ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use");
+}
+
+SslOptions& SslOptions::operator=(const SslOptions& o)
+{
+ certDbPath = o.certDbPath;
+ certName = o.certName;
+ certPasswordFile = o.certPasswordFile;
+ exportPolicy = o.exportPolicy;
+ return *this;
+}
+
+char* promptForPassword(PK11SlotInfo*, PRBool retry, void*)
+{
+ if (retry) return 0;
+ //TODO: something else?
+ return PL_strdup(getpass("Please enter the password for accessing the certificate database:"));
+}
+
+SslOptions SslOptions::global;
+
+char* readPasswordFromFile(PK11SlotInfo*, PRBool retry, void*)
+{
+ const std::string& passwordFile = SslOptions::global.certPasswordFile;
+ if (retry || passwordFile.empty() || !boost::filesystem::exists(passwordFile)) {
+ return 0;
+ } else {
+ std::ifstream file(passwordFile.c_str());
+ std::string password;
+ file >> password;
+ return PL_strdup(password.c_str());
+ }
+}
+
+void initNSS(const SslOptions& options, bool server)
+{
+ SslOptions::global = options;
+ if (options.certPasswordFile.empty()) {
+ PK11_SetPasswordFunc(promptForPassword);
+ } else {
+ PK11_SetPasswordFunc(readPasswordFromFile);
+ }
+ NSS_CHECK(NSS_Init(options.certDbPath.c_str()));
+ if (options.exportPolicy) {
+ NSS_CHECK(NSS_SetExportPolicy());
+ } else {
+ NSS_CHECK(NSS_SetDomesticPolicy());
+ }
+ if (server) {
+ //use defaults for all args, TODO: may want to make this configurable
+ SSL_ConfigServerSessionIDCache(0, 0, 0, 0);
+ }
+}
+
+void shutdownNSS()
+{
+ NSS_Shutdown();
+}
+
+}}} // namespace qpid::sys::ssl
diff --git a/qpid/cpp/src/qpid/sys/ssl/util.h b/qpid/cpp/src/qpid/sys/ssl/util.h
new file mode 100644
index 0000000000..f34adab7be
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/ssl/util.h
@@ -0,0 +1,50 @@
+#ifndef QPID_SYS_SSL_UTIL_H
+#define QPID_SYS_SSL_UTIL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Options.h"
+#include <string>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+struct SslOptions : qpid::Options
+{
+ static SslOptions global;
+
+ std::string certDbPath;
+ std::string certName;
+ std::string certPasswordFile;
+ bool exportPolicy;
+
+ SslOptions();
+ SslOptions& operator=(const SslOptions&);
+};
+
+void initNSS(const SslOptions& options, bool server = false);
+void shutdownNSS();
+
+}}} // namespace qpid::sys::ssl
+
+#endif /*!QPID_SYS_SSL_UTIL_H*/
diff --git a/qpid/cpp/src/qpid/sys/uuid.h b/qpid/cpp/src/qpid/sys/uuid.h
new file mode 100644
index 0000000000..804ab34463
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/uuid.h
@@ -0,0 +1,28 @@
+#ifndef _sys_uuid_h
+#define _sys_uuid_h
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifdef _WIN32
+# include "qpid/sys/windows/uuid.h"
+#else
+# include <uuid/uuid.h>
+#endif /* _WIN32 */
+
+#endif /* _sys_uuid_h */
diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
new file mode 100644
index 0000000000..8d84fdb7b2
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -0,0 +1,755 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/windows/AsynchIoResult.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Time.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/sys/windows/check.h"
+#include "qpid/sys/windows/mingw32_compat.h"
+
+#include <boost/thread/once.hpp>
+
+#include <queue>
+#include <winsock2.h>
+#include <mswsock.h>
+#include <windows.h>
+
+#include <boost/bind.hpp>
+
+namespace {
+
+ typedef qpid::sys::ScopedLock<qpid::sys::Mutex> QLock;
+
+/*
+ * The function pointers for AcceptEx and ConnectEx need to be looked up
+ * at run time. Make sure this is done only once.
+ */
+boost::once_flag lookUpAcceptExOnce = BOOST_ONCE_INIT;
+LPFN_ACCEPTEX fnAcceptEx = 0;
+typedef void (*lookUpFunc)(const qpid::sys::Socket &);
+
+void lookUpAcceptEx() {
+ SOCKET h = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ GUID guidAcceptEx = WSAID_ACCEPTEX;
+ DWORD dwBytes = 0;
+ WSAIoctl(h,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guidAcceptEx,
+ sizeof(guidAcceptEx),
+ &fnAcceptEx,
+ sizeof(fnAcceptEx),
+ &dwBytes,
+ NULL,
+ NULL);
+ closesocket(h);
+ if (fnAcceptEx == 0)
+ throw qpid::Exception(QPID_MSG("Failed to look up AcceptEx"));
+}
+
+}
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+/*
+ * Asynch Acceptor
+ *
+ */
+class AsynchAcceptor : public qpid::sys::AsynchAcceptor {
+
+ friend class AsynchAcceptResult;
+
+public:
+ AsynchAcceptor(const Socket& s, AsynchAcceptor::Callback callback);
+ ~AsynchAcceptor();
+ void start(Poller::shared_ptr poller);
+
+private:
+ void restart(void);
+
+ AsynchAcceptor::Callback acceptedCallback;
+ const Socket& socket;
+};
+
+AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback)
+ : acceptedCallback(callback),
+ socket(s) {
+
+ s.setNonblocking();
+#if (BOOST_VERSION >= 103500) /* boost 1.35 or later reversed the args */
+ boost::call_once(lookUpAcceptExOnce, lookUpAcceptEx);
+#else
+ boost::call_once(lookUpAcceptEx, lookUpAcceptExOnce);
+#endif
+}
+
+AsynchAcceptor::~AsynchAcceptor()
+{
+ socket.close();
+}
+
+void AsynchAcceptor::start(Poller::shared_ptr poller) {
+ PollerHandle ph = PollerHandle(socket);
+ poller->monitorHandle(ph, Poller::INPUT);
+ restart ();
+}
+
+void AsynchAcceptor::restart(void) {
+ DWORD bytesReceived = 0; // Not used, needed for AcceptEx API
+ AsynchAcceptResult *result = new AsynchAcceptResult(acceptedCallback,
+ this,
+ toSocketHandle(socket));
+ BOOL status;
+ status = ::fnAcceptEx(toSocketHandle(socket),
+ toSocketHandle(*result->newSocket),
+ result->addressBuffer,
+ 0,
+ AsynchAcceptResult::SOCKADDRMAXLEN,
+ AsynchAcceptResult::SOCKADDRMAXLEN,
+ &bytesReceived,
+ result->overlapped());
+ QPID_WINDOWS_CHECK_ASYNC_START(status);
+}
+
+
+AsynchAcceptResult::AsynchAcceptResult(AsynchAcceptor::Callback cb,
+ AsynchAcceptor *acceptor,
+ SOCKET listener)
+ : callback(cb), acceptor(acceptor), listener(listener) {
+ newSocket.reset (new Socket());
+}
+
+void AsynchAcceptResult::success(size_t /*bytesTransferred*/) {
+ ::setsockopt (toSocketHandle(*newSocket),
+ SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT,
+ (char*)&listener,
+ sizeof (listener));
+ callback(*(newSocket.release()));
+ acceptor->restart ();
+ delete this;
+}
+
+void AsynchAcceptResult::failure(int /*status*/) {
+ //if (status != WSA_OPERATION_ABORTED)
+ // Can there be anything else? ;
+ delete this;
+}
+
+/*
+ * AsynchConnector does synchronous connects for now... to do asynch the
+ * IocpPoller will need some extension to register an event handle as a
+ * CONNECT-type "direction", the connect completion/result will need an
+ * event handle to associate with the connecting handle. But there's no
+ * time for that right now...
+ */
+class AsynchConnector : public qpid::sys::AsynchConnector {
+private:
+ ConnectedCallback connCallback;
+ FailedCallback failCallback;
+ const Socket& socket;
+ const std::string hostname;
+ const std::string port;
+
+public:
+ AsynchConnector(const Socket& socket,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb = 0);
+ void start(Poller::shared_ptr poller);
+};
+
+AsynchConnector::AsynchConnector(const Socket& sock,
+ const std::string& hname,
+ const std::string& p,
+ ConnectedCallback connCb,
+ FailedCallback failCb) :
+ connCallback(connCb), failCallback(failCb), socket(sock),
+ hostname(hname), port(p)
+{
+}
+
+void AsynchConnector::start(Poller::shared_ptr)
+{
+ try {
+ socket.connect(hostname, port);
+ socket.setNonblocking();
+ connCallback(socket);
+ } catch(std::exception& e) {
+ if (failCallback)
+ failCallback(socket, -1, std::string(e.what()));
+ socket.close();
+ }
+}
+
+} // namespace windows
+
+AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
+ Callback callback)
+{
+ return new windows::AsynchAcceptor(s, callback);
+}
+
+AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s,
+ const std::string& hostname,
+ const std::string& port,
+ ConnectedCallback connCb,
+ FailedCallback failCb)
+{
+ return new windows::AsynchConnector(s,
+ hostname,
+ port,
+ connCb,
+ failCb);
+}
+
+
+/*
+ * Asynch reader/writer
+ */
+
+namespace windows {
+
+class AsynchIO : public qpid::sys::AsynchIO {
+public:
+ AsynchIO(const Socket& s,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0);
+ ~AsynchIO();
+
+ // Methods inherited from qpid::sys::AsynchIO
+
+ /**
+ * Notify the object is should delete itself as soon as possible.
+ */
+ virtual void queueForDeletion();
+
+ /// Take any actions needed to prepare for working with the poller.
+ virtual void start(Poller::shared_ptr poller);
+ virtual void queueReadBuffer(BufferBase* buff);
+ virtual void unread(BufferBase* buff);
+ virtual void queueWrite(BufferBase* buff);
+ virtual void notifyPendingWrite();
+ virtual void queueWriteClose();
+ virtual bool writeQueueEmpty();
+ virtual void startReading();
+ virtual void stopReading();
+ virtual void requestCallback(RequestCallback);
+
+ /**
+ * getQueuedBuffer returns a buffer from the buffer queue, if one is
+ * available.
+ *
+ * @retval Pointer to BufferBase buffer; 0 if none is available.
+ */
+ virtual BufferBase* getQueuedBuffer();
+
+private:
+ ReadCallback readCallback;
+ EofCallback eofCallback;
+ DisconnectCallback disCallback;
+ ClosedCallback closedCallback;
+ BuffersEmptyCallback emptyCallback;
+ IdleCallback idleCallback;
+ const Socket& socket;
+ Poller::shared_ptr poller;
+
+ std::deque<BufferBase*> bufferQueue;
+ std::deque<BufferBase*> writeQueue;
+ /* The MSVC-supplied deque is not thread-safe; keep locks to serialize
+ * access to the buffer queue and write queue.
+ */
+ Mutex bufferQueueLock;
+
+ // Number of outstanding I/O operations.
+ volatile LONG opsInProgress;
+ // Is there a write in progress?
+ volatile bool writeInProgress;
+ // Deletion requested, but there are callbacks in progress.
+ volatile bool queuedDelete;
+ // Socket close requested, but there are operations in progress.
+ volatile bool queuedClose;
+
+private:
+ // Dispatch events that have completed.
+ void notifyEof(void);
+ void notifyDisconnect(void);
+ void notifyClosed(void);
+ void notifyBuffersEmpty(void);
+ void notifyIdle(void);
+
+ /**
+ * Initiate a write of the specified buffer. There's no callback for
+ * write completion to the AsynchIO object.
+ */
+ void startWrite(AsynchIO::BufferBase* buff);
+
+ void close(void);
+
+ /**
+ * readComplete is called when a read request is complete.
+ *
+ * @param result Results of the operation.
+ */
+ void readComplete(AsynchReadResult *result);
+
+ /**
+ * writeComplete is called when a write request is complete.
+ *
+ * @param result Results of the operation.
+ */
+ void writeComplete(AsynchWriteResult *result);
+
+ /**
+ * Queue of completions to run. This queue enforces the requirement
+ * from upper layers that only one thread at a time is allowed to act
+ * on any given connection. Once a thread is busy processing a completion
+ * on this object, other threads that dispatch completions queue the
+ * completions here for the in-progress thread to handle when done.
+ * Thus, any threads can dispatch a completion from the IocpPoller, but
+ * this class ensures that actual processing at the connection level is
+ * only on one thread at a time.
+ */
+ std::queue<AsynchIoResult *> completionQueue;
+ volatile bool working;
+ Mutex completionLock;
+
+ /**
+ * Called when there's a completion to process.
+ */
+ void completion(AsynchIoResult *result);
+};
+
+// This is used to encapsulate pure callbacks into a handle
+class CallbackHandle : public IOHandle {
+public:
+ CallbackHandle(AsynchIoResult::Completer completeCb,
+ AsynchIO::RequestCallback reqCb = 0) :
+ IOHandle(new IOHandlePrivate (INVALID_SOCKET, completeCb, reqCb))
+ {}
+};
+
+AsynchIO::AsynchIO(const Socket& s,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb,
+ BuffersEmptyCallback eCb,
+ IdleCallback iCb) :
+
+ readCallback(rCb),
+ eofCallback(eofCb),
+ disCallback(disCb),
+ closedCallback(cCb),
+ emptyCallback(eCb),
+ idleCallback(iCb),
+ socket(s),
+ opsInProgress(0),
+ writeInProgress(false),
+ queuedDelete(false),
+ queuedClose(false),
+ working(false) {
+}
+
+struct deleter
+{
+ template <typename T>
+ void operator()(T *ptr){ delete ptr;}
+};
+
+AsynchIO::~AsynchIO() {
+ std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter());
+ std::for_each( writeQueue.begin(), writeQueue.end(), deleter());
+}
+
+void AsynchIO::queueForDeletion() {
+ queuedDelete = true;
+ if (opsInProgress > 0) {
+ QPID_LOG(info, "Delete AsynchIO queued; ops in progress");
+ // AsynchIOHandler calls this then deletes itself; don't do any more
+ // callbacks.
+ readCallback = 0;
+ eofCallback = 0;
+ disCallback = 0;
+ closedCallback = 0;
+ emptyCallback = 0;
+ idleCallback = 0;
+ }
+ else {
+ delete this;
+ }
+}
+
+void AsynchIO::start(Poller::shared_ptr poller0) {
+ PollerHandle ph = PollerHandle(socket);
+ poller = poller0;
+ poller->monitorHandle(ph, Poller::INPUT);
+ if (writeQueue.size() > 0) // Already have data queued for write
+ notifyPendingWrite();
+ startReading();
+}
+
+void AsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) {
+ assert(buff);
+ buff->dataStart = 0;
+ buff->dataCount = 0;
+ QLock l(bufferQueueLock);
+ bufferQueue.push_back(buff);
+}
+
+void AsynchIO::unread(AsynchIO::BufferBase* buff) {
+ assert(buff);
+ buff->squish();
+ QLock l(bufferQueueLock);
+ bufferQueue.push_front(buff);
+}
+
+void AsynchIO::queueWrite(AsynchIO::BufferBase* buff) {
+ assert(buff);
+ QLock l(bufferQueueLock);
+ writeQueue.push_back(buff);
+ if (!writeInProgress)
+ notifyPendingWrite();
+}
+
+void AsynchIO::notifyPendingWrite() {
+ // This method is generally called from a processing thread; transfer
+ // work on this to an I/O thread. Much of the upper layer code assumes
+ // that all I/O-related things happen in an I/O thread.
+ if (poller == 0) // Not really going yet...
+ return;
+
+ InterlockedIncrement(&opsInProgress);
+ PollerHandle ph(CallbackHandle(boost::bind(&AsynchIO::completion, this, _1)));
+ poller->monitorHandle(ph, Poller::OUTPUT);
+}
+
+void AsynchIO::queueWriteClose() {
+ queuedClose = true;
+ if (!writeInProgress)
+ notifyPendingWrite();
+}
+
+bool AsynchIO::writeQueueEmpty() {
+ QLock l(bufferQueueLock);
+ return writeQueue.size() == 0;
+}
+
+/*
+ * Initiate a read operation. AsynchIO::readComplete() will be
+ * called when the read is complete and data is available.
+ */
+void AsynchIO::startReading() {
+ if (queuedDelete)
+ return;
+
+ // (Try to) get a buffer; look on the front since there may be an
+ // "unread" one there with data remaining from last time.
+ AsynchIO::BufferBase *buff = 0;
+ {
+ QLock l(bufferQueueLock);
+
+ if (!bufferQueue.empty()) {
+ buff = bufferQueue.front();
+ assert(buff);
+ bufferQueue.pop_front();
+ }
+ }
+ if (buff != 0) {
+ int readCount = buff->byteCount - buff->dataCount;
+ AsynchReadResult *result =
+ new AsynchReadResult(boost::bind(&AsynchIO::completion, this, _1),
+ buff,
+ readCount);
+ DWORD bytesReceived = 0, flags = 0;
+ InterlockedIncrement(&opsInProgress);
+ int status = WSARecv(toSocketHandle(socket),
+ const_cast<LPWSABUF>(result->getWSABUF()), 1,
+ &bytesReceived,
+ &flags,
+ result->overlapped(),
+ 0);
+ if (status != 0) {
+ int error = WSAGetLastError();
+ if (error != WSA_IO_PENDING) {
+ result->failure(error);
+ result = 0; // result is invalid here
+ return;
+ }
+ }
+ // On status 0 or WSA_IO_PENDING, completion will handle the rest.
+ }
+ else {
+ notifyBuffersEmpty();
+ }
+ return;
+}
+
+// stopReading was added to prevent a race condition with read-credit on Linux.
+// It may or may not be required on windows.
+//
+// AsynchIOHandler::readbuff() calls stopReading() inside the same
+// critical section that protects startReading() in
+// AsynchIOHandler::giveReadCredit().
+//
+void AsynchIO::stopReading() {}
+
+// Queue the specified callback for invocation from an I/O thread.
+void AsynchIO::requestCallback(RequestCallback callback) {
+ // This method is generally called from a processing thread; transfer
+ // work on this to an I/O thread. Much of the upper layer code assumes
+ // that all I/O-related things happen in an I/O thread.
+ if (poller == 0) // Not really going yet...
+ return;
+
+ InterlockedIncrement(&opsInProgress);
+ PollerHandle ph(CallbackHandle(
+ boost::bind(&AsynchIO::completion, this, _1),
+ callback));
+ poller->monitorHandle(ph, Poller::INPUT);
+}
+
+/**
+ * Return a queued buffer if there are enough to spare.
+ */
+AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() {
+ QLock l(bufferQueueLock);
+ // Always keep at least one buffer (it might have data that was
+ // "unread" in it).
+ if (bufferQueue.size() <= 1)
+ return 0;
+ BufferBase* buff = bufferQueue.back();
+ assert(buff);
+ bufferQueue.pop_back();
+ return buff;
+}
+
+void AsynchIO::notifyEof(void) {
+ if (eofCallback)
+ eofCallback(*this);
+}
+
+void AsynchIO::notifyDisconnect(void) {
+ if (disCallback)
+ disCallback(*this);
+}
+
+void AsynchIO::notifyClosed(void) {
+ if (closedCallback)
+ closedCallback(*this, socket);
+}
+
+void AsynchIO::notifyBuffersEmpty(void) {
+ if (emptyCallback)
+ emptyCallback(*this);
+}
+
+void AsynchIO::notifyIdle(void) {
+ if (idleCallback)
+ idleCallback(*this);
+}
+
+/*
+ * Asynch reader/writer using overlapped I/O
+ */
+
+void AsynchIO::startWrite(AsynchIO::BufferBase* buff) {
+ writeInProgress = true;
+ InterlockedIncrement(&opsInProgress);
+ AsynchWriteResult *result =
+ new AsynchWriteResult(boost::bind(&AsynchIO::completion, this, _1),
+ buff,
+ buff->dataCount);
+ DWORD bytesSent = 0;
+ int status = WSASend(toSocketHandle(socket),
+ const_cast<LPWSABUF>(result->getWSABUF()), 1,
+ &bytesSent,
+ 0,
+ result->overlapped(),
+ 0);
+ if (status != 0) {
+ int error = WSAGetLastError();
+ if (error != WSA_IO_PENDING) {
+ result->failure(error); // Also decrements in-progress count
+ result = 0; // result is invalid here
+ return;
+ }
+ }
+ // On status 0 or WSA_IO_PENDING, completion will handle the rest.
+ return;
+}
+
+/*
+ * Close the socket and callback to say we've done it
+ */
+void AsynchIO::close(void) {
+ socket.close();
+ notifyClosed();
+}
+
+void AsynchIO::readComplete(AsynchReadResult *result) {
+ int status = result->getStatus();
+ size_t bytes = result->getTransferred();
+ if (status == 0 && bytes > 0) {
+ bool restartRead = true; // May not if receiver doesn't want more
+ if (readCallback)
+ readCallback(*this, result->getBuff());
+ if (restartRead)
+ startReading();
+ }
+ else {
+ // No data read, so put the buffer back. It may be partially filled,
+ // so "unread" it back to the front of the queue.
+ unread(result->getBuff());
+ notifyEof();
+ if (status != 0)
+ {
+ notifyDisconnect();
+ }
+ }
+}
+
+/*
+ * NOTE - this completion is called for completed writes and also when
+ * a write is desired. The difference is in the buff - if a write is desired
+ * the buff is 0.
+ */
+void AsynchIO::writeComplete(AsynchWriteResult *result) {
+ int status = result->getStatus();
+ size_t bytes = result->getTransferred();
+ AsynchIO::BufferBase *buff = result->getBuff();
+ if (buff != 0) {
+ writeInProgress = false;
+ if (status == 0 && bytes > 0) {
+ if (bytes < result->getRequested()) // Still more to go; resubmit
+ startWrite(buff);
+ else
+ queueReadBuffer(buff); // All done; back to the pool
+ }
+ else {
+ // An error... if it's a connection close, ignore it - it will be
+ // noticed and handled on a read completion any moment now.
+ // What to do with real error??? Save the Buffer?
+ }
+ }
+
+ // If there are no writes outstanding, check for more writes to initiate
+ // (either queued or via idle). The opsInProgress count is handled in
+ // completion()
+ if (!writeInProgress) {
+ bool writing = false;
+ {
+ QLock l(bufferQueueLock);
+ if (writeQueue.size() > 0) {
+ buff = writeQueue.front();
+ assert(buff);
+ writeQueue.pop_front();
+ startWrite(buff);
+ writing = true;
+ }
+ }
+ if (!writing && !queuedClose) {
+ notifyIdle();
+ }
+ }
+ return;
+}
+
+void AsynchIO::completion(AsynchIoResult *result) {
+ {
+ ScopedLock<Mutex> l(completionLock);
+ if (working) {
+ completionQueue.push(result);
+ return;
+ }
+
+ // First thread in with something to do; note we're working then keep
+ // handling completions.
+ working = true;
+ while (result != 0) {
+ // New scope to unlock temporarily.
+ {
+ ScopedUnlock<Mutex> ul(completionLock);
+ AsynchReadResult *r = dynamic_cast<AsynchReadResult*>(result);
+ if (r != 0)
+ readComplete(r);
+ else {
+ AsynchWriteResult *w =
+ dynamic_cast<AsynchWriteResult*>(result);
+ if (w != 0)
+ writeComplete(w);
+ else {
+ AsynchCallbackRequest *req =
+ dynamic_cast<AsynchCallbackRequest*>(result);
+ req->reqCallback(*this);
+ }
+ }
+ delete result;
+ result = 0;
+ InterlockedDecrement(&opsInProgress);
+ }
+ // Lock is held again.
+ if (completionQueue.empty())
+ continue;
+ result = completionQueue.front();
+ completionQueue.pop();
+ }
+ working = false;
+ }
+ // Lock released; ok to close if ops are done and close requested.
+ // Layer above will call back to queueForDeletion() if it hasn't
+ // already been done. If it already has, go ahead and delete.
+ if (opsInProgress == 0) {
+ if (queuedClose)
+ // close() may cause a delete; don't trust 'this' on return
+ close();
+ else if (queuedDelete)
+ delete this;
+ }
+}
+
+} // namespace windows
+
+AsynchIO* qpid::sys::AsynchIO::create(const Socket& s,
+ AsynchIO::ReadCallback rCb,
+ AsynchIO::EofCallback eofCb,
+ AsynchIO::DisconnectCallback disCb,
+ AsynchIO::ClosedCallback cCb,
+ AsynchIO::BuffersEmptyCallback eCb,
+ AsynchIO::IdleCallback iCb)
+{
+ return new qpid::sys::windows::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h b/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h
new file mode 100755
index 0000000000..b11324918b
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/AsynchIoResult.h
@@ -0,0 +1,204 @@
+#ifndef _windows_asynchIoResult_h
+#define _windows_asynchIoResult_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/Socket.h"
+#include <memory.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+/*
+ * AsynchIoResult defines the class that receives the result of an
+ * asynchronous I/O operation, either send/recv or accept/connect.
+ *
+ * Operation factories should set one of these up before beginning the
+ * operation. Poller knows how to dispatch completion to this class.
+ * This class must be subclassed for needed operations; this class provides
+ * an interface only and cannot be instantiated.
+ *
+ * This class is tied to Windows; it inherits from OVERLAPPED so that the
+ * IocpPoller can cast OVERLAPPED pointers back to AsynchIoResult and call
+ * the completion handler.
+ */
+class AsynchResult : private OVERLAPPED {
+public:
+ LPOVERLAPPED overlapped(void) { return this; }
+ static AsynchResult* from_overlapped(LPOVERLAPPED ol) {
+ return static_cast<AsynchResult*>(ol);
+ }
+ virtual void success (size_t bytesTransferred) {
+ bytes = bytesTransferred;
+ status = 0;
+ complete();
+ }
+ virtual void failure (int error) {
+ bytes = 0;
+ status = error;
+ complete();
+ }
+ size_t getTransferred(void) const { return bytes; }
+ int getStatus(void) const { return status; }
+
+protected:
+ AsynchResult() : bytes(0), status(0)
+ { memset(overlapped(), 0, sizeof(OVERLAPPED)); }
+ ~AsynchResult() {}
+ virtual void complete(void) = 0;
+
+ size_t bytes;
+ int status;
+};
+
+class AsynchAcceptor;
+
+class AsynchAcceptResult : public AsynchResult {
+
+ friend class AsynchAcceptor;
+
+public:
+ AsynchAcceptResult(qpid::sys::AsynchAcceptor::Callback cb,
+ AsynchAcceptor *acceptor,
+ SOCKET listener);
+ virtual void success (size_t bytesTransferred);
+ virtual void failure (int error);
+
+private:
+ virtual void complete(void) {} // No-op for this class.
+
+ std::auto_ptr<qpid::sys::Socket> newSocket;
+ qpid::sys::AsynchAcceptor::Callback callback;
+ AsynchAcceptor *acceptor;
+ SOCKET listener;
+
+ // AcceptEx needs a place to write the local and remote addresses
+ // when accepting the connection. Place those here; get enough for
+ // IPv6 addresses, even if the socket is IPv4.
+ enum { SOCKADDRMAXLEN = sizeof(sockaddr_in6) + 16,
+ SOCKADDRBUFLEN = 2 * SOCKADDRMAXLEN };
+ char addressBuffer[SOCKADDRBUFLEN];
+};
+
+class AsynchIoResult : public AsynchResult {
+public:
+ typedef boost::function1<void, AsynchIoResult *> Completer;
+
+ virtual ~AsynchIoResult() {}
+ qpid::sys::AsynchIO::BufferBase *getBuff(void) const { return iobuff; }
+ size_t getRequested(void) const { return requested; }
+ const WSABUF *getWSABUF(void) const { return &wsabuf; }
+
+protected:
+ void setBuff (qpid::sys::AsynchIO::BufferBase *buffer) { iobuff = buffer; }
+
+protected:
+ AsynchIoResult(Completer cb,
+ qpid::sys::AsynchIO::BufferBase *buff, size_t length)
+ : completionCallback(cb), iobuff(buff), requested(length) {}
+
+ virtual void complete(void) = 0;
+ WSABUF wsabuf;
+ Completer completionCallback;
+
+private:
+ qpid::sys::AsynchIO::BufferBase *iobuff;
+ size_t requested; // Number of bytes in original I/O request
+};
+
+class AsynchReadResult : public AsynchIoResult {
+
+ // complete() updates buffer then does completion callback.
+ virtual void complete(void) {
+ getBuff()->dataCount += bytes;
+ completionCallback(this);
+ }
+
+public:
+ AsynchReadResult(AsynchIoResult::Completer cb,
+ qpid::sys::AsynchIO::BufferBase *buff,
+ size_t length)
+ : AsynchIoResult(cb, buff, length) {
+ wsabuf.buf = buff->bytes + buff->dataCount;
+ wsabuf.len = length;
+ }
+};
+
+class AsynchWriteResult : public AsynchIoResult {
+
+ // complete() updates buffer then does completion callback.
+ virtual void complete(void) {
+ qpid::sys::AsynchIO::BufferBase *b = getBuff();
+ b->dataStart += bytes;
+ b->dataCount -= bytes;
+ completionCallback(this);
+ }
+
+public:
+ AsynchWriteResult(AsynchIoResult::Completer cb,
+ qpid::sys::AsynchIO::BufferBase *buff,
+ size_t length)
+ : AsynchIoResult(cb, buff, length) {
+ wsabuf.buf = buff ? buff->bytes : 0;
+ wsabuf.len = length;
+ }
+};
+
+class AsynchWriteWanted : public AsynchWriteResult {
+
+ // complete() just does completion callback; no buffers used.
+ virtual void complete(void) {
+ completionCallback(this);
+ }
+
+public:
+ AsynchWriteWanted(AsynchIoResult::Completer cb)
+ : AsynchWriteResult(cb, 0, 0) {
+ wsabuf.buf = 0;
+ wsabuf.len = 0;
+ }
+};
+
+class AsynchCallbackRequest : public AsynchIoResult {
+ // complete() needs to simply call the completionCallback; no buffers.
+ virtual void complete(void) {
+ completionCallback(this);
+ }
+
+public:
+ AsynchCallbackRequest(AsynchIoResult::Completer cb,
+ qpid::sys::AsynchIO::RequestCallback reqCb)
+ : AsynchIoResult(cb, 0, 0), reqCallback(reqCb) {
+ wsabuf.buf = 0;
+ wsabuf.len = 0;
+ }
+
+ qpid::sys::AsynchIO::RequestCallback reqCallback;
+};
+
+}}} // qpid::sys::windows
+
+#endif /*!_windows_asynchIoResult_h*/
diff --git a/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp b/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp
new file mode 100644
index 0000000000..88f1637d48
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/FileSysDir.cpp
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/FileSysDir.h"
+#include "qpid/sys/StrError.h"
+#include "qpid/Exception.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <direct.h>
+#include <errno.h>
+
+namespace qpid {
+namespace sys {
+
+bool FileSysDir::exists (void) const
+{
+ const char *cpath = dirPath.c_str ();
+ struct _stat s;
+ if (::_stat(cpath, &s)) {
+ if (errno == ENOENT) {
+ return false;
+ }
+ throw qpid::Exception (strError(errno) +
+ ": Can't check directory: " + dirPath);
+ }
+ if (s.st_mode & _S_IFDIR)
+ return true;
+ throw qpid::Exception(dirPath + " is not a directory");
+}
+
+void FileSysDir::mkdir(void)
+{
+ if (::_mkdir(dirPath.c_str()) == -1)
+ throw Exception ("Can't create directory: " + dirPath);
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp b/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp
new file mode 100755
index 0000000000..250737cb99
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/IOHandle.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+
+SOCKET toFd(const IOHandlePrivate* h)
+{
+ return h->fd;
+}
+
+IOHandle::IOHandle(IOHandlePrivate* h) :
+ impl(h)
+{}
+
+IOHandle::~IOHandle() {
+ delete impl;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h b/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h
new file mode 100755
index 0000000000..5943db5cc7
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/IoHandlePrivate.h
@@ -0,0 +1,61 @@
+#ifndef _sys_windows_IoHandlePrivate_h
+#define _sys_windows_IoHandlePrivate_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/windows/AsynchIoResult.h"
+#include "qpid/CommonImportExport.h"
+
+#include <winsock2.h>
+
+namespace qpid {
+namespace sys {
+
+// Private fd related implementation details
+// There should be either a valid socket handle or a completer callback.
+// Handle is used to associate with poller's iocp; completer is used to
+// inject a completion that will very quickly trigger a callback to the
+// completer from an I/O thread. If the callback mechanism is used, there
+// can be a RequestCallback set - this carries the callback object through
+// from AsynchIO::requestCallback() through to the I/O completion processing.
+class IOHandlePrivate {
+ friend QPID_COMMON_EXTERN SOCKET toSocketHandle(const Socket& s);
+ static IOHandlePrivate* getImpl(const IOHandle& h);
+
+public:
+ IOHandlePrivate(SOCKET f = INVALID_SOCKET,
+ windows::AsynchIoResult::Completer cb = 0,
+ AsynchIO::RequestCallback reqCallback = 0) :
+ fd(f), event(cb), cbRequest(reqCallback)
+ {}
+
+ SOCKET fd;
+ windows::AsynchIoResult::Completer event;
+ AsynchIO::RequestCallback cbRequest;
+};
+
+QPID_COMMON_EXTERN SOCKET toSocketHandle(const Socket& s);
+
+}}
+
+#endif /* _sys_windows_IoHandlePrivate_h */
diff --git a/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp b/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp
new file mode 100755
index 0000000000..1805dd2cd8
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Dispatcher.h"
+
+#include "qpid/sys/windows/AsynchIoResult.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+#include "qpid/sys/windows/check.h"
+
+#include <winsock2.h>
+#include <windows.h>
+
+#include <assert.h>
+#include <vector>
+#include <exception>
+
+namespace qpid {
+namespace sys {
+
+class PollerHandlePrivate {
+ friend class Poller;
+ friend class PollerHandle;
+
+ SOCKET fd;
+ windows::AsynchIoResult::Completer cb;
+ AsynchIO::RequestCallback cbRequest;
+
+ PollerHandlePrivate(SOCKET f,
+ windows::AsynchIoResult::Completer cb0 = 0,
+ AsynchIO::RequestCallback rcb = 0)
+ : fd(f), cb(cb0), cbRequest(rcb)
+ {
+ }
+
+};
+
+PollerHandle::PollerHandle(const IOHandle& h) :
+ impl(new PollerHandlePrivate(toSocketHandle(static_cast<const Socket&>(h)), h.impl->event, h.impl->cbRequest))
+{}
+
+PollerHandle::~PollerHandle() {
+ delete impl;
+}
+
+/**
+ * Concrete implementation of Poller to use the Windows I/O Completion
+ * port (IOCP) facility.
+ */
+class PollerPrivate {
+ friend class Poller;
+
+ const HANDLE iocp;
+
+ // The number of threads running the event loop.
+ volatile LONG threadsRunning;
+
+ // Shutdown request is handled by setting isShutdown and injecting a
+ // well-formed completion event into the iocp.
+ bool isShutdown;
+
+ PollerPrivate() :
+ iocp(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)),
+ threadsRunning(0),
+ isShutdown(false) {
+ QPID_WINDOWS_CHECK_NULL(iocp);
+ }
+
+ ~PollerPrivate() {
+ // It's probably okay to ignore any errors here as there can't be
+ // data loss
+ ::CloseHandle(iocp);
+ }
+};
+
+void Poller::shutdown() {
+ // Allow sloppy code to shut us down more than once.
+ if (impl->isShutdown)
+ return;
+ ULONG_PTR key = 1; // Tell wait() it's a shutdown, not I/O
+ PostQueuedCompletionStatus(impl->iocp, 0, key, 0);
+}
+
+bool Poller::hasShutdown()
+{
+ return impl->isShutdown;
+}
+
+bool Poller::interrupt(PollerHandle&) {
+ return false; // There's no concept of a registered handle.
+}
+
+void Poller::run() {
+ do {
+ Poller::Event event = this->wait();
+
+ // Handle shutdown
+ switch (event.type) {
+ case Poller::SHUTDOWN:
+ return;
+ break;
+ case Poller::INVALID: // On any type of success or fail completion
+ break;
+ default:
+ // This should be impossible
+ assert(false);
+ }
+ } while (true);
+}
+
+void Poller::monitorHandle(PollerHandle& handle, Direction dir) {
+ HANDLE h = (HANDLE)(handle.impl->fd);
+ if (h != INVALID_HANDLE_VALUE) {
+ HANDLE iocpHandle = ::CreateIoCompletionPort (h, impl->iocp, 0, 0);
+ QPID_WINDOWS_CHECK_NULL(iocpHandle);
+ }
+ else {
+ // INPUT is used to request a callback; OUTPUT to request a write
+ assert(dir == Poller::INPUT || dir == Poller::OUTPUT);
+
+ if (dir == Poller::OUTPUT) {
+ windows::AsynchWriteWanted *result =
+ new windows::AsynchWriteWanted(handle.impl->cb);
+ PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped());
+ }
+ else {
+ windows::AsynchCallbackRequest *result =
+ new windows::AsynchCallbackRequest(handle.impl->cb,
+ handle.impl->cbRequest);
+ PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped());
+ }
+ }
+}
+
+// All no-ops...
+void Poller::unmonitorHandle(PollerHandle& /*handle*/, Direction /*dir*/) {}
+void Poller::registerHandle(PollerHandle& /*handle*/) {}
+void Poller::unregisterHandle(PollerHandle& /*handle*/) {}
+
+Poller::Event Poller::wait(Duration timeout) {
+ DWORD timeoutMs = 0;
+ DWORD numTransferred = 0;
+ ULONG_PTR completionKey = 0;
+ OVERLAPPED *overlapped = 0;
+ windows::AsynchResult *result = 0;
+
+ // Wait for either an I/O operation to finish (thus signaling the
+ // IOCP handle) or a shutdown request to be made (thus signaling the
+ // shutdown event).
+ if (timeout == TIME_INFINITE)
+ timeoutMs = INFINITE;
+ else
+ timeoutMs = static_cast<DWORD>(timeout / TIME_MSEC);
+
+ InterlockedIncrement(&impl->threadsRunning);
+ bool goodOp = ::GetQueuedCompletionStatus (impl->iocp,
+ &numTransferred,
+ &completionKey,
+ &overlapped,
+ timeoutMs);
+ LONG remainingThreads = InterlockedDecrement(&impl->threadsRunning);
+ if (goodOp) {
+ // Dequeued a successful completion. If it's a posted packet from
+ // shutdown() the overlapped ptr is 0 and key is 1. Else downcast
+ // the OVERLAPPED pointer to an AsynchIoResult and call the
+ // completion handler.
+ if (overlapped == 0 && completionKey == 1) {
+ // If there are other threads still running this wait, re-post
+ // the completion.
+ if (remainingThreads > 0)
+ PostQueuedCompletionStatus(impl->iocp, 0, completionKey, 0);
+ return Event(0, SHUTDOWN);
+ }
+
+ result = windows::AsynchResult::from_overlapped(overlapped);
+ result->success (static_cast<size_t>(numTransferred));
+ }
+ else {
+ if (overlapped != 0) {
+ // Dequeued a completion for a failed operation. Downcast back
+ // to the result object and inform it that the operation failed.
+ DWORD status = ::GetLastError();
+ result = windows::AsynchResult::from_overlapped(overlapped);
+ result->failure (static_cast<int>(status));
+ }
+ }
+ return Event(0, INVALID); // TODO - this may need to be changed.
+
+}
+
+// Concrete constructors
+Poller::Poller() :
+ impl(new PollerPrivate())
+{}
+
+Poller::~Poller() {
+ delete impl;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/windows/LockFile.cpp b/qpid/cpp/src/qpid/sys/windows/LockFile.cpp
new file mode 100755
index 0000000000..048c2d5b18
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/LockFile.cpp
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright (c) 2008 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.
+ *
+ */
+
+#include "qpid/sys/LockFile.h"
+#include "qpid/sys/windows/check.h"
+
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+
+class LockFilePrivate {
+ friend class LockFile;
+
+ HANDLE fd;
+
+public:
+ LockFilePrivate(HANDLE f) : fd(f) {}
+};
+
+LockFile::LockFile(const std::string& path_, bool create)
+ : path(path_), created(create) {
+
+ HANDLE h = ::CreateFile(path.c_str(),
+ create ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ 0, /* Default security */
+ create ? OPEN_ALWAYS : OPEN_EXISTING,
+ FILE_FLAG_DELETE_ON_CLOSE, /* Delete file when closed */
+ NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ throw qpid::Exception(path + ": " + qpid::sys::strError(GetLastError()));
+
+ // Lock up to 4Gb
+ if (!::LockFile(h, 0, 0, 0xffffffff, 0))
+ throw qpid::Exception(path + ": " + qpid::sys::strError(GetLastError()));
+ impl.reset(new LockFilePrivate(h));
+}
+
+LockFile::~LockFile() {
+ if (impl) {
+ if (impl->fd != INVALID_HANDLE_VALUE) {
+ ::UnlockFile(impl->fd, 0, 0, 0xffffffff, 0);
+ ::CloseHandle(impl->fd);
+ }
+ }
+}
+
+}} /* namespace qpid::sys */
diff --git a/qpid/cpp/src/qpid/sys/windows/PipeHandle.cpp b/qpid/cpp/src/qpid/sys/windows/PipeHandle.cpp
new file mode 100755
index 0000000000..062458ae5f
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/PipeHandle.cpp
@@ -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.
+//
+
+#include "qpid/sys/PipeHandle.h"
+#include "qpid/sys/windows/check.h"
+#include <winsock2.h>
+
+namespace qpid {
+namespace sys {
+
+PipeHandle::PipeHandle(bool nonBlocking) {
+
+ SOCKET listener, pair[2];
+ struct sockaddr_in addr;
+ int err;
+ int addrlen = sizeof(addr);
+ pair[0] = pair[1] = INVALID_SOCKET;
+ if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ throw QPID_WINDOWS_ERROR(WSAGetLastError());
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = 0;
+
+ err = bind(listener, (const struct sockaddr*) &addr, sizeof(addr));
+ if (err == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ closesocket(listener);
+ throw QPID_WINDOWS_ERROR(err);
+ }
+
+ err = getsockname(listener, (struct sockaddr*) &addr, &addrlen);
+ if (err == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ closesocket(listener);
+ throw QPID_WINDOWS_ERROR(err);
+ }
+
+ try {
+ if (listen(listener, 1) == SOCKET_ERROR)
+ throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ if ((pair[0] = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ if (connect(pair[0], (const struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
+ throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ if ((pair[1] = accept(listener, NULL, NULL)) == INVALID_SOCKET)
+ throw QPID_WINDOWS_ERROR(WSAGetLastError());
+
+ closesocket(listener);
+ writeFd = pair[0];
+ readFd = pair[1];
+ }
+ catch (...) {
+ closesocket(listener);
+ if (pair[0] != INVALID_SOCKET)
+ closesocket(pair[0]);
+ throw;
+ }
+
+ // Set the socket to non-blocking
+ if (nonBlocking) {
+ unsigned long nonblock = 1;
+ ioctlsocket(readFd, FIONBIO, &nonblock);
+ }
+}
+
+PipeHandle::~PipeHandle() {
+ closesocket(readFd);
+ closesocket(writeFd);
+}
+
+int PipeHandle::read(void* buf, size_t bufSize) {
+ return ::recv(readFd, (char *)buf, bufSize, 0);
+}
+
+int PipeHandle::write(const void* buf, size_t bufSize) {
+ return ::send(writeFd, (const char *)buf, bufSize, 0);
+}
+
+int PipeHandle::getReadHandle() {
+ return readFd;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp b/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp
new file mode 100644
index 0000000000..6a1d9045b4
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp
@@ -0,0 +1,114 @@
+#ifndef QPID_SYS_WINDOWS_POLLABLECONDITION_CPP
+#define QPID_SYS_WINDOWS_POLLABLECONDITION_CPP
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/windows/AsynchIoResult.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+
+#include <boost/bind.hpp>
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+
+// PollableConditionPrivate will reuse the IocpPoller's ability to queue
+// a completion to the IOCP and have it dispatched to the completer callback
+// noted in the IOHandlePrivate when the request is queued. The
+// AsynchCallbackRequest object is not really used - we already have the
+// desired callback for the user of PollableCondition.
+class PollableConditionPrivate : private IOHandle {
+ friend class PollableCondition;
+
+private:
+ PollableConditionPrivate(const sys::PollableCondition::Callback& cb,
+ sys::PollableCondition& parent,
+ const boost::shared_ptr<sys::Poller>& poller);
+ ~PollableConditionPrivate();
+
+ void poke();
+ void dispatch(windows::AsynchIoResult *result);
+
+private:
+ PollableCondition::Callback cb;
+ PollableCondition& parent;
+ boost::shared_ptr<sys::Poller> poller;
+ LONG isSet;
+};
+
+PollableConditionPrivate::PollableConditionPrivate(const sys::PollableCondition::Callback& cb,
+ sys::PollableCondition& parent,
+ const boost::shared_ptr<sys::Poller>& poller)
+ : IOHandle(new sys::IOHandlePrivate(INVALID_SOCKET,
+ boost::bind(&PollableConditionPrivate::dispatch, this, _1))),
+ cb(cb), parent(parent), poller(poller), isSet(0)
+{
+}
+
+PollableConditionPrivate::~PollableConditionPrivate()
+{
+}
+
+void PollableConditionPrivate::poke()
+{
+ // monitorHandle will queue a completion for the IOCP; when it's handled, a
+ // poller thread will call back to dispatch() below.
+ PollerHandle ph(*this);
+ poller->monitorHandle(ph, Poller::INPUT);
+}
+
+void PollableConditionPrivate::dispatch(windows::AsynchIoResult *result)
+{
+ delete result; // Poller::monitorHandle() allocates this
+ cb(parent);
+ if (isSet)
+ poke();
+}
+
+ /* PollableCondition */
+
+PollableCondition::PollableCondition(const Callback& cb,
+ const boost::shared_ptr<sys::Poller>& poller)
+ : impl(new PollableConditionPrivate(cb, *this, poller))
+{
+}
+
+PollableCondition::~PollableCondition()
+{
+ delete impl;
+}
+
+void PollableCondition::set() {
+ // Add one to the set count and poke it to provoke a callback
+ ::InterlockedIncrement(&impl->isSet);
+ impl->poke();
+}
+
+void PollableCondition::clear() {
+ ::InterlockedExchange(&impl->isSet, 0);
+}
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_WINDOWS_POLLABLECONDITION_CPP*/
diff --git a/qpid/cpp/src/qpid/sys/windows/Shlib.cpp b/qpid/cpp/src/qpid/sys/windows/Shlib.cpp
new file mode 100644
index 0000000000..ba18747eb4
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/Shlib.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Shlib.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/windows/check.h"
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+
+void Shlib::load(const char* name) {
+ HMODULE h = LoadLibrary(name);
+ if (h == NULL) {
+ throw QPID_WINDOWS_ERROR(GetLastError());
+ }
+ handle = static_cast<void*>(h);
+}
+
+void Shlib::unload() {
+ if (handle) {
+ if (FreeLibrary(static_cast<HMODULE>(handle)) == 0) {
+ throw QPID_WINDOWS_ERROR(GetLastError());
+ }
+ handle = 0;
+ }
+}
+
+void* Shlib::getSymbol(const char* name) {
+ // Double cast avoids warning about casting function pointer to object
+ void *sym = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(GetProcAddress(static_cast<HMODULE>(handle), name)));
+ if (sym == NULL)
+ throw QPID_WINDOWS_ERROR(GetLastError());
+ return sym;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/Socket.cpp b/qpid/cpp/src/qpid/sys/windows/Socket.cpp
new file mode 100755
index 0000000000..baa80f04e0
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/Socket.cpp
@@ -0,0 +1,289 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Ensure we get all of winsock2.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+#include "qpid/sys/windows/check.h"
+#include "qpid/sys/Time.h"
+
+#include <cstdlib>
+#include <string.h>
+
+#include <winsock2.h>
+
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+// Need to initialize WinSock. Ideally, this would be a singleton or embedded
+// in some one-time initialization function. I tried boost singleton and could
+// not get it to compile (and others located in google had the same problem).
+// So, this simple static with an interlocked increment will do for known
+// use cases at this time. Since this will only shut down winsock at process
+// termination, there may be some problems with client programs that also
+// expect to load and unload winsock, but we'll see...
+// If someone does get an easy-to-use singleton sometime, converting to it
+// may be preferable.
+
+namespace {
+
+static LONG volatile initialized = 0;
+
+class WinSockSetup {
+ // : public boost::details::pool::singleton_default<WinSockSetup> {
+
+public:
+ WinSockSetup() {
+ LONG timesEntered = InterlockedIncrement(&initialized);
+ if (timesEntered > 1)
+ return;
+ err = 0;
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ /* Request WinSock 2.2 */
+ wVersionRequested = MAKEWORD(2, 2);
+ err = WSAStartup(wVersionRequested, &wsaData);
+ }
+
+ ~WinSockSetup() {
+ WSACleanup();
+ }
+
+public:
+ int error(void) const { return err; }
+
+protected:
+ DWORD err;
+};
+
+static WinSockSetup setup;
+
+} /* namespace */
+
+namespace qpid {
+namespace sys {
+
+namespace {
+
+std::string getName(SOCKET fd, bool local)
+{
+ sockaddr_in name; // big enough for any socket address
+ socklen_t namelen = sizeof(name);
+ if (local) {
+ QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen));
+ } else {
+ QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen));
+ }
+
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
+ dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ return std::string(dispName) + ":" + std::string(servName);
+}
+} // namespace
+
+Socket::Socket() :
+ IOHandle(new IOHandlePrivate),
+ nonblocking(false),
+ nodelay(false)
+{
+ SOCKET& socket = impl->fd;
+ if (socket != INVALID_SOCKET) Socket::close();
+ SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0);
+ if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ socket = s;
+}
+
+Socket::Socket(IOHandlePrivate* h) :
+ IOHandle(h),
+ nonblocking(false),
+ nodelay(false)
+{}
+
+void
+Socket::createSocket(const SocketAddress& sa) const
+{
+ SOCKET& socket = impl->fd;
+ if (socket != INVALID_SOCKET) Socket::close();
+
+ SOCKET s = ::socket (getAddrInfo(sa).ai_family,
+ getAddrInfo(sa).ai_socktype,
+ 0);
+ if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ socket = s;
+
+ try {
+ if (nonblocking) setNonblocking();
+ if (nodelay) setTcpNoDelay();
+ } catch (std::exception&) {
+ closesocket(s);
+ socket = INVALID_SOCKET;
+ throw;
+ }
+}
+
+void Socket::setNonblocking() const {
+ u_long nonblock = 1;
+ QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock));
+}
+
+void Socket::connect(const std::string& host, const std::string& port) const
+{
+ SocketAddress sa(host, port);
+ connect(sa);
+}
+
+void
+Socket::connect(const SocketAddress& addr) const
+{
+ peername = addr.asString(false);
+
+ const SOCKET& socket = impl->fd;
+ const addrinfo *addrs = &(getAddrInfo(addr));
+ int error = 0;
+ WSASetLastError(0);
+ while (addrs != 0) {
+ if ((::connect(socket, addrs->ai_addr, addrs->ai_addrlen) == 0) ||
+ (WSAGetLastError() == WSAEWOULDBLOCK))
+ break;
+ // Error... save this error code and see if there are other address
+ // to try before throwing the exception.
+ error = WSAGetLastError();
+ addrs = addrs->ai_next;
+ }
+ if (error)
+ throw qpid::Exception(QPID_MSG(strError(error) << ": " << peername));
+}
+
+void
+Socket::close() const
+{
+ SOCKET& socket = impl->fd;
+ if (socket == INVALID_SOCKET) return;
+ QPID_WINSOCK_CHECK(closesocket(socket));
+ socket = INVALID_SOCKET;
+}
+
+
+int Socket::write(const void *buf, size_t count) const
+{
+ const SOCKET& socket = impl->fd;
+ int sent = ::send(socket, (const char *)buf, count, 0);
+ if (sent == SOCKET_ERROR)
+ return -1;
+ return sent;
+}
+
+int Socket::read(void *buf, size_t count) const
+{
+ const SOCKET& socket = impl->fd;
+ int received = ::recv(socket, (char *)buf, count, 0);
+ if (received == SOCKET_ERROR)
+ return -1;
+ return received;
+}
+
+int Socket::listen(const std::string&, const std::string& port, int backlog) const
+{
+ const SOCKET& socket = impl->fd;
+ BOOL yes=1;
+ QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)));
+ struct sockaddr_in name;
+ memset(&name, 0, sizeof(name));
+ name.sin_family = AF_INET;
+ name.sin_port = htons(boost::lexical_cast<uint16_t>(port));
+ name.sin_addr.s_addr = 0;
+ if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) == SOCKET_ERROR)
+ throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(WSAGetLastError())));
+ if (::listen(socket, backlog) == SOCKET_ERROR)
+ throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(WSAGetLastError())));
+
+ socklen_t namelen = sizeof(name);
+ QPID_WINSOCK_CHECK(::getsockname(socket, (struct sockaddr*)&name, &namelen));
+ return ntohs(name.sin_port);
+}
+
+Socket* Socket::accept() const
+{
+ SOCKET afd = ::accept(impl->fd, 0, 0);
+ if (afd != INVALID_SOCKET)
+ return new Socket(new IOHandlePrivate(afd));
+ else if (WSAGetLastError() == EAGAIN)
+ return 0;
+ else throw QPID_WINDOWS_ERROR(WSAGetLastError());
+}
+
+std::string Socket::getPeerAddress() const
+{
+ if (peername.empty())
+ peername = getName(impl->fd, false);
+ return peername;
+}
+
+std::string Socket::getLocalAddress() const
+{
+ if (localname.empty())
+ localname = getName(impl->fd, true);
+ return localname;
+}
+
+int Socket::getError() const
+{
+ int result;
+ socklen_t rSize = sizeof (result);
+
+ QPID_WINSOCK_CHECK(::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize));
+ return result;
+}
+
+void Socket::setTcpNoDelay() const
+{
+ int flag = 1;
+ int result = setsockopt(impl->fd,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (char *)&flag,
+ sizeof(flag));
+ QPID_WINSOCK_CHECK(result);
+ nodelay = true;
+}
+
+inline IOHandlePrivate* IOHandlePrivate::getImpl(const qpid::sys::IOHandle &h)
+{
+ return h.impl;
+}
+
+SOCKET toSocketHandle(const Socket& s)
+{
+ return IOHandlePrivate::getImpl(s)->fd;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp
new file mode 100644
index 0000000000..ac43cd2d23
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/SocketAddress.cpp
@@ -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.
+ *
+ */
+
+// Ensure we get all of winsock2.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include "qpid/sys/SocketAddress.h"
+
+#include "qpid/sys/windows/check.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <string.h>
+
+namespace qpid {
+namespace sys {
+
+SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) :
+ host(host0),
+ port(port0),
+ addrInfo(0)
+{
+ ::addrinfo hints;
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; // In order to allow AF_INET6 we'd have to change createTcp() as well
+ hints.ai_socktype = SOCK_STREAM;
+
+ const char* node = 0;
+ if (host.empty()) {
+ hints.ai_flags |= AI_PASSIVE;
+ } else {
+ node = host.c_str();
+ }
+ const char* service = port.empty() ? "0" : port.c_str();
+
+ int n = ::getaddrinfo(node, service, &hints, &addrInfo);
+ if (n != 0)
+ throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n)));
+}
+
+SocketAddress::~SocketAddress()
+{
+ ::freeaddrinfo(addrInfo);
+}
+
+std::string SocketAddress::asString(bool) const
+{
+ return host + ":" + port;
+}
+
+const ::addrinfo& getAddrInfo(const SocketAddress& sa)
+{
+ return *sa.addrInfo;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
new file mode 100644
index 0000000000..11a3389e45
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
@@ -0,0 +1,661 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "SslAsynchIO.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Time.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/sys/windows/check.h"
+
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+#include <queue>
+#include <boost/bind.hpp>
+
+namespace {
+
+ /*
+ * To make the SSL encryption more efficient, set up a new BufferBase
+ * that leaves room for the SSL header to be prepended and the SSL
+ * trailer to be appended.
+ *
+ * This works by accepting a properly formed BufferBase, remembering it,
+ * and resetting the members of this struct to reflect the reserved
+ * header and trailer areas. It's only needed for giving buffers up to
+ * the frame layer for writing into.
+ */
+ struct SslIoBuff : public qpid::sys::AsynchIO::BufferBase {
+ std::auto_ptr<qpid::sys::AsynchIO::BufferBase> aioBuff;
+
+ SslIoBuff (qpid::sys::AsynchIO::BufferBase *base,
+ const SecPkgContext_StreamSizes &sizes)
+ : qpid::sys::AsynchIO::BufferBase(&base->bytes[sizes.cbHeader],
+ std::min(base->byteCount - sizes.cbHeader - sizes.cbTrailer,
+ sizes.cbMaximumMessage)),
+ aioBuff(base)
+ {}
+
+ ~SslIoBuff() {}
+ qpid::sys::AsynchIO::BufferBase* release() { return aioBuff.release(); }
+ };
+}
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb,
+ BuffersEmptyCallback eCb,
+ IdleCallback iCb,
+ NegotiateDoneCallback nCb) :
+ credHandle(hCred),
+ aio(0),
+ state(Negotiating),
+ readCallback(rCb),
+ idleCallback(iCb),
+ negotiateDoneCallback(nCb),
+ callbacksInProgress(0),
+ queuedDelete(false),
+ leftoverPlaintext(0)
+{
+ SecInvalidateHandle(&ctxtHandle);
+ peerAddress = s.getPeerAddress();
+ aio = qpid::sys::AsynchIO::create(s,
+ boost::bind(&SslAsynchIO::sslDataIn, this, _1, _2),
+ eofCb,
+ disCb,
+ cCb,
+ eCb,
+ boost::bind(&SslAsynchIO::idle, this, _1));
+}
+
+SslAsynchIO::~SslAsynchIO() {
+ if (leftoverPlaintext) {
+ delete leftoverPlaintext;
+ leftoverPlaintext = 0;
+ }
+}
+
+void SslAsynchIO::queueForDeletion() {
+ // This method effectively disconnects the layer above; pass it on the
+ // AsynchIO and delete.
+ aio->queueForDeletion();
+ queuedDelete = true;
+ if (!callbacksInProgress)
+ delete this;
+}
+
+void SslAsynchIO::start(qpid::sys::Poller::shared_ptr poller) {
+ aio->start(poller);
+ startNegotiate();
+}
+
+void SslAsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) {
+ aio->queueReadBuffer(buff);
+}
+
+void SslAsynchIO::unread(AsynchIO::BufferBase* buff) {
+ // This is plaintext data being given back for more. Since it's already
+ // decrypted, don't give it back to the aio layer; keep it to append
+ // any new data for the upper layer.
+ assert(buff);
+ buff->squish();
+ assert(leftoverPlaintext == 0);
+ leftoverPlaintext = buff;
+}
+
+void SslAsynchIO::queueWrite(AsynchIO::BufferBase* buff) {
+ // @@TODO: Need to delay the write if the session is renegotiating.
+
+ // Should not have gotten here without an SslIoBuff. This assert is
+ // primarily to catch any stray cases where write is called with a buffer
+ // not obtained via getQueuedBuffer.
+ SslIoBuff *sslBuff = dynamic_cast<SslIoBuff*>(buff);
+ assert(sslBuff != 0);
+
+ // Encrypt and hand off to the io layer. Remember that the upper layer's
+ // encoding was working on, and adjusting counts for, the SslIoBuff.
+ // Update the count of the original BufferBase before handing off to
+ // the I/O layer.
+ buff = sslBuff->release();
+ SecBuffer buffs[4];
+ buffs[0].cbBuffer = schSizes.cbHeader;
+ buffs[0].BufferType = SECBUFFER_STREAM_HEADER;
+ buffs[0].pvBuffer = buff->bytes; // This space was left by SslIoBuff
+ buffs[1].cbBuffer = sslBuff->dataCount;
+ buffs[1].BufferType = SECBUFFER_DATA;
+ buffs[1].pvBuffer = sslBuff->bytes;
+ buffs[2].cbBuffer = schSizes.cbTrailer;
+ buffs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ buffs[2].pvBuffer = &sslBuff->bytes[sslBuff->dataCount];
+ buffs[3].cbBuffer = 0;
+ buffs[3].BufferType = SECBUFFER_EMPTY;
+ buffs[3].pvBuffer = 0;
+ SecBufferDesc buffDesc;
+ buffDesc.ulVersion = SECBUFFER_VERSION;
+ buffDesc.cBuffers = 4;
+ buffDesc.pBuffers = buffs;
+ SECURITY_STATUS status = ::EncryptMessage(&ctxtHandle, 0, &buffDesc, 0);
+
+ // EncryptMessage encrypts the data in place. The header and trailer
+ // areas were left previously and must now be included in the updated
+ // count of bytes to write to the peer.
+ delete sslBuff;
+ buff->dataCount = buffs[0].cbBuffer + buffs[1].cbBuffer + buffs[2].cbBuffer;
+ aio->queueWrite(buff);
+}
+
+void SslAsynchIO::notifyPendingWrite() {
+ aio->notifyPendingWrite();
+}
+
+void SslAsynchIO::queueWriteClose() {
+ if (state == Negotiating) {
+ // Never got going, so don't bother trying to close SSL down orderly.
+ state = ShuttingDown;
+ aio->queueWriteClose();
+ return;
+ }
+
+ state = ShuttingDown;
+
+ DWORD shutdown = SCHANNEL_SHUTDOWN;
+ SecBuffer shutBuff;
+ shutBuff.cbBuffer = sizeof(DWORD);
+ shutBuff.BufferType = SECBUFFER_TOKEN;
+ shutBuff.pvBuffer = &shutdown;
+ SecBufferDesc desc;
+ desc.ulVersion = SECBUFFER_VERSION;
+ desc.cBuffers = 1;
+ desc.pBuffers = &shutBuff;
+ ::ApplyControlToken(&ctxtHandle, &desc);
+ negotiateStep(0);
+ // When the shutdown sequence is done, negotiateDone() will handle
+ // shutting down aio.
+}
+
+bool SslAsynchIO::writeQueueEmpty() {
+ return aio->writeQueueEmpty();
+}
+
+/*
+ * Initiate a read operation. AsynchIO::readComplete() will be
+ * called when the read is complete and data is available.
+ */
+void SslAsynchIO::startReading() {
+ aio->startReading();
+}
+
+void SslAsynchIO::stopReading() {
+ aio->stopReading();
+}
+
+// Queue the specified callback for invocation from an I/O thread.
+void SslAsynchIO::requestCallback(RequestCallback callback) {
+ aio->requestCallback(callback);
+}
+
+/**
+ * Return a queued buffer read to put new data in for writing.
+ * This method ALWAYS returns a SslIoBuff reflecting a BufferBase from
+ * the aio layer that has header and trailer space reserved.
+ */
+AsynchIO::BufferBase* SslAsynchIO::getQueuedBuffer() {
+ SslIoBuff *sslBuff = 0;
+ BufferBase* buff = aio->getQueuedBuffer();
+ if (buff == 0)
+ return 0;
+
+ sslBuff = new SslIoBuff(buff, schSizes);
+ return sslBuff;
+}
+
+unsigned int SslAsynchIO::getSslKeySize() {
+ SecPkgContext_KeyInfo info;
+ memset(&info, 0, sizeof(info));
+ ::QueryContextAttributes(&ctxtHandle, SECPKG_ATTR_KEY_INFO, &info);
+ return info.KeySize;
+}
+
+void SslAsynchIO::negotiationDone() {
+ switch(state) {
+ case Negotiating:
+ ::QueryContextAttributes(&ctxtHandle,
+ SECPKG_ATTR_STREAM_SIZES,
+ &schSizes);
+ state = Running;
+ if (negotiateDoneCallback)
+ negotiateDoneCallback(SEC_E_OK);
+ break;
+ case Redo:
+ state = Running;
+ break;
+ case ShuttingDown:
+ aio->queueWriteClose();
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void SslAsynchIO::negotiationFailed(SECURITY_STATUS status) {
+ QPID_LOG(notice, "SSL negotiation failed to " << peerAddress << ": " <<
+ qpid::sys::strError(status));
+ if (negotiateDoneCallback)
+ negotiateDoneCallback(status);
+ else
+ queueWriteClose();
+}
+
+void SslAsynchIO::sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff) {
+ if (state != Running) {
+ negotiateStep(buff);
+ return;
+ }
+
+ // Decrypt the buffer; if there's legit data, pass it on through.
+ // However, it's also possible that the peer hasn't supplied enough
+ // data yet, or the session needs to be renegotiated, or the session
+ // is ending.
+ SecBuffer recvBuffs[4];
+ recvBuffs[0].cbBuffer = buff->dataCount;
+ recvBuffs[0].BufferType = SECBUFFER_DATA;
+ recvBuffs[0].pvBuffer = &buff->bytes[buff->dataStart];
+ recvBuffs[1].BufferType = SECBUFFER_EMPTY;
+ recvBuffs[2].BufferType = SECBUFFER_EMPTY;
+ recvBuffs[3].BufferType = SECBUFFER_EMPTY;
+ SecBufferDesc buffDesc;
+ buffDesc.ulVersion = SECBUFFER_VERSION;
+ buffDesc.cBuffers = 4;
+ buffDesc.pBuffers = recvBuffs;
+ SECURITY_STATUS status = ::DecryptMessage(&ctxtHandle, &buffDesc, 0, NULL);
+ if (status != SEC_E_OK) {
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Give the partially filled buffer back and get more data
+ a.unread(buff);
+ }
+ else {
+ // Don't need this any more...
+ a.queueReadBuffer(buff);
+
+ if (status == SEC_I_RENEGOTIATE) {
+ state = Redo;
+ negotiateStep(0);
+ }
+ else if (status == SEC_I_CONTEXT_EXPIRED) {
+ queueWriteClose();
+ }
+ else {
+ throw QPID_WINDOWS_ERROR(status);
+ }
+ }
+ return;
+ }
+
+ // All decrypted and verified... continue with AMQP. The recvBuffs have
+ // been set up by DecryptMessage to demarcate the SSL header, data, and
+ // trailer, as well as any extra data left over. Walk through and find
+ // that info, adjusting the buff data accordingly to reflect only the
+ // actual decrypted data.
+ // If there's extra data, copy that out to a new buffer and run through
+ // this method again.
+ BufferBase *extraBuff = 0;
+ for (int i = 0; i < 4; i++) {
+ switch (recvBuffs[i].BufferType) {
+ case SECBUFFER_STREAM_HEADER:
+ buff->dataStart += recvBuffs[i].cbBuffer;
+ // Fall through - also don't count these bytes as data
+ case SECBUFFER_STREAM_TRAILER:
+ buff->dataCount -= recvBuffs[i].cbBuffer;
+ break;
+ case SECBUFFER_EXTRA:
+ // Very important to get this buffer from the downstream aio.
+ // The ones constructed from the local getQueuedBuffer() are
+ // restricted size for encrypting. However, data coming up from
+ // TCP may have a bunch of SSL segments coalesced and be much
+ // larger than the maximum single SSL segment.
+ extraBuff = a.getQueuedBuffer();
+ if (0 == extraBuff)
+ throw QPID_WINDOWS_ERROR(WSAENOBUFS);
+ memmove(extraBuff->bytes,
+ recvBuffs[i].pvBuffer,
+ recvBuffs[i].cbBuffer);
+ extraBuff->dataCount = recvBuffs[i].cbBuffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Since we've already taken (possibly) all the available bytes from the
+ // aio layer, need to be sure that everything that's processable is
+ // processed before returning back to aio. It could be that any
+ // leftoverPlaintext data plus new buff data won't fit in one buffer, so
+ // need to keep going around the input processing loop until either
+ // all the bytes are gone, or there's less than a full frame remaining
+ // (so we can count on more bytes being on the way via aio).
+ do {
+ BufferBase *temp = 0;
+ // Now that buff reflects only decrypted data, see if there was any
+ // partial data left over from last time. If so, append this new
+ // data to that and release the current buff back to aio. Assume that
+ // leftoverPlaintext was squished so the data starts at 0.
+ if (leftoverPlaintext != 0) {
+ // There is leftover data; append all the new data that will fit.
+ int32_t count = buff->dataCount;
+ if (leftoverPlaintext->dataCount + count > leftoverPlaintext->byteCount)
+ count = (leftoverPlaintext->byteCount - leftoverPlaintext->dataCount);
+ ::memmove(&leftoverPlaintext->bytes[leftoverPlaintext->dataCount],
+ &buff->bytes[buff->dataStart],
+ count);
+ leftoverPlaintext->dataCount += count;
+ buff->dataCount -= count;
+ buff->dataStart += count;
+ if (buff->dataCount == 0) {
+ a.queueReadBuffer(buff);
+ buff = 0;
+ }
+ // Prepare to pass the buffer up. Beware that the read callback
+ // may do an unread(), so move the leftoverPlaintext pointer
+ // out of the way. It also may release the buffer back to aio,
+ // so in either event, the pointer passed to the callback is not
+ // valid on return.
+ temp = leftoverPlaintext;
+ leftoverPlaintext = 0;
+ }
+ else {
+ temp = buff;
+ buff = 0;
+ }
+ if (readCallback) {
+ // The callback guard here is to prevent an upcall from deleting
+ // this out from under us via queueForDeletion().
+ ++callbacksInProgress;
+ readCallback(*this, temp);
+ --callbacksInProgress;
+ }
+ else
+ a.queueReadBuffer(temp); // What else can we do with this???
+ } while (buff != 0);
+
+ // Ok, the current decrypted data is done. If there was any extra data,
+ // go back and handle that.
+ if (extraBuff != 0)
+ sslDataIn(a, extraBuff);
+
+ // If the upper layer queued for delete, do that now that all the
+ // callbacks are done.
+ if (queuedDelete && callbacksInProgress == 0)
+ delete this;
+}
+
+void SslAsynchIO::idle(qpid::sys::AsynchIO&) {
+ // Don't relay idle indication to layer above until SSL session is up.
+ if (state == Running) {
+ state = Running;
+ if (idleCallback)
+ idleCallback(*this);
+ }
+}
+
+ /**************************************************/
+
+ClientSslAsynchIO::ClientSslAsynchIO(const std::string& brokerHost,
+ const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb,
+ BuffersEmptyCallback eCb,
+ IdleCallback iCb,
+ NegotiateDoneCallback nCb) :
+ SslAsynchIO(s, hCred, rCb, eofCb, disCb, cCb, eCb, iCb, nCb),
+ serverHost(brokerHost)
+{
+}
+
+void ClientSslAsynchIO::startNegotiate() {
+ // SEC_CHAR is non-const, so do all the typing here.
+ SEC_CHAR *host = const_cast<SEC_CHAR *>(serverHost.c_str());
+
+ // Need a buffer to receive the token to send to the server.
+ BufferBase *buff = aio->getQueuedBuffer();
+ ULONG ctxtRequested = ISC_REQ_STREAM;
+ ULONG ctxtAttrs;
+ // sendBuffs gets information to forward to the peer.
+ SecBuffer sendBuffs[2];
+ sendBuffs[0].cbBuffer = buff->byteCount;
+ sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+ sendBuffs[0].pvBuffer = buff->bytes;
+ sendBuffs[1].cbBuffer = 0;
+ sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+ sendBuffs[1].pvBuffer = 0;
+ SecBufferDesc sendBuffDesc;
+ sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+ sendBuffDesc.cBuffers = 2;
+ sendBuffDesc.pBuffers = sendBuffs;
+ SECURITY_STATUS status = ::InitializeSecurityContext(&credHandle,
+ NULL,
+ host,
+ ctxtRequested,
+ 0,
+ 0,
+ NULL,
+ 0,
+ &ctxtHandle,
+ &sendBuffDesc,
+ &ctxtAttrs,
+ NULL);
+ if (status == SEC_I_CONTINUE_NEEDED) {
+ buff->dataCount = sendBuffs[0].cbBuffer;
+ aio->queueWrite(buff);
+ }
+}
+
+void ClientSslAsynchIO::negotiateStep(BufferBase* buff) {
+ // SEC_CHAR is non-const, so do all the typing here.
+ SEC_CHAR *host = const_cast<SEC_CHAR *>(serverHost.c_str());
+ ULONG ctxtRequested = ISC_REQ_STREAM;
+ ULONG ctxtAttrs;
+
+ // tokenBuffs describe the buffer that's coming in. It should have
+ // a token from the SSL server.
+ SecBuffer tokenBuffs[2];
+ tokenBuffs[0].cbBuffer = buff ? buff->dataCount : 0;
+ tokenBuffs[0].BufferType = SECBUFFER_TOKEN;
+ tokenBuffs[0].pvBuffer = buff ? buff->bytes : 0;
+ tokenBuffs[1].cbBuffer = 0;
+ tokenBuffs[1].BufferType = SECBUFFER_EMPTY;
+ tokenBuffs[1].pvBuffer = 0;
+ SecBufferDesc tokenBuffDesc;
+ tokenBuffDesc.ulVersion = SECBUFFER_VERSION;
+ tokenBuffDesc.cBuffers = 2;
+ tokenBuffDesc.pBuffers = tokenBuffs;
+
+ // Need a buffer to receive any token to send back to the server.
+ BufferBase *sendbuff = aio->getQueuedBuffer();
+ // sendBuffs gets information to forward to the peer.
+ SecBuffer sendBuffs[2];
+ sendBuffs[0].cbBuffer = sendbuff->byteCount;
+ sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+ sendBuffs[0].pvBuffer = sendbuff->bytes;
+ sendBuffs[1].cbBuffer = 0;
+ sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+ sendBuffs[1].pvBuffer = 0;
+ SecBufferDesc sendBuffDesc;
+ sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+ sendBuffDesc.cBuffers = 2;
+ sendBuffDesc.pBuffers = sendBuffs;
+
+ SECURITY_STATUS status = ::InitializeSecurityContext(&credHandle,
+ &ctxtHandle,
+ host,
+ ctxtRequested,
+ 0,
+ 0,
+ &tokenBuffDesc,
+ 0,
+ NULL,
+ &sendBuffDesc,
+ &ctxtAttrs,
+ NULL);
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Not enough - get more data from the server then try again.
+ aio->unread(buff);
+ aio->queueReadBuffer(sendbuff); // Don't need this one for now...
+ return;
+ }
+ // Done with the buffer that came in...
+ if (buff)
+ aio->queueReadBuffer(buff);
+ if (status == SEC_I_CONTINUE_NEEDED) {
+ sendbuff->dataCount = sendBuffs[0].cbBuffer;
+ aio->queueWrite(sendbuff);
+ return;
+ }
+ // Nothing to send back to the server...
+ aio->queueReadBuffer(sendbuff);
+ // SEC_I_CONTEXT_EXPIRED means session stop complete; SEC_E_OK can be
+ // either session stop or negotiation done (session up).
+ if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED)
+ negotiationDone();
+ else
+ negotiationFailed(status);
+}
+
+/*************************************************/
+
+ServerSslAsynchIO::ServerSslAsynchIO(bool clientMustAuthenticate,
+ const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb,
+ BuffersEmptyCallback eCb,
+ IdleCallback iCb,
+ NegotiateDoneCallback nCb) :
+ SslAsynchIO(s, hCred, rCb, eofCb, disCb, cCb, eCb, iCb, nCb),
+ clientAuth(clientMustAuthenticate)
+{
+}
+
+void ServerSslAsynchIO::startNegotiate() {
+ // Nothing... need the client to send a token first.
+}
+
+void ServerSslAsynchIO::negotiateStep(BufferBase* buff) {
+ ULONG ctxtRequested = ASC_REQ_STREAM;
+ if (clientAuth)
+ ctxtRequested |= ASC_REQ_MUTUAL_AUTH;
+ ULONG ctxtAttrs;
+
+ // tokenBuffs describe the buffer that's coming in. It should have
+ // a token from the SSL server except if shutting down or renegotiating.
+ SecBuffer tokenBuffs[2];
+ tokenBuffs[0].cbBuffer = buff ? buff->dataCount : 0;
+ tokenBuffs[0].BufferType = SECBUFFER_TOKEN;
+ tokenBuffs[0].pvBuffer = buff ? buff->bytes : 0;
+ tokenBuffs[1].cbBuffer = 0;
+ tokenBuffs[1].BufferType = SECBUFFER_EMPTY;
+ tokenBuffs[1].pvBuffer = 0;
+ SecBufferDesc tokenBuffDesc;
+ tokenBuffDesc.ulVersion = SECBUFFER_VERSION;
+ tokenBuffDesc.cBuffers = 2;
+ tokenBuffDesc.pBuffers = tokenBuffs;
+
+ // Need a buffer to receive any token to send back to the server.
+ BufferBase *sendbuff = aio->getQueuedBuffer();
+ // sendBuffs gets information to forward to the peer.
+ SecBuffer sendBuffs[2];
+ sendBuffs[0].cbBuffer = sendbuff->byteCount;
+ sendBuffs[0].BufferType = SECBUFFER_TOKEN;
+ sendBuffs[0].pvBuffer = sendbuff->bytes;
+ sendBuffs[1].cbBuffer = 0;
+ sendBuffs[1].BufferType = SECBUFFER_EMPTY;
+ sendBuffs[1].pvBuffer = 0;
+ SecBufferDesc sendBuffDesc;
+ sendBuffDesc.ulVersion = SECBUFFER_VERSION;
+ sendBuffDesc.cBuffers = 2;
+ sendBuffDesc.pBuffers = sendBuffs;
+ PCtxtHandle ctxtHandlePtr = (SecIsValidHandle(&ctxtHandle)) ? &ctxtHandle : 0;
+ SECURITY_STATUS status = ::AcceptSecurityContext(&credHandle,
+ ctxtHandlePtr,
+ &tokenBuffDesc,
+ ctxtRequested,
+ 0,
+ &ctxtHandle,
+ &sendBuffDesc,
+ &ctxtAttrs,
+ NULL);
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Not enough - get more data from the server then try again.
+ if (buff)
+ aio->unread(buff);
+ aio->queueReadBuffer(sendbuff); // Don't need this one for now...
+ return;
+ }
+ // Done with the buffer that came in...
+ if (buff)
+ aio->queueReadBuffer(buff);
+ if (status == SEC_I_CONTINUE_NEEDED) {
+ sendbuff->dataCount = sendBuffs[0].cbBuffer;
+ aio->queueWrite(sendbuff);
+ return;
+ }
+ // There may have been a token generated; if so, send it to the client.
+ if (sendBuffs[0].cbBuffer > 0) {
+ sendbuff->dataCount = sendBuffs[0].cbBuffer;
+ aio->queueWrite(sendbuff);
+ }
+ else
+ // Nothing to send back to the server...
+ aio->queueReadBuffer(sendbuff);
+
+ // SEC_I_CONTEXT_EXPIRED means session stop complete; SEC_E_OK can be
+ // either session stop or negotiation done (session up).
+ if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
+ if (clientAuth)
+ QPID_LOG(warning, "DID WE CHECK FOR CLIENT AUTH???");
+
+ negotiationDone();
+ }
+ else {
+ negotiationFailed(status);
+ }
+}
+
+}}} // namespace qpid::sys::windows
diff --git a/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h
new file mode 100644
index 0000000000..3cdf2c8f08
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.h
@@ -0,0 +1,191 @@
+#ifndef _sys_windows_SslAsynchIO
+#define _sys_windows_SslAsynchIO
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/AsynchIO.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <windows.h>
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+namespace qpid {
+namespace sys {
+namespace windows {
+
+class Socket;
+class Poller;
+
+/*
+ * SSL/Schannel shim between the frame-handling and AsynchIO layers.
+ * SslAsynchIO creates a regular AsynchIO object to handle I/O and this class
+ * gets involved for SSL negotiations and encrypt/decrypt. The details of
+ * how this all works are invisible to the layers on either side. The only
+ * change from normal AsynchIO usage is that there's an extra callback
+ * from SslAsynchIO to indicate that the initial session negotiation is
+ * complete.
+ *
+ * The details of session negotiation are different for client and server
+ * SSL roles. These differences are handled by deriving separate client
+ * and server role classes.
+ */
+class SslAsynchIO : public qpid::sys::AsynchIO {
+public:
+ typedef boost::function1<void, SECURITY_STATUS> NegotiateDoneCallback;
+
+ SslAsynchIO(const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0,
+ NegotiateDoneCallback nCb = 0);
+ ~SslAsynchIO();
+
+ virtual void queueForDeletion();
+
+ virtual void start(qpid::sys::Poller::shared_ptr poller);
+ virtual void queueReadBuffer(BufferBase* buff);
+ virtual void unread(BufferBase* buff);
+ virtual void queueWrite(BufferBase* buff);
+ virtual void notifyPendingWrite();
+ virtual void queueWriteClose();
+ virtual bool writeQueueEmpty();
+ virtual void startReading();
+ virtual void stopReading();
+ virtual void requestCallback(RequestCallback);
+ virtual BufferBase* getQueuedBuffer();
+
+ QPID_COMMON_EXTERN unsigned int getSslKeySize();
+
+protected:
+ CredHandle credHandle;
+
+ // AsynchIO layer below that's actually doing the I/O
+ qpid::sys::AsynchIO *aio;
+
+ // Track what the state of the SSL session is. Have to know when it's
+ // time to notify the upper layer that the session is up, and also to
+ // know when it's not legit to pass data through to either side.
+ enum { Negotiating, Running, Redo, ShuttingDown } state;
+ bool sessionUp;
+ CtxtHandle ctxtHandle;
+ TimeStamp credExpiry;
+
+ // Client- and server-side SSL subclasses implement these to do the
+ // proper negotiation steps. negotiateStep() is called with a buffer
+ // just received from the peer.
+ virtual void startNegotiate() = 0;
+ virtual void negotiateStep(BufferBase *buff) = 0;
+
+ // The negotiating steps call one of these when it's finalized:
+ void negotiationDone();
+ void negotiationFailed(SECURITY_STATUS status);
+
+private:
+ // These are callbacks from AsynchIO to here.
+ void sslDataIn(qpid::sys::AsynchIO& a, BufferBase *buff);
+ void idle(qpid::sys::AsynchIO&);
+
+ // These callbacks are to the layer above.
+ ReadCallback readCallback;
+ IdleCallback idleCallback;
+ NegotiateDoneCallback negotiateDoneCallback;
+ volatile unsigned int callbacksInProgress; // >0 if w/in callbacks
+ volatile bool queuedDelete;
+
+ // Address of peer, in case it's needed for logging.
+ std::string peerAddress;
+
+ // Partial buffer of decrypted plaintext given back by the layer above.
+ AsynchIO::BufferBase *leftoverPlaintext;
+
+ SecPkgContext_StreamSizes schSizes;
+};
+
+/*
+ * SSL/Schannel client-side shim between the frame-handling and AsynchIO
+ * layers.
+ */
+class ClientSslAsynchIO : public SslAsynchIO {
+public:
+ // Args same as for SslIoShim, with the addition of brokerHost which is
+ // the expected SSL name of the server.
+ QPID_COMMON_EXTERN ClientSslAsynchIO(const std::string& brokerHost,
+ const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0,
+ NegotiateDoneCallback nCb = 0);
+
+private:
+ std::string serverHost;
+
+ // Client- and server-side SSL subclasses implement these to do the
+ // proper negotiation steps. negotiateStep() is called with a buffer
+ // just received from the peer.
+ void startNegotiate();
+ void negotiateStep(BufferBase *buff);
+};
+/*
+ * SSL/Schannel server-side shim between the frame-handling and AsynchIO
+ * layers.
+ */
+class ServerSslAsynchIO : public SslAsynchIO {
+public:
+ QPID_COMMON_EXTERN ServerSslAsynchIO(bool clientMustAuthenticate,
+ const qpid::sys::Socket& s,
+ CredHandle hCred,
+ ReadCallback rCb,
+ EofCallback eofCb,
+ DisconnectCallback disCb,
+ ClosedCallback cCb = 0,
+ BuffersEmptyCallback eCb = 0,
+ IdleCallback iCb = 0,
+ NegotiateDoneCallback nCb = 0);
+
+private:
+ bool clientAuth;
+
+ // Client- and server-side SSL subclasses implement these to do the
+ // proper negotiation steps. negotiateStep() is called with a buffer
+ // just received from the peer.
+ void startNegotiate();
+ void negotiateStep(BufferBase *buff);
+};
+
+}}} // namespace qpid::sys::windows
+
+#endif // _sys_windows_SslAsynchIO
diff --git a/qpid/cpp/src/qpid/sys/windows/StrError.cpp b/qpid/cpp/src/qpid/sys/windows/StrError.cpp
new file mode 100755
index 0000000000..546d399d16
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/StrError.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/StrError.h"
+#include <string>
+#include <string.h>
+#include <windows.h>
+
+namespace qpid {
+namespace sys {
+
+std::string strError(int err) {
+ const size_t bufsize = 512;
+ char buf[bufsize];
+ buf[0] = 0;
+ if (0 == FormatMessage (FORMAT_MESSAGE_MAX_WIDTH_MASK
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ err,
+ 0, // Default language
+ buf,
+ bufsize,
+ 0))
+ {
+#ifdef _MSC_VER
+ strerror_s(buf, bufsize, err);
+#else
+ return std::string(strerror(err));
+#endif
+ }
+ return std::string(buf);
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp
new file mode 100755
index 0000000000..4da440bdd4
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp
@@ -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.
+ *
+ */
+
+/* GetNativeSystemInfo call requires _WIN32_WINNT 0x0501 or higher */
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0501
+#endif
+
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/SystemInfo.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <tlhelp32.h>
+
+#ifndef HOST_NAME_MAX
+# define HOST_NAME_MAX 256
+#endif
+
+namespace qpid {
+namespace sys {
+
+long SystemInfo::concurrency() {
+ SYSTEM_INFO sys_info;
+ ::GetSystemInfo (&sys_info);
+ long activeProcessors = 0;
+ DWORD_PTR mask = sys_info.dwActiveProcessorMask;
+ while (mask != 0) {
+ if (mask & 1)
+ ++activeProcessors;
+ mask >>= 1;
+ }
+ return activeProcessors;
+}
+
+bool SystemInfo::getLocalHostname (Address &address) {
+ char name[HOST_NAME_MAX];
+ if (::gethostname(name, sizeof(name)) != 0) {
+ errno = WSAGetLastError();
+ return false;
+ }
+ address.host = name;
+ return true;
+}
+
+static const std::string LOCALHOST("127.0.0.1");
+static const std::string TCP("tcp");
+
+void SystemInfo::getLocalIpAddresses (uint16_t port,
+ std::vector<Address> &addrList) {
+ enum { MAX_URL_INTERFACES = 100 };
+
+ SOCKET s = socket (PF_INET, SOCK_STREAM, 0);
+ if (s != INVALID_SOCKET) {
+ INTERFACE_INFO interfaces[MAX_URL_INTERFACES];
+ DWORD filledBytes = 0;
+ WSAIoctl (s,
+ SIO_GET_INTERFACE_LIST,
+ 0,
+ 0,
+ interfaces,
+ sizeof (interfaces),
+ &filledBytes,
+ 0,
+ 0);
+ unsigned int interfaceCount = filledBytes / sizeof (INTERFACE_INFO);
+ for (unsigned int i = 0; i < interfaceCount; ++i) {
+ if (interfaces[i].iiFlags & IFF_UP) {
+ std::string addr(inet_ntoa(interfaces[i].iiAddress.AddressIn.sin_addr));
+ if (addr != LOCALHOST)
+ addrList.push_back(Address(TCP, addr, port));
+ }
+ }
+ closesocket (s);
+ }
+}
+
+void SystemInfo::getSystemId (std::string &osName,
+ std::string &nodeName,
+ std::string &release,
+ std::string &version,
+ std::string &machine)
+{
+ osName = "Microsoft Windows";
+
+ char node[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD nodelen = MAX_COMPUTERNAME_LENGTH + 1;
+ GetComputerName (node, &nodelen);
+ nodeName = node;
+
+ OSVERSIONINFOEX vinfo;
+ vinfo.dwOSVersionInfoSize = sizeof(vinfo);
+ GetVersionEx ((OSVERSIONINFO *)&vinfo);
+
+ SYSTEM_INFO sinfo;
+ GetNativeSystemInfo(&sinfo);
+
+ switch(vinfo.dwMajorVersion) {
+ case 5:
+ switch(vinfo.dwMinorVersion) {
+ case 0:
+ release ="2000";
+ break;
+ case 1:
+ release = "XP";
+ break;
+ case 2:
+ if (sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
+ sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
+ release = "XP-64";
+ else
+ release = "Server 2003";
+ break;
+ default:
+ release = "Windows";
+ }
+ break;
+ case 6:
+ if (vinfo.wProductType == VER_NT_SERVER)
+ release = "Server 2008";
+ else
+ release = "Vista";
+ break;
+ default:
+ release = "Microsoft Windows";
+ }
+ version = vinfo.szCSDVersion;
+
+ switch(sinfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ machine = "x86-64";
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ machine = "IA64";
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ machine = "x86";
+ break;
+ default:
+ machine = "unknown";
+ break;
+ }
+}
+
+uint32_t SystemInfo::getProcessId()
+{
+ return static_cast<uint32_t>(::GetCurrentProcessId());
+}
+
+uint32_t SystemInfo::getParentProcessId()
+{
+ // Only want info for the current process, so ask for something specific.
+ // The module info won't be used here but it keeps the snapshot limited to
+ // the current process so a search through all processes is not needed.
+ HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
+ if (snap == INVALID_HANDLE_VALUE)
+ return 0;
+ PROCESSENTRY32 entry;
+ entry.dwSize = sizeof(entry);
+ if (!Process32First(snap, &entry))
+ entry.th32ParentProcessID = 0;
+ CloseHandle(snap);
+ return static_cast<uint32_t>(entry.th32ParentProcessID);
+}
+
+std::string SystemInfo::getProcessName()
+{
+ std::string name;
+
+ // Only want info for the current process, so ask for something specific.
+ // The module info won't be used here but it keeps the snapshot limited to
+ // the current process so a search through all processes is not needed.
+ HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
+ if (snap == INVALID_HANDLE_VALUE)
+ return name;
+ PROCESSENTRY32 entry;
+ entry.dwSize = sizeof(entry);
+ if (!Process32First(snap, &entry))
+ entry.szExeFile[0] = '\0';
+ CloseHandle(snap);
+ name = entry.szExeFile;
+ return name;
+}
+
+}} // namespace qpid::sys
diff --git a/qpid/cpp/src/qpid/sys/windows/Thread.cpp b/qpid/cpp/src/qpid/sys/windows/Thread.cpp
new file mode 100755
index 0000000000..583a9613a3
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/Thread.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/windows/check.h"
+
+#include <process.h>
+#include <windows.h>
+
+namespace {
+unsigned __stdcall runRunnable(void* p)
+{
+ static_cast<qpid::sys::Runnable*>(p)->run();
+ _endthreadex(0);
+ return 0;
+}
+}
+
+namespace qpid {
+namespace sys {
+
+class ThreadPrivate {
+ friend class Thread;
+
+ HANDLE threadHandle;
+ unsigned threadId;
+
+ ThreadPrivate(Runnable* runnable) {
+ uintptr_t h = _beginthreadex(0,
+ 0,
+ runRunnable,
+ runnable,
+ 0,
+ &threadId);
+ QPID_WINDOWS_CHECK_CRT_NZ(h);
+ threadHandle = reinterpret_cast<HANDLE>(h);
+ }
+
+ ThreadPrivate()
+ : threadHandle(GetCurrentThread()), threadId(GetCurrentThreadId()) {}
+};
+
+Thread::Thread() {}
+
+Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {}
+
+Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {}
+
+Thread::operator bool() {
+ return impl;
+}
+
+bool Thread::operator==(const Thread& t) const {
+ return impl->threadId == t.impl->threadId;
+}
+
+bool Thread::operator!=(const Thread& t) const {
+ return !(*this==t);
+}
+
+void Thread::join() {
+ if (impl) {
+ DWORD status = WaitForSingleObject (impl->threadHandle, INFINITE);
+ QPID_WINDOWS_CHECK_NOT(status, WAIT_FAILED);
+ CloseHandle (impl->threadHandle);
+ impl->threadHandle = 0;
+ }
+}
+
+unsigned long Thread::logId() {
+ return GetCurrentThreadId();
+}
+
+/* static */
+Thread Thread::current() {
+ Thread t;
+ t.impl.reset(new ThreadPrivate());
+ return t;
+}
+
+}} /* qpid::sys */
diff --git a/qpid/cpp/src/qpid/sys/windows/Time.cpp b/qpid/cpp/src/qpid/sys/windows/Time.cpp
new file mode 100644
index 0000000000..25c50819cd
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/Time.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/sys/Time.h"
+#include <ostream>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/thread/thread_time.hpp>
+#include <windows.h>
+
+using namespace boost::posix_time;
+
+namespace {
+
+// High-res timing support. This will display times since program start,
+// more or less. Keep track of the start value and the conversion factor to
+// seconds.
+bool timeInitialized = false;
+LARGE_INTEGER start;
+double freq = 1.0;
+
+}
+
+namespace qpid {
+namespace sys {
+
+AbsTime::AbsTime(const AbsTime& t, const Duration& d) {
+ if (d == Duration::max()) {
+ timepoint = ptime(max_date_time);
+ }
+ else {
+ time_duration td = microseconds(d.nanosecs / 1000);
+ timepoint = t.timepoint + td;
+ }
+}
+
+AbsTime AbsTime::FarFuture() {
+ AbsTime ff;
+ ptime maxd(max_date_time);
+ ff.timepoint = maxd;
+ return ff;
+}
+
+AbsTime AbsTime::Epoch() {
+ AbsTime time_epoch;
+ time_epoch.timepoint = boost::posix_time::from_time_t(0);
+ return time_epoch;
+}
+
+AbsTime AbsTime::now() {
+ AbsTime time_now;
+ time_now.timepoint = boost::get_system_time();
+ return time_now;
+}
+
+Duration::Duration(const AbsTime& start, const AbsTime& finish) {
+ time_duration d = finish.timepoint - start.timepoint;
+ nanosecs = d.total_nanoseconds();
+}
+
+std::ostream& operator<<(std::ostream& o, const Duration& d) {
+ return o << int64_t(d) << "ns";
+}
+
+std::ostream& operator<<(std::ostream& o, const AbsTime& t) {
+ std::string time_string = to_simple_string(t.timepoint);
+ return o << time_string;
+}
+
+
+void sleep(int secs) {
+ ::Sleep(secs * 1000);
+}
+
+void usleep(uint64_t usecs) {
+ DWORD msecs = usecs / 1000;
+ if (msecs == 0)
+ msecs = 1;
+ ::Sleep(msecs);
+}
+
+void outputFormattedNow(std::ostream& o) {
+ ::time_t rawtime;
+ ::tm timeinfo;
+ char time_string[100];
+
+ ::time( &rawtime );
+#ifdef _MSC_VER
+ ::localtime_s(&timeinfo, &rawtime);
+#else
+ timeinfo = *(::localtime(&rawtime));
+#endif
+ ::strftime(time_string, 100,
+ "%Y-%m-%d %H:%M:%S",
+ &timeinfo);
+ o << time_string << " ";
+}
+
+void outputHiresNow(std::ostream& o) {
+ if (!timeInitialized) {
+ start.QuadPart = 0;
+ LARGE_INTEGER iFreq;
+ iFreq.QuadPart = 1;
+ QueryPerformanceCounter(&start);
+ QueryPerformanceFrequency(&iFreq);
+ freq = static_cast<double>(iFreq.QuadPart);
+ timeInitialized = true;
+ }
+ LARGE_INTEGER iNow;
+ iNow.QuadPart = 0;
+ QueryPerformanceCounter(&iNow);
+ iNow.QuadPart -= start.QuadPart;
+ if (iNow.QuadPart < 0)
+ iNow.QuadPart = 0;
+ double now = static_cast<double>(iNow.QuadPart);
+ now /= freq; // now is seconds after this
+ o << std::fixed << std::setprecision(8) << std::setw(16) << std::setfill('0') << now << "s ";
+}
+}}
diff --git a/qpid/cpp/src/qpid/sys/windows/mingw32_compat.h b/qpid/cpp/src/qpid/sys/windows/mingw32_compat.h
new file mode 100644
index 0000000000..51f613cc25
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/mingw32_compat.h
@@ -0,0 +1,39 @@
+#ifndef _sys_windows_mingw32_compat
+#define _sys_windows_mingw32_compat
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifdef WIN32
+#ifndef _MSC_VER
+
+//
+// The following definitions for extension function GUIDs and signatures are taken from
+// MswSock.h in the Windows32 SDK. These rightfully belong in the mingw32 version of
+// mswsock.h, but are not included presently.
+//
+
+#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
+typedef BOOL (PASCAL *LPFN_ACCEPTEX)(SOCKET,SOCKET,PVOID,DWORD,DWORD,DWORD,LPDWORD,LPOVERLAPPED);
+
+#endif
+#endif
+
+#endif
diff --git a/qpid/cpp/src/qpid/sys/windows/uuid.cpp b/qpid/cpp/src/qpid/sys/windows/uuid.cpp
new file mode 100644
index 0000000000..3316ecbc00
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/uuid.cpp
@@ -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.
+ *
+ */
+
+#include <rpc.h>
+#ifdef uuid_t /* Done in rpcdce.h */
+# undef uuid_t
+#endif
+
+#include "qpid/sys/windows/uuid.h"
+
+#include <string.h>
+
+void uuid_clear (uuid_t uu) {
+ UuidCreateNil (reinterpret_cast<UUID*>(uu));
+}
+
+void uuid_copy (uuid_t dst, const uuid_t src) {
+ memcpy (dst, src, qpid::sys::UuidSize);
+}
+
+void uuid_generate (uuid_t out) {
+ UuidCreate (reinterpret_cast<UUID*>(out));
+}
+
+int uuid_is_null (const uuid_t uu) {
+ RPC_STATUS unused;
+ return UuidIsNil ((UUID*)uu, &unused);
+}
+
+int uuid_parse (const char *in, uuid_t uu) {
+ return UuidFromString ((unsigned char*)in, (UUID*)uu) == RPC_S_OK ? 0 : -1;
+}
+
+void uuid_unparse (const uuid_t uu, char *out) {
+ unsigned char *formatted;
+ if (UuidToString((UUID*)uu, &formatted) == RPC_S_OK) {
+#ifdef _MSC_VER
+ strncpy_s (out, 36+1, (char*)formatted, _TRUNCATE);
+#else
+ strncpy (out, (char*)formatted, 36+1);
+#endif
+ RpcStringFree(&formatted);
+ }
+}
+
+int uuid_compare (const uuid_t a, const uuid_t b) {
+ RPC_STATUS unused;
+ return !UuidEqual((UUID*)a, (UUID*)b, &unused);
+}
diff --git a/qpid/cpp/src/qpid/sys/windows/uuid.h b/qpid/cpp/src/qpid/sys/windows/uuid.h
new file mode 100644
index 0000000000..8ab132e9ce
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/windows/uuid.h
@@ -0,0 +1,39 @@
+#ifndef _sys_windows_uuid_h
+#define _sys_windows_uuid_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/types/ImportExport.h"
+#include <qpid/sys/IntegerTypes.h>
+
+namespace qpid { namespace sys { const size_t UuidSize = 16; }}
+typedef uint8_t uuid_t[qpid::sys::UuidSize];
+
+QPID_TYPES_EXTERN void uuid_clear (uuid_t uu);
+QPID_TYPES_EXTERN void uuid_copy (uuid_t dst, const uuid_t src);
+QPID_TYPES_EXTERN void uuid_generate (uuid_t out);
+QPID_TYPES_EXTERN int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0
+QPID_TYPES_EXTERN int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1
+QPID_TYPES_EXTERN void uuid_unparse (const uuid_t uu, char *out);
+QPID_TYPES_EXTERN int uuid_compare (const uuid_t a, const uuid_t b);
+
+#endif /*!_sys_windows_uuid_h*/
diff --git a/qpid/cpp/src/qpid/types/Exception.cpp b/qpid/cpp/src/qpid/types/Exception.cpp
new file mode 100644
index 0000000000..71390e6abd
--- /dev/null
+++ b/qpid/cpp/src/qpid/types/Exception.cpp
@@ -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.
+ *
+ */
+#include "qpid/types/Exception.h"
+
+namespace qpid {
+namespace types {
+
+Exception::Exception(const std::string& msg) throw() : message(msg) {}
+Exception::~Exception() throw() {}
+const char* Exception::what() const throw() { return message.c_str(); }
+
+}} // namespace qpid::types
diff --git a/qpid/cpp/src/qpid/types/Uuid.cpp b/qpid/cpp/src/qpid/types/Uuid.cpp
new file mode 100644
index 0000000000..9face4e5d2
--- /dev/null
+++ b/qpid/cpp/src/qpid/types/Uuid.cpp
@@ -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.
+ *
+ */
+#include "qpid/types/Uuid.h"
+#include "qpid/sys/uuid.h"
+#include <sstream>
+#include <iostream>
+#include <string.h>
+
+namespace qpid {
+namespace types {
+
+using namespace std;
+
+const size_t Uuid::SIZE=16;
+static const size_t UNPARSED_SIZE=36;
+
+Uuid::Uuid(bool unique)
+{
+ if (unique) {
+ generate();
+ } else {
+ clear();
+ }
+}
+
+Uuid::Uuid(const Uuid& other)
+{
+ ::memcpy(bytes, other.bytes, Uuid::SIZE);
+}
+
+Uuid::Uuid(const unsigned char* uuid)
+{
+ ::memcpy(bytes, uuid, Uuid::SIZE);
+}
+
+Uuid& Uuid::operator=(const Uuid& other)
+{
+ if (this == &other) return *this;
+ ::memcpy(bytes, other.bytes, Uuid::SIZE);
+ return *this;
+}
+
+void Uuid::generate()
+{
+ uuid_generate(bytes);
+}
+
+void Uuid::clear()
+{
+ uuid_clear(bytes);
+}
+
+// Force int 0/!0 to false/true; avoids compile warnings.
+bool Uuid::isNull() const
+{
+ return !!uuid_is_null(bytes);
+}
+
+Uuid::operator bool() const { return !isNull(); }
+bool Uuid::operator!() const { return isNull(); }
+
+size_t Uuid::size() const { return SIZE; }
+
+const unsigned char* Uuid::data() const
+{
+ return bytes;
+}
+
+bool operator==(const Uuid& a, const Uuid& b)
+{
+ return uuid_compare(a.bytes, b.bytes) == 0;
+}
+
+bool operator!=(const Uuid& a, const Uuid& b)
+{
+ return !(a == b);
+}
+
+bool operator<(const Uuid& a, const Uuid& b)
+{
+ return uuid_compare(a.bytes, b.bytes) < 0;
+}
+
+bool operator>(const Uuid& a, const Uuid& b)
+{
+ return uuid_compare(a.bytes, b.bytes) > 0;
+}
+
+bool operator<=(const Uuid& a, const Uuid& b)
+{
+ return uuid_compare(a.bytes, b.bytes) <= 0;
+}
+
+bool operator>=(const Uuid& a, const Uuid& b)
+{
+ return uuid_compare(a.bytes, b.bytes) >= 0;
+}
+
+ostream& operator<<(ostream& out, Uuid uuid)
+{
+ char unparsed[UNPARSED_SIZE + 1];
+ uuid_unparse(uuid.bytes, unparsed);
+ return out << unparsed;
+}
+
+istream& operator>>(istream& in, Uuid& uuid)
+{
+ char unparsed[UNPARSED_SIZE + 1] = {0};
+ in.get(unparsed, sizeof(unparsed));
+ if (uuid_parse(unparsed, uuid.bytes) != 0)
+ in.setstate(ios::failbit);
+ return in;
+}
+
+std::string Uuid::str() const
+{
+ std::ostringstream os;
+ os << *this;
+ return os.str();
+}
+
+}} // namespace qpid::types
diff --git a/qpid/cpp/src/qpid/types/Variant.cpp b/qpid/cpp/src/qpid/types/Variant.cpp
new file mode 100644
index 0000000000..0b28234025
--- /dev/null
+++ b/qpid/cpp/src/qpid/types/Variant.cpp
@@ -0,0 +1,890 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/types/Variant.h"
+#include "qpid/Msg.h"
+#include "qpid/log/Statement.h"
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <algorithm>
+#include <limits>
+#include <sstream>
+
+namespace qpid {
+namespace types {
+
+namespace {
+const std::string EMPTY;
+const std::string PREFIX("invalid conversion: ");
+}
+
+InvalidConversion::InvalidConversion(const std::string& msg) : Exception(PREFIX + msg) {}
+
+class VariantImpl
+{
+ public:
+ VariantImpl();
+ VariantImpl(bool);
+ VariantImpl(uint8_t);
+ VariantImpl(uint16_t);
+ VariantImpl(uint32_t);
+ VariantImpl(uint64_t);
+ VariantImpl(int8_t);
+ VariantImpl(int16_t);
+ VariantImpl(int32_t);
+ VariantImpl(int64_t);
+ VariantImpl(float);
+ VariantImpl(double);
+ VariantImpl(const std::string&, const std::string& encoding=std::string());
+ VariantImpl(const Variant::Map&);
+ VariantImpl(const Variant::List&);
+ VariantImpl(const Uuid&);
+ ~VariantImpl();
+
+ VariantType getType() const;
+
+ bool asBool() const;
+ uint8_t asUint8() const;
+ uint16_t asUint16() const;
+ uint32_t asUint32() const;
+ uint64_t asUint64() const;
+ int8_t asInt8() const;
+ int16_t asInt16() const;
+ int32_t asInt32() const;
+ int64_t asInt64() const;
+ float asFloat() const;
+ double asDouble() const;
+ std::string asString() const;
+ Uuid asUuid() const;
+
+ const Variant::Map& asMap() const;
+ Variant::Map& asMap();
+ const Variant::List& asList() const;
+ Variant::List& asList();
+
+ const std::string& getString() const;
+ std::string& getString();
+
+ void setEncoding(const std::string&);
+ const std::string& getEncoding() const;
+
+ bool isEqualTo(VariantImpl&) const;
+ bool isEquivalentTo(VariantImpl&) const;
+
+ static VariantImpl* create(const Variant&);
+ private:
+ const VariantType type;
+ union {
+ bool b;
+ uint8_t ui8;
+ uint16_t ui16;
+ uint32_t ui32;
+ uint64_t ui64;
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ float f;
+ double d;
+ void* v;//variable width data
+ } value;
+ std::string encoding;//optional encoding for variable length data
+
+ template<class T> T convertFromString() const
+ {
+ std::string* s = reinterpret_cast<std::string*>(value.v);
+ if (std::numeric_limits<T>::is_signed || s->find('-') != 0) {
+ //lexical_cast won't fail if string is a negative number and T is unsigned
+ try {
+ return boost::lexical_cast<T>(*s);
+ } catch(const boost::bad_lexical_cast&) {
+ //don't return, throw exception below
+ }
+ } else {
+ //T is unsigned and number starts with '-'
+ try {
+ //handle special case of negative zero
+ if (boost::lexical_cast<int>(*s) == 0) return 0;
+ //else its a non-zero negative number so throw exception at end of function
+ } catch(const boost::bad_lexical_cast&) {
+ //wasn't a valid int, therefore not a valid uint
+ }
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert " << *s));
+ }
+};
+
+
+VariantImpl::VariantImpl() : type(VAR_VOID) { value.i64 = 0; }
+VariantImpl::VariantImpl(bool b) : type(VAR_BOOL) { value.b = b; }
+VariantImpl::VariantImpl(uint8_t i) : type(VAR_UINT8) { value.ui8 = i; }
+VariantImpl::VariantImpl(uint16_t i) : type(VAR_UINT16) { value.ui16 = i; }
+VariantImpl::VariantImpl(uint32_t i) : type(VAR_UINT32) { value.ui32 = i; }
+VariantImpl::VariantImpl(uint64_t i) : type(VAR_UINT64) { value.ui64 = i; }
+VariantImpl::VariantImpl(int8_t i) : type(VAR_INT8) { value.i8 = i; }
+VariantImpl::VariantImpl(int16_t i) : type(VAR_INT16) { value.i16 = i; }
+VariantImpl::VariantImpl(int32_t i) : type(VAR_INT32) { value.i32 = i; }
+VariantImpl::VariantImpl(int64_t i) : type(VAR_INT64) { value.i64 = i; }
+VariantImpl::VariantImpl(float f) : type(VAR_FLOAT) { value.f = f; }
+VariantImpl::VariantImpl(double d) : type(VAR_DOUBLE) { value.d = d; }
+VariantImpl::VariantImpl(const std::string& s, const std::string& e)
+ : type(VAR_STRING), encoding(e) { value.v = new std::string(s); }
+VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.v = new Variant::Map(m); }
+VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.v = new Variant::List(l); }
+VariantImpl::VariantImpl(const Uuid& u) : type(VAR_UUID) { value.v = new Uuid(u); }
+
+VariantImpl::~VariantImpl() {
+ switch (type) {
+ case VAR_STRING:
+ delete reinterpret_cast<std::string*>(value.v);
+ break;
+ case VAR_MAP:
+ delete reinterpret_cast<Variant::Map*>(value.v);
+ break;
+ case VAR_LIST:
+ delete reinterpret_cast<Variant::List*>(value.v);
+ break;
+ case VAR_UUID:
+ delete reinterpret_cast<Uuid*>(value.v);
+ break;
+ default:
+ break;
+ }
+}
+
+VariantType VariantImpl::getType() const { return type; }
+
+namespace {
+
+bool same_char(char a, char b)
+{
+ return toupper(a) == toupper(b);
+}
+
+bool caseInsensitiveMatch(const std::string& a, const std::string& b)
+{
+ return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), &same_char);
+}
+
+const std::string TRUE("True");
+const std::string FALSE("False");
+
+bool toBool(const std::string& s)
+{
+ if (caseInsensitiveMatch(s, TRUE)) return true;
+ if (caseInsensitiveMatch(s, FALSE)) return false;
+ try { return boost::lexical_cast<int>(s); } catch(const boost::bad_lexical_cast&) {}
+ throw InvalidConversion(QPID_MSG("Cannot convert " << s << " to bool"));
+}
+
+template <class T> std::string toString(const T& t)
+{
+ std::stringstream out;
+ out << t;
+ return out.str();
+}
+
+template <class T> bool equal(const T& a, const T& b)
+{
+ return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
+}
+
+}
+
+bool VariantImpl::asBool() const
+{
+ switch(type) {
+ case VAR_VOID: return false;
+ case VAR_BOOL: return value.b;
+ case VAR_UINT8: return value.ui8;
+ case VAR_UINT16: return value.ui16;
+ case VAR_UINT32: return value.ui32;
+ case VAR_UINT64: return value.ui64;
+ case VAR_INT8: return value.i8;
+ case VAR_INT16: return value.i16;
+ case VAR_INT32: return value.i32;
+ case VAR_INT64: return value.i64;
+ case VAR_STRING: return toBool(*reinterpret_cast<std::string*>(value.v));
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_BOOL)));
+ }
+}
+uint8_t VariantImpl::asUint8() const
+{
+ switch(type) {
+ case VAR_UINT8: return value.ui8;
+ case VAR_UINT16:
+ if (value.ui16 <= 0x00ff)
+ return uint8_t(value.ui16);
+ break;
+ case VAR_UINT32:
+ if (value.ui32 <= 0x000000ff)
+ return uint8_t(value.ui32);
+ break;
+ case VAR_UINT64:
+ if (value.ui64 <= 0x00000000000000ff)
+ return uint8_t(value.ui64);
+ break;
+ case VAR_INT8:
+ if (value.i8 >= 0)
+ return uint8_t(value.i8);
+ break;
+ case VAR_INT16:
+ if (value.i16 >= 0 && value.i16 <= 0x00ff)
+ return uint8_t(value.i16);
+ break;
+ case VAR_INT32:
+ if (value.i32 >= 0 && value.i32 <= 0x000000ff)
+ return uint8_t(value.i32);
+ break;
+ case VAR_INT64:
+ if (value.i64 >= 0 && value.i64 <= 0x00000000000000ff)
+ return uint8_t(value.i64);
+ break;
+ case VAR_STRING: return convertFromString<uint8_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT8)));
+}
+uint16_t VariantImpl::asUint16() const
+{
+ switch(type) {
+ case VAR_UINT8: return value.ui8;
+ case VAR_UINT16: return value.ui16;
+ case VAR_UINT32:
+ if (value.ui32 <= 0x0000ffff)
+ return uint16_t(value.ui32);
+ break;
+ case VAR_UINT64:
+ if (value.ui64 <= 0x000000000000ffff)
+ return uint16_t(value.ui64);
+ break;
+ case VAR_INT8:
+ if (value.i8 >= 0)
+ return uint16_t(value.i8);
+ break;
+ case VAR_INT16:
+ if (value.i16 >= 0)
+ return uint16_t(value.i16);
+ break;
+ case VAR_INT32:
+ if (value.i32 >= 0 && value.i32 <= 0x0000ffff)
+ return uint16_t(value.i32);
+ break;
+ case VAR_INT64:
+ if (value.i64 >= 0 && value.i64 <= 0x000000000000ffff)
+ return uint16_t(value.i64);
+ break;
+ case VAR_STRING: return convertFromString<uint16_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT16)));
+}
+uint32_t VariantImpl::asUint32() const
+{
+ switch(type) {
+ case VAR_UINT8: return value.ui8;
+ case VAR_UINT16: return value.ui16;
+ case VAR_UINT32: return value.ui32;
+ case VAR_UINT64:
+ if (value.ui64 <= 0x00000000ffffffff)
+ return uint32_t(value.ui64);
+ break;
+ case VAR_INT8:
+ if (value.i8 >= 0)
+ return uint32_t(value.i8);
+ break;
+ case VAR_INT16:
+ if (value.i16 >= 0)
+ return uint32_t(value.i16);
+ break;
+ case VAR_INT32:
+ if (value.i32 >= 0)
+ return uint32_t(value.i32);
+ break;
+ case VAR_INT64:
+ if (value.i64 >= 0 && value.i64 <= 0x00000000ffffffff)
+ return uint32_t(value.i64);
+ break;
+ case VAR_STRING: return convertFromString<uint32_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT32)));
+}
+uint64_t VariantImpl::asUint64() const
+{
+ switch(type) {
+ case VAR_UINT8: return value.ui8;
+ case VAR_UINT16: return value.ui16;
+ case VAR_UINT32: return value.ui32;
+ case VAR_UINT64: return value.ui64;
+ case VAR_INT8:
+ if (value.i8 >= 0)
+ return uint64_t(value.i8);
+ break;
+ case VAR_INT16:
+ if (value.i16 >= 0)
+ return uint64_t(value.i16);
+ break;
+ case VAR_INT32:
+ if (value.i32 >= 0)
+ return uint64_t(value.i32);
+ break;
+ case VAR_INT64:
+ if (value.i64 >= 0)
+ return uint64_t(value.i64);
+ break;
+ case VAR_STRING: return convertFromString<uint64_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UINT64)));
+}
+
+int8_t VariantImpl::asInt8() const
+{
+ switch(type) {
+ case VAR_INT8: return value.i8;
+ case VAR_INT16:
+ if ((value.i16 >= std::numeric_limits<int8_t>::min()) && (value.i16 <= std::numeric_limits<int8_t>::max()))
+ return int8_t(value.i16);
+ break;
+ case VAR_INT32:
+ if ((value.i32 >= std::numeric_limits<int8_t>::min()) && (value.i32 <= std::numeric_limits<int8_t>::max()))
+ return int8_t(value.i32);
+ break;
+ case VAR_INT64:
+ if ((value.i64 >= std::numeric_limits<int8_t>::min()) && (value.i64 <= std::numeric_limits<int8_t>::max()))
+ return int8_t(value.i64);
+ break;
+ case VAR_UINT8:
+ if (value.ui8 <= std::numeric_limits<int8_t>::max())
+ return int8_t(value.ui8);
+ break;
+ case VAR_UINT16:
+ if (value.ui16 <= std::numeric_limits<int8_t>::max())
+ return int8_t(value.ui16);
+ break;
+ case VAR_UINT32:
+ if (value.ui32 <= (uint32_t) std::numeric_limits<int8_t>::max())
+ return int8_t(value.ui32);
+ break;
+ case VAR_UINT64:
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int8_t>::max())
+ return int8_t(value.ui64);
+ break;
+ case VAR_STRING: return convertFromString<int8_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT8)));
+}
+int16_t VariantImpl::asInt16() const
+{
+ switch(type) {
+ case VAR_INT8: return value.i8;
+ case VAR_INT16: return value.i16;
+ case VAR_INT32:
+ if ((value.i32 >= std::numeric_limits<int16_t>::min()) && (value.i32 <= std::numeric_limits<int16_t>::max()))
+ return int16_t(value.i32);
+ break;
+ case VAR_INT64:
+ if ((value.i64 >= std::numeric_limits<int16_t>::min()) && (value.i64 <= std::numeric_limits<int16_t>::max()))
+ return int16_t(value.i64);
+ break;
+ case VAR_UINT8: return int16_t(value.ui8);
+ case VAR_UINT16:
+ if (value.ui16 <= std::numeric_limits<int16_t>::max())
+ return int16_t(value.ui16);
+ break;
+ case VAR_UINT32:
+ if (value.ui32 <= (uint32_t) std::numeric_limits<int16_t>::max())
+ return int16_t(value.ui32);
+ break;
+ case VAR_UINT64:
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int16_t>::max())
+ return int16_t(value.ui64);
+ break;
+ case VAR_STRING: return convertFromString<int16_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT16)));
+}
+int32_t VariantImpl::asInt32() const
+{
+ switch(type) {
+ case VAR_INT8: return value.i8;
+ case VAR_INT16: return value.i16;
+ case VAR_INT32: return value.i32;
+ case VAR_INT64:
+ if ((value.i64 >= std::numeric_limits<int32_t>::min()) && (value.i64 <= std::numeric_limits<int32_t>::max()))
+ return int32_t(value.i64);
+ break;
+ case VAR_UINT8: return int32_t(value.ui8);
+ case VAR_UINT16: return int32_t(value.ui16);
+ case VAR_UINT32:
+ if (value.ui32 <= (uint32_t) std::numeric_limits<int32_t>::max())
+ return int32_t(value.ui32);
+ break;
+ case VAR_UINT64:
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int32_t>::max())
+ return int32_t(value.ui64);
+ break;
+ case VAR_STRING: return convertFromString<int32_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT32)));
+}
+int64_t VariantImpl::asInt64() const
+{
+ switch(type) {
+ case VAR_INT8: return value.i8;
+ case VAR_INT16: return value.i16;
+ case VAR_INT32: return value.i32;
+ case VAR_INT64: return value.i64;
+ case VAR_UINT8: return int64_t(value.ui8);
+ case VAR_UINT16: return int64_t(value.ui16);
+ case VAR_UINT32: return int64_t(value.ui32);
+ case VAR_UINT64:
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int64_t>::max())
+ return int64_t(value.ui64);
+ break;
+ case VAR_STRING: return convertFromString<int64_t>();
+ default: break;
+ }
+ throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_INT64)));
+}
+float VariantImpl::asFloat() const
+{
+ switch(type) {
+ case VAR_FLOAT: return value.f;
+ case VAR_STRING: return convertFromString<float>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_FLOAT)));
+ }
+}
+double VariantImpl::asDouble() const
+{
+ switch(type) {
+ case VAR_FLOAT: return value.f;
+ case VAR_DOUBLE: return value.d;
+ case VAR_STRING: return convertFromString<double>();
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_DOUBLE)));
+ }
+}
+std::string VariantImpl::asString() const
+{
+ switch(type) {
+ case VAR_VOID: return EMPTY;
+ case VAR_BOOL: return value.b ? TRUE : FALSE;
+ case VAR_UINT8: return boost::lexical_cast<std::string>((int) value.ui8);
+ case VAR_UINT16: return boost::lexical_cast<std::string>(value.ui16);
+ case VAR_UINT32: return boost::lexical_cast<std::string>(value.ui32);
+ case VAR_UINT64: return boost::lexical_cast<std::string>(value.ui64);
+ case VAR_INT8: return boost::lexical_cast<std::string>((int) value.i8);
+ case VAR_INT16: return boost::lexical_cast<std::string>(value.i16);
+ case VAR_INT32: return boost::lexical_cast<std::string>(value.i32);
+ case VAR_INT64: return boost::lexical_cast<std::string>(value.i64);
+ case VAR_DOUBLE: return boost::lexical_cast<std::string>(value.d);
+ case VAR_FLOAT: return boost::lexical_cast<std::string>(value.f);
+ case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
+ case VAR_UUID: return reinterpret_cast<Uuid*>(value.v)->str();
+ case VAR_LIST: return toString(asList());
+ case VAR_MAP: return toString(asMap());
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_STRING)));
+ }
+}
+Uuid VariantImpl::asUuid() const
+{
+ switch(type) {
+ case VAR_UUID: return *reinterpret_cast<Uuid*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UUID)));
+ }
+}
+
+bool VariantImpl::isEqualTo(VariantImpl& other) const
+{
+ if (type == other.type) {
+ switch(type) {
+ case VAR_VOID: return true;
+ case VAR_BOOL: return value.b == other.value.b;
+ case VAR_UINT8: return value.ui8 == other.value.ui8;
+ case VAR_UINT16: return value.ui16 == other.value.ui16;
+ case VAR_UINT32: return value.ui32 == other.value.ui32;
+ case VAR_UINT64: return value.ui64 == other.value.ui64;
+ case VAR_INT8: return value.i8 == other.value.i8;
+ case VAR_INT16: return value.i16 == other.value.i16;
+ case VAR_INT32: return value.i32 == other.value.i32;
+ case VAR_INT64: return value.i64 == other.value.i64;
+ case VAR_DOUBLE: return value.d == other.value.d;
+ case VAR_FLOAT: return value.f == other.value.f;
+ case VAR_STRING: return *reinterpret_cast<std::string*>(value.v)
+ == *reinterpret_cast<std::string*>(other.value.v);
+ case VAR_UUID: return *reinterpret_cast<Uuid*>(value.v)
+ == *reinterpret_cast<Uuid*>(other.value.v);
+ case VAR_LIST: return equal(asList(), other.asList());
+ case VAR_MAP: return equal(asMap(), other.asMap());
+ }
+ }
+ return false;
+}
+
+const Variant::Map& VariantImpl::asMap() const
+{
+ switch(type) {
+ case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP)));
+ }
+}
+
+Variant::Map& VariantImpl::asMap()
+{
+ switch(type) {
+ case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP)));
+ }
+}
+
+const Variant::List& VariantImpl::asList() const
+{
+ switch(type) {
+ case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST)));
+ }
+}
+
+Variant::List& VariantImpl::asList()
+{
+ switch(type) {
+ case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST)));
+ }
+}
+
+std::string& VariantImpl::getString()
+{
+ switch(type) {
+ case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
+ }
+}
+
+const std::string& VariantImpl::getString() const
+{
+ switch(type) {
+ case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
+ default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
+ }
+}
+
+void VariantImpl::setEncoding(const std::string& s) { encoding = s; }
+const std::string& VariantImpl::getEncoding() const { return encoding; }
+
+std::string getTypeName(VariantType type)
+{
+ switch (type) {
+ case VAR_VOID: return "void";
+ case VAR_BOOL: return "bool";
+ case VAR_UINT8: return "uint8";
+ case VAR_UINT16: return "uint16";
+ case VAR_UINT32: return "uint32";
+ case VAR_UINT64: return "uint64";
+ case VAR_INT8: return "int8";
+ case VAR_INT16: return "int16";
+ case VAR_INT32: return "int32";
+ case VAR_INT64: return "int64";
+ case VAR_FLOAT: return "float";
+ case VAR_DOUBLE: return "double";
+ case VAR_STRING: return "string";
+ case VAR_MAP: return "map";
+ case VAR_LIST: return "list";
+ case VAR_UUID: return "uuid";
+ }
+ return "<unknown>";//should never happen
+}
+
+VariantImpl* VariantImpl::create(const Variant& v)
+{
+ switch (v.getType()) {
+ case VAR_BOOL: return new VariantImpl(v.asBool());
+ case VAR_UINT8: return new VariantImpl(v.asUint8());
+ case VAR_UINT16: return new VariantImpl(v.asUint16());
+ case VAR_UINT32: return new VariantImpl(v.asUint32());
+ case VAR_UINT64: return new VariantImpl(v.asUint64());
+ case VAR_INT8: return new VariantImpl(v.asInt8());
+ case VAR_INT16: return new VariantImpl(v.asInt16());
+ case VAR_INT32: return new VariantImpl(v.asInt32());
+ case VAR_INT64: return new VariantImpl(v.asInt64());
+ case VAR_FLOAT: return new VariantImpl(v.asFloat());
+ case VAR_DOUBLE: return new VariantImpl(v.asDouble());
+ case VAR_STRING: return new VariantImpl(v.asString(), v.getEncoding());
+ case VAR_MAP: return new VariantImpl(v.asMap());
+ case VAR_LIST: return new VariantImpl(v.asList());
+ case VAR_UUID: return new VariantImpl(v.asUuid());
+ default: return new VariantImpl();
+ }
+}
+
+Variant::Variant() : impl(0) {}
+Variant::Variant(bool b) : impl(new VariantImpl(b)) {}
+Variant::Variant(uint8_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint16_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint32_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(uint64_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int8_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int16_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int32_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(int64_t i) : impl(new VariantImpl(i)) {}
+Variant::Variant(float f) : impl(new VariantImpl(f)) {}
+Variant::Variant(double d) : impl(new VariantImpl(d)) {}
+Variant::Variant(const std::string& s) : impl(new VariantImpl(s)) {}
+Variant::Variant(const char* s) : impl(new VariantImpl(std::string(s))) {}
+Variant::Variant(const Map& m) : impl(new VariantImpl(m)) {}
+Variant::Variant(const List& l) : impl(new VariantImpl(l)) {}
+Variant::Variant(const Variant& v) : impl(VariantImpl::create(v)) {}
+Variant::Variant(const Uuid& u) : impl(new VariantImpl(u)) {}
+
+Variant::~Variant() { if (impl) delete impl; }
+
+void Variant::reset()
+{
+ if (impl) delete impl;
+ impl = 0;
+}
+
+
+Variant& Variant::operator=(bool b)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(b);
+ return *this;
+}
+
+Variant& Variant::operator=(uint8_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint16_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint32_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(uint64_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+
+Variant& Variant::operator=(int8_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int16_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int32_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+Variant& Variant::operator=(int64_t i)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(i);
+ return *this;
+}
+
+Variant& Variant::operator=(float f)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(f);
+ return *this;
+}
+Variant& Variant::operator=(double d)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(d);
+ return *this;
+}
+
+Variant& Variant::operator=(const std::string& s)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(s);
+ return *this;
+}
+
+Variant& Variant::operator=(const char* s)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(std::string(s));
+ return *this;
+}
+
+Variant& Variant::operator=(const Uuid& u)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(u);
+ return *this;
+}
+
+Variant& Variant::operator=(const Map& m)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(m);
+ return *this;
+}
+
+Variant& Variant::operator=(const List& l)
+{
+ if (impl) delete impl;
+ impl = new VariantImpl(l);
+ return *this;
+}
+
+Variant& Variant::operator=(const Variant& v)
+{
+ if (impl) delete impl;
+ impl = VariantImpl::create(v);
+ return *this;
+}
+
+Variant& Variant::parse(const std::string& s)
+{
+ operator=(s);
+ try {
+ return operator=(asInt64());
+ } catch (const InvalidConversion&) {}
+ try {
+ return operator=(asDouble());
+ } catch (const InvalidConversion&) {}
+ try {
+ return operator=(asBool());
+ } catch (const InvalidConversion&) {}
+ return *this;
+}
+
+
+VariantType Variant::getType() const { return impl ? impl->getType() : VAR_VOID; }
+bool Variant::isVoid() const { return getType() == VAR_VOID; }
+bool Variant::asBool() const { return impl && impl->asBool(); }
+uint8_t Variant::asUint8() const { return impl ? impl->asUint8() : 0; }
+uint16_t Variant::asUint16() const { return impl ? impl->asUint16() : 0; }
+uint32_t Variant::asUint32() const { return impl ? impl->asUint32() : 0; }
+uint64_t Variant::asUint64() const { return impl ? impl->asUint64() : 0; }
+int8_t Variant::asInt8() const { return impl ? impl->asInt8() : 0; }
+int16_t Variant::asInt16() const { return impl ? impl->asInt16() : 0; }
+int32_t Variant::asInt32() const { return impl ? impl->asInt32(): 0; }
+int64_t Variant::asInt64() const { return impl ? impl->asInt64() : 0; }
+float Variant::asFloat() const { return impl ? impl->asFloat() : 0; }
+double Variant::asDouble() const { return impl ? impl->asDouble() : 0; }
+std::string Variant::asString() const { return impl ? impl->asString() : EMPTY; }
+Uuid Variant::asUuid() const { return impl ? impl->asUuid() : Uuid(); }
+const Variant::Map& Variant::asMap() const { if (!impl) throw InvalidConversion("Can't convert VOID to MAP"); return impl->asMap(); }
+Variant::Map& Variant::asMap() { if (!impl) throw InvalidConversion("Can't convert VOID to MAP"); return impl->asMap(); }
+const Variant::List& Variant::asList() const { if (!impl) throw InvalidConversion("Can't convert VOID to LIST"); return impl->asList(); }
+Variant::List& Variant::asList() { if (!impl) throw InvalidConversion("Can't convert VOID to LIST"); return impl->asList(); }
+const std::string& Variant::getString() const { if (!impl) throw InvalidConversion("Can't convert VOID to STRING"); return impl->getString(); }
+std::string& Variant::getString() { if (!impl) throw InvalidConversion("Can't convert VOID to STRING"); return impl->getString(); }
+void Variant::setEncoding(const std::string& s) {
+ if (!impl) impl = new VariantImpl();
+ impl->setEncoding(s);
+}
+const std::string& Variant::getEncoding() const { return impl ? impl->getEncoding() : EMPTY; }
+
+Variant::operator bool() const { return asBool(); }
+Variant::operator uint8_t() const { return asUint8(); }
+Variant::operator uint16_t() const { return asUint16(); }
+Variant::operator uint32_t() const { return asUint32(); }
+Variant::operator uint64_t() const { return asUint64(); }
+Variant::operator int8_t() const { return asInt8(); }
+Variant::operator int16_t() const { return asInt16(); }
+Variant::operator int32_t() const { return asInt32(); }
+Variant::operator int64_t() const { return asInt64(); }
+Variant::operator float() const { return asFloat(); }
+Variant::operator double() const { return asDouble(); }
+Variant::operator std::string() const { return asString(); }
+Variant::operator Uuid() const { return asUuid(); }
+
+std::ostream& operator<<(std::ostream& out, const Variant::Map& map)
+{
+ out << "{";
+ for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) {
+ if (i != map.begin()) out << ", ";
+ out << i->first << ":" << i->second;
+ }
+ out << "}";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Variant::List& list)
+{
+ out << "[";
+ for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ if (i != list.begin()) out << ", ";
+ out << *i;
+ }
+ out << "]";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Variant& value)
+{
+ switch (value.getType()) {
+ case VAR_MAP:
+ out << value.asMap();
+ break;
+ case VAR_LIST:
+ out << value.asList();
+ break;
+ case VAR_VOID:
+ out << "<void>";
+ break;
+ default:
+ out << value.asString();
+ break;
+ }
+ return out;
+}
+
+bool operator==(const Variant& a, const Variant& b)
+{
+ return a.isEqualTo(b);
+}
+
+bool Variant::isEqualTo(const Variant& other) const
+{
+ return impl && impl->isEqualTo(*other.impl);
+}
+
+}} // namespace qpid::types
diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp
new file mode 100644
index 0000000000..b7ff5d211d
--- /dev/null
+++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp
@@ -0,0 +1,408 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "config.h"
+
+#include "qpid/xml/XmlExchange.h"
+
+#include "qpid/broker/DeliverableMessage.h"
+
+#include "qpid/log/Statement.h"
+#include "qpid/broker/FedOps.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/reply_exceptions.h"
+
+#include "qpid/Plugin.h"
+
+#include <xercesc/framework/MemBufInputSource.hpp>
+
+#ifdef XQ_EFFECTIVE_BOOLEAN_VALUE_HPP
+#include <xqilla/ast/XQEffectiveBooleanValue.hpp>
+#endif
+
+#include <xqilla/ast/XQGlobalVariable.hpp>
+
+#include <xqilla/context/ItemFactory.hpp>
+#include <xqilla/xqilla-simple.hpp>
+
+#include <boost/bind.hpp>
+#include <functional>
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+
+using namespace qpid::framing;
+using namespace qpid::sys;
+using qpid::management::Manageable;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+XQilla XmlBinding::xqilla;
+
+XmlBinding::XmlBinding(const std::string& key, const Queue::shared_ptr queue, const std::string& _fedOrigin, Exchange* parent,
+ const ::qpid::framing::FieldTable& _arguments, const std::string& queryText )
+ : Binding(key, queue, parent, _arguments),
+ xquery(),
+ parse_message_content(true),
+ fedOrigin(_fedOrigin)
+{
+ startManagement();
+
+ QPID_LOG(trace, "Creating binding with query: " << queryText );
+
+ try {
+ Query q(xqilla.parse(X(queryText.c_str())));
+ xquery = q;
+
+ QPID_LOG(trace, "Bound successfully with query: " << queryText );
+
+ parse_message_content = false;
+
+ if (xquery->getQueryBody()->getStaticAnalysis().areContextFlagsUsed()) {
+ parse_message_content = true;
+ }
+ else {
+ GlobalVariables &vars = const_cast<GlobalVariables&>(xquery->getVariables());
+ for (GlobalVariables::iterator it = vars.begin(); it != vars.end(); ++it) {
+ if ((*it)->getStaticAnalysis().areContextFlagsUsed()) {
+ parse_message_content = true;
+ break;
+ }
+ }
+ }
+ }
+ catch (XQException& e) {
+ throw InternalErrorException(QPID_MSG("Could not parse xquery:"+ queryText));
+ }
+ catch (...) {
+ throw InternalErrorException(QPID_MSG("Unexpected error - Could not parse xquery:"+ queryText));
+ }
+}
+
+
+XmlExchange::XmlExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+XmlExchange::XmlExchange(const std::string& _name, bool _durable,
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
+{
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type (typeName);
+}
+
+bool XmlExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+{
+
+ // Federation uses bind for unbind and reorigin comands as well as for binds.
+ //
+ // Both federated and local binds are done in this method. Other
+ // federated requests are done by calling the relevent methods.
+
+ string fedOp;
+ string fedTags;
+ string fedOrigin;
+
+ if (args)
+ fedOp = args->getAsString(qpidFedOp);
+ if (! fedOp.empty()) {
+ fedTags = args->getAsString(qpidFedTags);
+ fedOrigin = args->getAsString(qpidFedOrigin);
+ }
+
+ if (fedOp == fedOpUnbind) {
+ return fedUnbind(fedOrigin, fedTags, queue, bindingKey, args);
+ }
+ else if (fedOp == fedOpReorigin) {
+ fedReorigin();
+ return true;
+ }
+
+ // OK, looks like we're really going to bind
+
+ else if (fedOp.empty() || fedOp == fedOpBind) {
+
+ string queryText = args->getAsString("xquery");
+
+ RWlock::ScopedWlock l(lock);
+
+ XmlBinding::vector& bindings(bindingsMap[bindingKey]);
+ XmlBinding::vector::ConstPtr p = bindings.snapshot();
+
+ if (!p || std::find_if(p->begin(), p->end(), MatchQueueAndOrigin(queue, fedOrigin)) == p->end()) {
+
+ XmlBinding::shared_ptr binding(new XmlBinding (bindingKey, queue, fedOrigin, this, *args, queryText));
+ bindings.add(binding);
+
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ return false;
+ }
+ }
+ else {
+ QPID_LOG(warning, "Unknown Federation Op: " << fedOp);
+ }
+
+ routeIVE();
+ propagateFedOp(bindingKey, fedTags, fedOp, fedOrigin, args);
+
+ return true;
+}
+
+bool XmlExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+{
+ /*
+ * When called directly, no qpidFedOrigin argument will be
+ * present. When called from federation, it will be present.
+ *
+ * This is a bit of a hack - the binding needs the origin, but
+ * this interface, as originally defined, would not supply one.
+ */
+ string fedOrigin;
+ if (args) fedOrigin = args->getAsString(qpidFedOrigin);
+
+ RWlock::ScopedWlock l(lock);
+ if (bindingsMap[bindingKey].remove_if(MatchQueueAndOrigin(queue, fedOrigin))) {
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args, bool parse_message_content)
+{
+ string msgContent;
+
+ try {
+ QPID_LOG(trace, "matches: query is [" << UTF8(query->getQueryText()) << "]");
+
+ boost::scoped_ptr<DynamicContext> context(query->createDynamicContext());
+ if (!context.get()) {
+ throw InternalErrorException(QPID_MSG("Query context looks munged ..."));
+ }
+
+ if (parse_message_content) {
+
+ msg.getMessage().getFrames().getContent(msgContent);
+
+ QPID_LOG(trace, "matches: message content is [" << msgContent << "]");
+
+ XERCES_CPP_NAMESPACE::MemBufInputSource xml((const XMLByte*) msgContent.c_str(),
+ msgContent.length(), "input" );
+
+ // This will parse the document using either Xerces or FastXDM, depending
+ // on your XQilla configuration. FastXDM can be as much as 10x faster.
+
+ Sequence seq(context->parseDocument(xml));
+
+ if(!seq.isEmpty() && seq.first()->isNode()) {
+ context->setContextItem(seq.first());
+ context->setContextPosition(1);
+ context->setContextSize(1);
+ }
+ }
+
+ if (args) {
+ FieldTable::ValueMap::const_iterator v = args->begin();
+ for(; v != args->end(); ++v) {
+
+ if (v->second->convertsTo<double>()) {
+ QPID_LOG(trace, "XmlExchange, external variable (double): " << v->first << " = " << v->second->get<double>());
+ Item::Ptr value = context->getItemFactory()->createDouble(v->second->get<double>(), context.get());
+ context->setExternalVariable(X(v->first.c_str()), value);
+ }
+ else if (v->second->convertsTo<int>()) {
+ QPID_LOG(trace, "XmlExchange, external variable (int):" << v->first << " = " << v->second->getData().getInt());
+ Item::Ptr value = context->getItemFactory()->createInteger(v->second->get<int>(), context.get());
+ context->setExternalVariable(X(v->first.c_str()), value);
+ }
+ else if (v->second->convertsTo<std::string>()) {
+ QPID_LOG(trace, "XmlExchange, external variable (string):" << v->first << " = " << v->second->getData().getString().c_str());
+ Item::Ptr value = context->getItemFactory()->createString(X(v->second->get<std::string>().c_str()), context.get());
+ context->setExternalVariable(X(v->first.c_str()), value);
+ }
+
+ }
+ }
+
+ Result result = query->execute(context.get());
+#ifdef XQ_EFFECTIVE_BOOLEAN_VALUE_HPP
+ Item::Ptr first_ = result->next(context.get());
+ Item::Ptr second_ = result->next(context.get());
+ return XQEffectiveBooleanValue::get(first_, second_, context.get(), 0);
+#else
+ return result->getEffectiveBooleanValue(context.get(), 0);
+#endif
+ }
+ catch (XQException& e) {
+ QPID_LOG(warning, "Could not parse XML content (or message headers):" << msgContent);
+ }
+ catch (...) {
+ QPID_LOG(warning, "Unexpected error routing message: " << msgContent);
+ }
+ return 0;
+}
+
+// Future optimization: If any query in a binding for a given routing key requires
+// message content, parse the message once, and use that parsed form for all bindings.
+//
+// Future optimization: XQilla does not currently do document projection for data
+// accessed via the context item. If there is a single query for a given routing key,
+// and it accesses document data, this could be a big win.
+//
+// Document projection often is not a win if you have multiple queries on the same data.
+// But for very large messages, if all these queries are on the first part of the data,
+// it could still be a big win.
+
+void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args)
+{
+ PreRoute pr(msg, this);
+ try {
+ XmlBinding::vector::ConstPtr p;
+ BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ {
+ RWlock::ScopedRlock l(lock);
+ p = bindingsMap[routingKey].snapshot();
+ if (!p.get()) return;
+ }
+
+ for (std::vector<XmlBinding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); i++) {
+ if (matches((*i)->xquery, msg, args, (*i)->parse_message_content)) {
+ b->push_back(*i);
+ }
+ }
+ doRoute(msg, b);
+ } catch (...) {
+ QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey);
+ }
+}
+
+
+bool XmlExchange::isBound(Queue::shared_ptr queue, const string* const bindingKey, const FieldTable* const)
+{
+ RWlock::ScopedRlock l(lock);
+ if (bindingKey) {
+ XmlBindingsMap::iterator i = bindingsMap.find(*bindingKey);
+
+ if (i == bindingsMap.end())
+ return false;
+ if (!queue)
+ return true;
+ XmlBinding::vector::ConstPtr p = i->second.snapshot();
+ return p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end();
+ } else if (!queue) {
+ //if no queue or routing key is specified, just report whether any bindings exist
+ return bindingsMap.size() > 0;
+ } else {
+ for (XmlBindingsMap::iterator i = bindingsMap.begin(); i != bindingsMap.end(); i++) {
+ XmlBinding::vector::ConstPtr p = i->second.snapshot();
+ if (p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end()) return true;
+ }
+ return false;
+ }
+
+}
+
+XmlExchange::~XmlExchange()
+{
+ bindingsMap.clear();
+}
+
+void XmlExchange::propagateFedOp(const std::string& bindingKey, const std::string& fedTags, const std::string& fedOp, const std::string& fedOrigin, const qpid::framing::FieldTable* args)
+{
+ FieldTable nonFedArgs;
+
+ if (args) {
+ for (qpid::framing::FieldTable::ValueMap::const_iterator i=args->begin(); i != args->end(); ++i) {
+ const string& name(i->first);
+ if (name != qpidFedOp &&
+ name != qpidFedTags &&
+ name != qpidFedOrigin) {
+ nonFedArgs.insert((*i));
+ }
+ }
+ }
+
+ FieldTable* propArgs = (nonFedArgs.count() > 0 ? &nonFedArgs : 0);
+ Exchange::propagateFedOp(bindingKey, fedTags, fedOp, fedOrigin, propArgs);
+}
+
+bool XmlExchange::fedUnbind(const string& fedOrigin, const string& fedTags, Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+{
+ RWlock::ScopedRlock l(lock);
+
+ if (unbind(queue, bindingKey, args)) {
+ propagateFedOp(bindingKey, fedTags, fedOpUnbind, fedOrigin);
+ return true;
+ }
+ return false;
+}
+
+void XmlExchange::fedReorigin()
+{
+ std::vector<std::string> keys2prop;
+ {
+ RWlock::ScopedRlock l(lock);
+ for (XmlBindingsMap::iterator i = bindingsMap.begin(); i != bindingsMap.end(); ++i) {
+ XmlBinding::vector::ConstPtr p = i->second.snapshot();
+ if (std::find_if(p->begin(), p->end(), MatchOrigin(string())) != p->end()) {
+ keys2prop.push_back(i->first);
+ }
+ }
+ } /* lock dropped */
+ for (std::vector<std::string>::const_iterator key = keys2prop.begin();
+ key != keys2prop.end(); key++) {
+ propagateFedOp( *key, string(), fedOpBind, string());
+ }
+}
+
+
+XmlExchange::MatchOrigin::MatchOrigin(const string& _origin) : origin(_origin) {}
+
+bool XmlExchange::MatchOrigin::operator()(XmlBinding::shared_ptr b)
+{
+ return b->fedOrigin == origin;
+}
+
+
+XmlExchange::MatchQueueAndOrigin::MatchQueueAndOrigin(Queue::shared_ptr _queue, const string& _origin) : queue(_queue), origin(_origin) {}
+
+bool XmlExchange::MatchQueueAndOrigin::operator()(XmlBinding::shared_ptr b)
+{
+ return b->queue == queue and b->fedOrigin == origin;
+}
+
+
+const std::string XmlExchange::typeName("xml");
+
+}
+}
diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.h b/qpid/cpp/src/qpid/xml/XmlExchange.h
new file mode 100644
index 0000000000..958bad4931
--- /dev/null
+++ b/qpid/cpp/src/qpid/xml/XmlExchange.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#ifndef _XmlExchange_
+#define _XmlExchange_
+
+#include "qpid/broker/Exchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/broker/Queue.h"
+
+#include <xqilla/xqilla-simple.hpp>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <map>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+typedef boost::shared_ptr<XQQuery> Query;
+
+struct XmlBinding : public Exchange::Binding {
+
+ static XQilla xqilla;
+
+ typedef boost::shared_ptr<XmlBinding> shared_ptr;
+ typedef qpid::sys::CopyOnWriteArray<XmlBinding::shared_ptr> vector;
+
+ Query xquery;
+ bool parse_message_content;
+ const std::string fedOrigin; // empty for local bindings
+
+ XmlBinding(const std::string& key, const Queue::shared_ptr queue, const std::string& fedOrigin, Exchange* parent,
+ const ::qpid::framing::FieldTable& _arguments, const std::string& );
+
+};
+
+class XmlExchange : public virtual Exchange {
+
+ typedef std::map<string, XmlBinding::vector> XmlBindingsMap;
+ XmlBindingsMap bindingsMap;
+
+ qpid::sys::RWlock lock;
+
+ bool matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args, bool parse_message_content);
+
+ public:
+ static const std::string typeName;
+
+ XmlExchange(const std::string& name, management::Manageable* parent = 0, Broker* broker = 0);
+ XmlExchange(const std::string& _name, bool _durable,
+ const qpid::framing::FieldTable& _args, management::Manageable* parent = 0, Broker* broker = 0);
+
+ virtual std::string getType() const { return typeName; }
+
+ virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args);
+
+ virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args);
+
+ virtual void propagateFedOp(const std::string& bindingKey, const std::string& fedTags, const std::string& fedOp, const std::string& fedOrigin, const qpid::framing::FieldTable* args=0);
+
+ virtual bool fedUnbind(const std::string& fedOrigin, const std::string& fedTags, Queue::shared_ptr queue, const std::string& bindingKey, const qpid::framing::FieldTable* args);
+
+ virtual void fedReorigin();
+
+ virtual bool supportsDynamicBinding() { return true; }
+
+ virtual ~XmlExchange();
+
+ struct MatchOrigin {
+ const std::string origin;
+ MatchOrigin(const std::string& origin);
+ bool operator()(XmlBinding::shared_ptr b);
+ };
+
+ struct MatchQueueAndOrigin {
+ const Queue::shared_ptr queue;
+ const std::string origin;
+ MatchQueueAndOrigin(Queue::shared_ptr queue, const std::string& origin);
+ bool operator()(XmlBinding::shared_ptr b);
+ };
+
+};
+
+
+}
+}
+
+
+#endif
diff --git a/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp b/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp
new file mode 100644
index 0000000000..742b878e86
--- /dev/null
+++ b/qpid/cpp/src/qpid/xml/XmlExchangePlugin.cpp
@@ -0,0 +1,69 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <sstream>
+#include "qpid/acl/Acl.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility/in_place_factory.hpp>
+
+#include "qpid/xml/XmlExchange.h"
+
+namespace qpid {
+namespace broker { // ACL uses the acl namespace here - should I?
+
+using namespace std;
+class Broker;
+
+Exchange::shared_ptr create(const std::string& name, bool durable,
+ const framing::FieldTable& args,
+ management::Manageable* parent,
+ Broker* broker)
+{
+ Exchange::shared_ptr e(new XmlExchange(name, durable, args, parent, broker));
+ return e;
+}
+
+
+class XmlExchangePlugin : public Plugin
+{
+public:
+ void earlyInitialize(Plugin::Target& target);
+ void initialize(Plugin::Target& target);
+};
+
+
+void XmlExchangePlugin::earlyInitialize(Plugin::Target& target)
+{
+ Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ if (broker) {
+ broker->getExchanges().registerType(XmlExchange::typeName, &create);
+ QPID_LOG(info, "Registered xml exchange");
+ }
+}
+
+void XmlExchangePlugin::initialize(Target&) {}
+
+
+static XmlExchangePlugin matchingPlugin;
+
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp
new file mode 100644
index 0000000000..a7c1dbe8a6
--- /dev/null
+++ b/qpid/cpp/src/qpidd.cpp
@@ -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.
+ *
+ */
+
+#include "./qpidd.h"
+#include "qpid/Plugin.h"
+#include "qpid/Version.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Statement.h"
+
+#include <iostream>
+#include <memory>
+using namespace std;
+
+auto_ptr<QpiddOptions> options;
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ BootstrapOptions bootOptions(argv[0]);
+ string defaultPath (bootOptions.module.loadDir);
+ // Parse only the common, load, and log options to see which
+ // modules need to be loaded. Once the modules are loaded,
+ // the command line will be re-parsed with all of the
+ // module-supplied options.
+ try {
+ bootOptions.parse (argc, argv, bootOptions.common.config, true);
+ qpid::log::Logger::instance().configure(bootOptions.log);
+ } catch (const std::exception& e) {
+ // Couldn't configure logging so write the message direct to stderr.
+ cerr << "Unexpected error: " << e.what() << endl;
+ return 1;
+ }
+
+ for (vector<string>::iterator iter = bootOptions.module.load.begin();
+ iter != bootOptions.module.load.end();
+ iter++)
+ qpid::tryShlib (iter->data(), false);
+
+ if (!bootOptions.module.noLoad) {
+ bool isDefault = defaultPath == bootOptions.module.loadDir;
+ qpid::loadModuleDir (bootOptions.module.loadDir, isDefault);
+ }
+
+ // Parse options
+ options.reset(new QpiddOptions(argv[0]));
+ options->parse(argc, argv, options->common.config);
+
+ // Options that just print information.
+ if (options->common.help || options->common.version) {
+ if (options->common.version)
+ cout << "qpidd (" << qpid::product << ") version "
+ << qpid::version << endl;
+ else if (options->common.help)
+ options->usage();
+ return 0;
+ }
+
+ // Everything else is driven by the platform-specific broker
+ // logic.
+ QpiddBroker broker;
+ return broker.execute(options.get());
+ }
+ catch(const exception& e) {
+ QPID_LOG(critical, "Unexpected error: " << e.what());
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/qpidd.h b/qpid/cpp/src/qpidd.h
new file mode 100644
index 0000000000..c702270e80
--- /dev/null
+++ b/qpid/cpp/src/qpidd.h
@@ -0,0 +1,70 @@
+#ifndef QPID_H
+#define QPID_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/Modules.h"
+#include "qpid/Options.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Options.h"
+
+#include <memory>
+
+// BootstrapOptions is a minimal subset of options used for a pre-parse
+// of the command line to discover which plugin modules need to be loaded.
+// The pre-parse is necessary because plugin modules may supply their own
+// set of options. CommonOptions is needed to properly support loading
+// from a configuration file.
+struct BootstrapOptions : public qpid::Options {
+ qpid::CommonOptions common;
+ qpid::ModuleOptions module;
+ qpid::log::Options log;
+
+ BootstrapOptions(const char *argv0);
+};
+
+// Each platform derives an options struct from QpiddOptionsPrivate, adding
+// platform-specific option types. QpiddOptions needs to allocation one of
+// these derived structs from its constructor.
+struct QpiddOptions;
+struct QpiddOptionsPrivate {
+ QpiddOptions *options;
+ QpiddOptionsPrivate(QpiddOptions *parent) : options(parent) {}
+ virtual ~QpiddOptionsPrivate() {}
+protected:
+ QpiddOptionsPrivate() {}
+};
+
+struct QpiddOptions : public qpid::Options {
+ qpid::CommonOptions common;
+ qpid::ModuleOptions module;
+ qpid::broker::Broker::Options broker;
+ qpid::log::Options log;
+ std::auto_ptr<QpiddOptionsPrivate> platform;
+
+ QpiddOptions(const char *argv0);
+ void usage() const;
+};
+
+class QpiddBroker {
+public:
+ int execute (QpiddOptions *options);
+};
+
+#endif /*!QPID_H*/
diff --git a/qpid/cpp/src/rdma.cmake b/qpid/cpp/src/rdma.cmake
new file mode 100644
index 0000000000..e020cb84a9
--- /dev/null
+++ b/qpid/cpp/src/rdma.cmake
@@ -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.
+#
+#
+# RDMA (Remote DMA) wrapper CMake fragment, to be included in CMakeLists.txt
+#
+
+# Optional RDMA support. Requires ibverbs and rdma_cm.
+
+include(CheckIncludeFiles)
+include(CheckLibraryExists)
+
+CHECK_LIBRARY_EXISTS (ibverbs ibv_create_qp "" HAVE_IBVERBS)
+CHECK_LIBRARY_EXISTS (rdmacm rdma_create_id "" HAVE_RDMACM)
+CHECK_INCLUDE_FILES (infiniband/verbs.h HAVE_IBVERBS_H)
+CHECK_INCLUDE_FILES (rdma/rdma_cma.h HAVE_RDMACM_H)
+
+set (rdma_default ${rdma_force})
+if (HAVE_IBVERBS AND HAVE_IBVERBS_H)
+ if (HAVE_RDMACM AND HAVE_RDMACM_H)
+ set (rdma_default ON)
+ endif (HAVE_RDMACM AND HAVE_RDMACM_H)
+endif (HAVE_IBVERBS AND HAVE_IBVERBS_H)
+
+option(BUILD_RDMA "Build with support for Remote DMA protocols" ${rdma_default})
+if (BUILD_RDMA)
+ if (NOT HAVE_IBVERBS)
+ message(FATAL_ERROR "libibverbs not found, required for RDMA support")
+ endif (NOT HAVE_IBVERBS)
+ if (NOT HAVE_RDMACM)
+ message(FATAL_ERROR "librdmacm not found, required for RDMA support")
+ endif (NOT HAVE_RDMACM)
+ if (NOT HAVE_IBVERBS_H)
+ message(FATAL_ERROR "ibverbs headers not found, required for RDMA support")
+ endif (NOT HAVE_IBVERBS_H)
+ if (NOT HAVE_RDMACM_H)
+ message(FATAL_ERROR "rdmacm headers not found, required for RDMA support")
+ endif (NOT HAVE_RDMACM_H)
+
+ set (rdma_SOURCES
+ qpid/sys/rdma/rdma_exception.h
+ qpid/sys/rdma/rdma_factories.cpp
+ qpid/sys/rdma/rdma_factories.h
+ qpid/sys/rdma/RdmaIO.cpp
+ qpid/sys/rdma/RdmaIO.h
+ qpid/sys/rdma/rdma_wrap.cpp
+ qpid/sys/rdma/rdma_wrap.h
+ )
+
+ add_library (rdmawrap SHARED ${rdma_SOURCES})
+ target_link_libraries (rdmawrap qpidcommon rdmacm ibverbs)
+ set_target_properties (rdmawrap PROPERTIES VERSION ${qpidc_version})
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(rdmawrap PROPERTIES
+ COMPILE_FLAGS -Wno-missing-field-initializers
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS rdmawrap
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_COMMON})
+
+ add_library (rdma MODULE qpid/sys/RdmaIOPlugin.cpp)
+ target_link_libraries (rdma qpidbroker rdmawrap)
+ set_target_properties (rdma PROPERTIES
+ PREFIX "")
+
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(rdma PROPERTIES
+ COMPILE_FLAGS -Wno-missing-field-initializers
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS rdma
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+ add_library (rdmaconnector MODULE qpid/client/RdmaConnector.cpp)
+ target_link_libraries (rdmaconnector qpidclient rdmawrap)
+ set_target_properties (rdmaconnector PROPERTIES
+ PREFIX "")
+
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(rdmaconnector PROPERTIES
+ COMPILE_FLAGS -Wno-missing-field-initializers
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS rdmaconnector
+ DESTINATION ${QPIDC_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+
+ # RDMA test/sample programs
+ add_executable (RdmaServer qpid/sys/rdma/RdmaServer.cpp)
+ target_link_libraries (RdmaServer rdmawrap qpidcommon)
+ add_executable (RdmaClient qpid/sys/rdma/RdmaClient.cpp)
+ target_link_libraries (RdmaClient rdmawrap qpidcommon)
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(RdmaClient PROPERTIES
+ COMPILE_FLAGS -Wno-missing-field-initializers)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+endif (BUILD_RDMA)
diff --git a/qpid/cpp/src/replication.mk b/qpid/cpp/src/replication.mk
new file mode 100644
index 0000000000..e5da32f88b
--- /dev/null
+++ b/qpid/cpp/src/replication.mk
@@ -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.
+
+# Make file for building two plugins for asynchronously replicating
+# queues.
+
+dmoduleexec_LTLIBRARIES += replicating_listener.la replication_exchange.la
+
+# a queue event listener plugin that creates messages on a replication
+# queue corresponding to enqueue and dequeue events:
+replicating_listener_la_SOURCES = \
+ qpid/replication/constants.h \
+ qpid/replication/ReplicatingEventListener.cpp \
+ qpid/replication/ReplicatingEventListener.h
+
+replicating_listener_la_LIBADD = libqpidbroker.la
+if SUNOS
+ replicating_listener_la_LIBADD += libqpidcommon.la -lboost_program_options -luuid $(SUNCC_RUNTIME_LIBS)
+endif
+replicating_listener_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+# a custom exchange plugin that allows an exchange to be created that
+# can process the messages from a replication queue (populated on the
+# source system by the replicating listener plugin above) and take the
+# corresponding action on the local queues
+replication_exchange_la_SOURCES = \
+ qpid/replication/constants.h \
+ qpid/replication/ReplicationExchange.cpp \
+ qpid/replication/ReplicationExchange.h
+
+replication_exchange_la_LIBADD = libqpidbroker.la
+
+if SUNOS
+ replication_exchange_la_LIBADD += libqpidcommon.la -lboost_program_options $(SUNCC_RUNTIME_LIBS) -luuid
+endif
+replication_exchange_la_LDFLAGS = $(PLUGINLDFLAGS)
+
diff --git a/qpid/cpp/src/ssl.cmake b/qpid/cpp/src/ssl.cmake
new file mode 100644
index 0000000000..c205845388
--- /dev/null
+++ b/qpid/cpp/src/ssl.cmake
@@ -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.
+#
+#
+# SSL/TLS CMake fragment, to be included in CMakeLists.txt
+#
+
+# Optional SSL/TLS support. Requires Netscape Portable Runtime on Linux.
+
+include(FindPkgConfig)
+
+# According to some cmake docs this is not a reliable way to detect
+# pkg-configed libraries, but it's no worse than what we did under
+# autotools
+pkg_check_modules(NSS nss)
+
+set (ssl_default ${ssl_force})
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set (ssl_default ON)
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (NSS_FOUND)
+ set (ssl_default ON)
+ endif (NSS_FOUND)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+option(BUILD_SSL "Build with support for SSL" ${ssl_default})
+if (BUILD_SSL)
+ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set (sslclient_windows_SOURCES qpid/client/windows/SslConnector.cpp)
+ set (sslbroker_windows_SOURCES qpid/broker/windows/SslProtocolFactory.cpp)
+ set (sslcommon_windows_SOURCES
+ qpid/sys/windows/SslAsynchIO.cpp
+ )
+ set (windows_ssl_libs Secur32.lib)
+ set (windows_ssl_server_libs Crypt32.lib)
+ else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+ if (NOT NSS_FOUND)
+ message(FATAL_ERROR "nss/nspr not found, required for ssl support")
+ endif (NOT NSS_FOUND)
+
+ foreach(f ${NSS_CFLAGS})
+ set (NSS_COMPILE_FLAGS "${NSS_COMPILE_FLAGS} ${f}")
+ endforeach(f)
+
+ foreach(f ${NSS_LDFLAGS})
+ set (NSS_LINK_FLAGS "${NSS_LINK_FLAGS} ${f}")
+ endforeach(f)
+
+ set (sslcommon_SOURCES
+ qpid/sys/ssl/check.h
+ qpid/sys/ssl/check.cpp
+ qpid/sys/ssl/util.h
+ qpid/sys/ssl/util.cpp
+ qpid/sys/ssl/SslSocket.h
+ qpid/sys/ssl/SslSocket.cpp
+ qpid/sys/ssl/SslIo.h
+ qpid/sys/ssl/SslIo.cpp
+ )
+
+ add_library (sslcommon SHARED ${sslcommon_SOURCES})
+ target_link_libraries (sslcommon qpidcommon)
+ set_target_properties (sslcommon PROPERTIES
+ VERSION ${qpidc_version}
+ COMPILE_FLAGS ${NSS_COMPILE_FLAGS}
+ LINK_FLAGS ${NSS_LINK_FLAGS})
+
+ set (ssl_SOURCES
+ qpid/sys/SslPlugin.cpp
+ qpid/sys/ssl/SslHandler.h
+ qpid/sys/ssl/SslHandler.cpp
+ )
+ add_library (ssl MODULE ${ssl_SOURCES})
+ target_link_libraries (ssl qpidbroker sslcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ set_target_properties (ssl PROPERTIES
+ PREFIX ""
+ COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(ssl PROPERTIES
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS ssl
+ DESTINATION ${QPIDD_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_BROKER})
+
+ add_library (sslconnector MODULE qpid/client/SslConnector.cpp)
+ target_link_libraries (sslconnector qpidclient sslcommon)
+ set_target_properties (sslconnector PROPERTIES
+ PREFIX ""
+ COMPILE_FLAGS ${NSS_COMPILE_FLAGS})
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(sslconnector PROPERTIES
+ LINK_FLAGS -Wl,--no-undefined)
+ endif (CMAKE_COMPILER_IS_GNUCXX)
+
+ install (TARGETS sslconnector
+ DESTINATION ${QPIDC_MODULE_DIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT})
+ endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+endif (BUILD_SSL)
diff --git a/qpid/cpp/src/ssl.mk b/qpid/cpp/src/ssl.mk
new file mode 100644
index 0000000000..4dba9bb61c
--- /dev/null
+++ b/qpid/cpp/src/ssl.mk
@@ -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.
+#
+#
+# Makefile fragment, conditionally included in Makefile.am
+#
+libsslcommon_la_SOURCES = \
+ qpid/sys/ssl/check.h \
+ qpid/sys/ssl/check.cpp \
+ qpid/sys/ssl/util.h \
+ qpid/sys/ssl/util.cpp \
+ qpid/sys/ssl/SslSocket.h \
+ qpid/sys/ssl/SslSocket.cpp \
+ qpid/sys/ssl/SslIo.h \
+ qpid/sys/ssl/SslIo.cpp
+
+SSLCOMMON_VERSION_INFO = 2:0:0
+libsslcommon_la_LDFLAGS = -version-info $(SSLCOMMON_VERSION_INFO)
+libsslcommon_la_LIBADD= -lnss3 -lssl3 -lnspr4 libqpidcommon.la
+libsslcommon_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS)
+
+lib_LTLIBRARIES += libsslcommon.la
+
+ssl_la_SOURCES = \
+ qpid/sys/SslPlugin.cpp \
+ qpid/sys/ssl/SslHandler.h \
+ qpid/sys/ssl/SslHandler.cpp
+
+ssl_la_LIBADD= libqpidbroker.la libsslcommon.la
+
+ssl_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS)
+
+ssl_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+dmoduleexec_LTLIBRARIES += ssl.la
+
+sslconnector_la_SOURCES = \
+ qpid/client/SslConnector.cpp
+
+sslconnector_la_LIBADD = \
+ libqpidclient.la \
+ libsslcommon.la
+
+sslconnector_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\" $(SSL_CFLAGS)
+
+sslconnector_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+cmoduleexec_LTLIBRARIES += \
+ sslconnector.la
diff --git a/qpid/cpp/src/tests/.valgrind.supp b/qpid/cpp/src/tests/.valgrind.supp
new file mode 100644
index 0000000000..2c6a1509ff
--- /dev/null
+++ b/qpid/cpp/src/tests/.valgrind.supp
@@ -0,0 +1,202 @@
+{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Reported on FC5 and RHEL5 when md5 sasl libs are installed
+ Memcheck:Leak
+ fun:*
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:*
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ Benign leak in CPG - patched version.
+ Memcheck:Leak
+ fun:*
+ fun:openais_service_connect
+ fun:cpg_initialize
+}
+
+{
+ Benign error in libcpg.
+ Memcheck:Param
+ socketcall.sendmsg(msg.msg_iov[i])
+ obj:*/libpthread-2.5.so
+ obj:*/libcpg.so.2.0.0
+}
+
+{
+ Uninitialised value problem in _dl_relocate (F7, F8)
+ Memcheck:Cond
+ fun:_dl_relocate_object
+ fun:*dl_*
+}
+
+{
+ False "possibly leaked" in boost program_options - global std::string var.
+ Memcheck:Leak
+ fun:_Znwj
+ fun:_ZNSs4_Rep9_S_createEjjRKSaIcE
+ obj:/usr/lib/libstdc++.so.6.0.8
+ fun:_ZNSsC1EPKcRKSaIcE
+ obj:/usr/lib/libboost_program_options.so.1.33.1
+}
+
+{
+ INVESTIGATE
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid6client9Connector4initEv
+ fun:_ZN4qpid6client14ConnectionImpl4openERKSsiS3_S3_S3_
+}
+
+{
+ INVESTIGATE
+ Memcheck:Param
+ write(buf)
+ obj:/lib64/tls/libc-2.3.4.so
+ fun:_ZNK4qpid3sys6Socket5writeEPKvm
+ fun:_ZN4qpid3sys8AsynchIO9writeableERNS0_14DispatchHandleE
+}
+
+{
+ "Conditional jump or move depends on uninitialised value(s)" from Xerces parser
+ Memcheck:Cond
+ fun:_ZN11xercesc_2_717XMLUTF8Transcoder13transcodeFromEPKhjPtjRjPh
+ fun:_ZN11xercesc_2_79XMLReader14xcodeMoreCharsEPtPhj
+ fun:_ZN11xercesc_2_79XMLReader17refreshCharBufferEv
+}
+
+{
+ INVESTIGATE
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:send
+ fun:get_mapping
+ fun:__nscd_get_map_ref
+ fun:nscd_gethst_r
+ fun:__nscd_gethostbyname_r
+ fun:gethostbyname_r@@GLIBC_2.2.5
+ fun:gethostbyname
+ fun:_ZNK4qpid3sys6Socket7connectERKSsi
+}
+
+{
+ INVESTIGATE
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid6broker5Timer5startEv
+ fun:_ZN4qpid6broker5TimerC1Ev
+ fun:_ZN4qpid6broker10DtxManagerC1Ev
+ fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE
+ fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE
+ fun:_ZN20ClientSessionFixtureC1Ev
+ fun:_Z14testQueueQueryv
+ fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv
+ obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0
+ fun:_ZN5boost17execution_monitor7executeEbi
+ fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi
+ fun:_ZN5boost9unit_test9test_case3runEv
+ fun:_ZN5boost9unit_test10test_suite6do_runEv
+ fun:_ZN5boost9unit_test9test_case3runEv
+ fun:main
+}
+
+{
+ INVESTIGATE
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid6client9Connector4initEv
+}
+
+{
+ MICK -- FIX
+ Memcheck:Leak
+ fun:_Znam
+ fun:_ZN4qpid7Options5parseEiPPcRKSsb
+}
+
+{
+ MICK -- FIX
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ fun:_ZN4qpid7Options5parseEiPPcRKSsb
+}
+
+{
+ CPG error - seems benign.
+ Memcheck:Param
+ socketcall.sendmsg(msg.msg_iov[i])
+ obj:*
+ obj:*/libcpg.so.2.0.0
+}
+
+{
+ Known leak in boost.thread 1.33.1. Wildcards for 64/32 bit diffs.
+ Memcheck:Leak
+ fun:*
+ obj:/usr/*/libboost_thread.so.1.33.1
+ fun:_ZN5boost6detail3tss3setEPv
+}
+
+{
+ Shows up on RHEL5: believed benign
+ Memcheck:Cond
+ fun:__strcpy_chk
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr8
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr4
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
+}
+
diff --git a/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/qpid/cpp/src/tests/AccumulatedAckTest.cpp
new file mode 100644
index 0000000000..c736a519d2
--- /dev/null
+++ b/qpid/cpp/src/tests/AccumulatedAckTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/AccumulatedAck.h"
+#include "unit_test.h"
+#include <iostream>
+#include <list>
+
+using std::list;
+using namespace qpid::framing;
+
+
+namespace qpid {
+namespace tests {
+
+bool covers(const AccumulatedAck& ack, int i)
+{
+ return ack.covers(SequenceNumber(i));
+}
+
+void update(AccumulatedAck& ack, int start, int end)
+{
+ ack.update(SequenceNumber(start), SequenceNumber(end));
+}
+
+QPID_AUTO_TEST_SUITE(AccumulatedAckTestSuite)
+
+QPID_AUTO_TEST_CASE(testGeneral)
+{
+ AccumulatedAck ack(0);
+ ack.clear();
+ update(ack, 3,3);
+ update(ack, 7,7);
+ update(ack, 9,9);
+ update(ack, 1,2);
+ update(ack, 4,5);
+ update(ack, 6,6);
+
+ for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(covers(ack, 9));
+
+ BOOST_CHECK(!covers(ack, 8));
+ BOOST_CHECK(!covers(ack, 10));
+
+ ack.consolidate();
+
+ for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(covers(ack, 9));
+
+ BOOST_CHECK(!covers(ack, 8));
+ BOOST_CHECK(!covers(ack, 10));
+}
+
+QPID_AUTO_TEST_CASE(testCovers)
+{
+ AccumulatedAck ack(5);
+ update(ack, 7, 7);
+ update(ack, 9, 9);
+
+ BOOST_CHECK(covers(ack, 1));
+ BOOST_CHECK(covers(ack, 2));
+ BOOST_CHECK(covers(ack, 3));
+ BOOST_CHECK(covers(ack, 4));
+ BOOST_CHECK(covers(ack, 5));
+ BOOST_CHECK(covers(ack, 7));
+ BOOST_CHECK(covers(ack, 9));
+
+ BOOST_CHECK(!covers(ack, 6));
+ BOOST_CHECK(!covers(ack, 8));
+ BOOST_CHECK(!covers(ack, 10));
+}
+
+QPID_AUTO_TEST_CASE(testUpdateFromCompletionData)
+{
+ AccumulatedAck ack(0);
+ SequenceNumber mark(2);
+ SequenceNumberSet ranges;
+ ranges.addRange(SequenceNumber(5), SequenceNumber(8));
+ ranges.addRange(SequenceNumber(10), SequenceNumber(15));
+ ranges.addRange(SequenceNumber(9), SequenceNumber(9));
+ ranges.addRange(SequenceNumber(3), SequenceNumber(4));
+
+ ack.update(mark, ranges);
+
+ for(int i = 0; i <= 15; i++) {
+ BOOST_CHECK(covers(ack, i));
+ }
+ BOOST_CHECK(!covers(ack, 16));
+ BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue());
+}
+
+QPID_AUTO_TEST_CASE(testCase1)
+{
+ AccumulatedAck ack(3);
+ update(ack, 1,2);
+ for(int i = 1; i <= 3; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(!covers(ack, 4));
+}
+
+QPID_AUTO_TEST_CASE(testCase2)
+{
+ AccumulatedAck ack(3);
+ update(ack, 3,6);
+ for(int i = 1; i <= 6; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(!covers(ack, 7));
+}
+
+QPID_AUTO_TEST_CASE(testCase3)
+{
+ AccumulatedAck ack(3);
+ update(ack, 4,6);
+ for(int i = 1; i <= 6; i++) {
+ BOOST_CHECK(covers(ack, i));
+ }
+ BOOST_CHECK(!covers(ack, 7));
+}
+
+QPID_AUTO_TEST_CASE(testCase4)
+{
+ AccumulatedAck ack(3);
+ update(ack, 5,6);
+ for(int i = 1; i <= 6; i++) {
+ if (i == 4) BOOST_CHECK(!covers(ack, i));
+ else BOOST_CHECK(covers(ack, i));
+ }
+ BOOST_CHECK(!covers(ack, 7));
+}
+
+QPID_AUTO_TEST_CASE(testConsolidation1)
+{
+ AccumulatedAck ack(3);
+ update(ack, 7,7);
+ BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+
+ update(ack, 8,9);
+ BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+
+ update(ack, 1,2);
+ BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+
+ update(ack, 4,5);
+ BOOST_CHECK_EQUAL((uint32_t) 5, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+
+ update(ack, 6,6);
+ BOOST_CHECK_EQUAL((uint32_t) 9, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size());
+
+ for(int i = 1; i <= 9; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(!covers(ack, 10));
+}
+
+QPID_AUTO_TEST_CASE(testConsolidation2)
+{
+ AccumulatedAck ack(0);
+ update(ack, 10,12);
+ BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+
+ update(ack, 7,9);
+ BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+ BOOST_CHECK_EQUAL((uint32_t) 7, ack.ranges.front().start.getValue());
+ BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue());
+
+ update(ack, 5,7);
+ BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+ BOOST_CHECK_EQUAL((uint32_t) 5, ack.ranges.front().start.getValue());
+ BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue());
+
+ update(ack, 3,4);
+ BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size());
+ BOOST_CHECK_EQUAL((uint32_t) 3, ack.ranges.front().start.getValue());
+ BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue());
+
+ update(ack, 1,2);
+ BOOST_CHECK_EQUAL((uint32_t) 12, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size());
+
+ for(int i = 1; i <= 12; i++) BOOST_CHECK(covers(ack, i));
+ BOOST_CHECK(!covers(ack, 13));
+}
+
+QPID_AUTO_TEST_CASE(testConsolidation3)
+{
+ AccumulatedAck ack(0);
+ update(ack, 10,12);
+ update(ack, 6,7);
+ update(ack, 3,4);
+ update(ack, 1,15);
+ BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue());
+ BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size());
+}
+
+QPID_AUTO_TEST_CASE(testConsolidation4)
+{
+ AccumulatedAck ack(0);
+ ack.update(SequenceNumber(0), SequenceNumber(2));
+ ack.update(SequenceNumber(5), SequenceNumber(8));
+ ack.update(SequenceNumber(10), SequenceNumber(15));
+ ack.update(SequenceNumber(9), SequenceNumber(9));
+ ack.update(SequenceNumber(3), SequenceNumber(4));
+
+ for(int i = 0; i <= 15; i++) {
+ BOOST_CHECK(covers(ack, i));
+ }
+ BOOST_CHECK(!covers(ack, 16));
+ BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Address.cpp b/qpid/cpp/src/tests/Address.cpp
new file mode 100644
index 0000000000..f41f27b6df
--- /dev/null
+++ b/qpid/cpp/src/tests/Address.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/messaging/Address.h"
+#include "qpid/types/Variant.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AddressSuite)
+
+QPID_AUTO_TEST_CASE(testParseNameOnly)
+{
+ Address address("my-topic");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubject)
+{
+ Address address("my-topic/my-subject");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptions)
+{
+ Address address("my-topic; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"]);
+ BOOST_CHECK_EQUAL(101, static_cast<int>(address.getOptions()["x"]));
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+
+ // Test asString() and asInt64() once here
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"].asString());
+ BOOST_CHECK_EQUAL(static_cast<uint16_t>(101), address.getOptions()["x"].asInt64());
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"].asString());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubjectAndOptions)
+{
+ Address address("my-topic/my-subject; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"]);
+ BOOST_CHECK_EQUAL(101, static_cast<int>(address.getOptions()["x"]));
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+}
+
+QPID_AUTO_TEST_CASE(testParseNestedOptions)
+{
+ Address address("my-topic; {a:{p:202, q:'another string'}, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(202, static_cast<int>(address.getOptions()["a"].asMap()["p"]));
+ BOOST_CHECK_EQUAL(std::string("another string"), address.getOptions()["a"].asMap()["q"]);
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithList)
+{
+ Address address("my-topic; {a:[202, 'another string'], x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::List& list = address.getOptions()["a"].asList();
+ Variant::List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL((uint16_t) 202, i->asInt64());
+ BOOST_CHECK(++i != list.end());
+ BOOST_CHECK_EQUAL(std::string("another string"), i->asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyList)
+{
+ Address address("my-topic; {a:[], x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::List& list = address.getOptions()["a"].asList();
+ BOOST_CHECK_EQUAL(list.size(), (size_t) 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyMap)
+{
+ Address address("my-topic; {a:{}, x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::Map& map = address.getOptions()["a"].asMap();
+ BOOST_CHECK_EQUAL(map.size(), (size_t) 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseQuotedNameAndSubject)
+{
+ Address address("'my topic with / in it'/'my subject with ; in it'");
+ BOOST_CHECK_EQUAL(std::string("my topic with / in it"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my subject with ; in it"), address.getSubject());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp
new file mode 100644
index 0000000000..7622b89d15
--- /dev/null
+++ b/qpid/cpp/src/tests/Array.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include <sstream>
+#include "qpid/framing/Array.h"
+#include "qpid/framing/FieldValue.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ArrayTestSuite)
+
+using namespace qpid::framing;
+
+void populate(std::vector<std::string>& data, int count = 10)
+{
+ for (int i = 0; i < count; i++) {
+ std::stringstream out;
+ out << "item-" << i;
+ data.push_back(out.str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testEncodeDecode)
+{
+ std::vector<std::string> data;
+ populate(data);
+
+ Array a(data);
+
+ char buff[200];
+ Buffer wbuffer(buff, 200);
+ a.encode(wbuffer);
+
+ Array b;
+ Buffer rbuffer(buff, 200);
+ b.decode(rbuffer);
+ BOOST_CHECK_EQUAL(a, b);
+
+ std::vector<std::string> data2;
+ b.collect(data2);
+ //BOOST_CHECK_EQUAL(data, data2);
+ BOOST_CHECK(data == data2);
+}
+
+QPID_AUTO_TEST_CASE(testArrayAssignment)
+{
+ std::vector<std::string> data;
+ populate(data);
+ Array b;
+ {
+ Array a(data);
+ b = a;
+ BOOST_CHECK_EQUAL(a, b);
+ }
+ std::vector<std::string> data2;
+ b.collect(data2);
+ //BOOST_CHECK_EQUAL(data, data2);
+ BOOST_CHECK(data == data2);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/AsyncCompletion.cpp b/qpid/cpp/src/tests/AsyncCompletion.cpp
new file mode 100644
index 0000000000..e32097106f
--- /dev/null
+++ b/qpid/cpp/src/tests/AsyncCompletion.cpp
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/QueueQueryResult.h"
+#include "qpid/client/TypedResult.h"
+
+using namespace std;
+using namespace qpid;
+using namespace client;
+using namespace framing;
+
+namespace qpid { namespace broker {
+class TransactionContext;
+class PersistableQueue;
+}}
+
+using broker::PersistableMessage;
+using broker::NullMessageStore;
+using broker::TransactionContext;
+using broker::PersistableQueue;
+using sys::TIME_SEC;
+using boost::intrusive_ptr;
+
+/** @file Unit tests for async completion.
+ * Using a dummy store, verify that the broker indicates async completion of
+ * message enqueues at the correct time.
+ */
+
+namespace qpid {
+namespace tests {
+
+class AsyncCompletionMessageStore : public NullMessageStore {
+ public:
+ sys::BlockingQueue<boost::intrusive_ptr<PersistableMessage> > enqueued;
+
+ AsyncCompletionMessageStore() : NullMessageStore() {}
+ ~AsyncCompletionMessageStore(){}
+
+ void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& )
+ {
+ enqueued.push(msg);
+ }
+};
+
+QPID_AUTO_TEST_SUITE(AsyncCompletionTestSuite)
+
+QPID_AUTO_TEST_CASE(testWaitTillComplete) {
+ SessionFixture fix;
+ AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore;
+ boost::shared_ptr<qpid::broker::MessageStore> p;
+ p.reset(store);
+ fix.broker->setStore(p);
+ AsyncSession s = fix.session;
+
+ static const int count = 3;
+
+ s.queueDeclare("q", arg::durable=true);
+ Completion transfers[count];
+ for (int i = 0; i < count; ++i) {
+ Message msg(boost::lexical_cast<string>(i), "q");
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ transfers[i] = s.messageTransfer(arg::content=msg);
+ }
+
+ // Get hold of the broker-side messages.
+ typedef vector<intrusive_ptr<PersistableMessage> > BrokerMessages;
+ BrokerMessages enqueued;
+ for (int j = 0; j < count; ++j)
+ enqueued.push_back(store->enqueued.pop(TIME_SEC));
+
+ // Send a sync, make sure it does not complete till all messages are complete.
+ // In reverse order for fun.
+ Completion sync = s.executionSync(arg::sync=true);
+ for (int k = count-1; k >= 0; --k) {
+ BOOST_CHECK(!transfers[k].isComplete()); // Should not be complete yet.
+ BOOST_CHECK(!sync.isComplete()); // Should not be complete yet.
+ enqueued[k]->enqueueComplete();
+ }
+ sync.wait(); // Should complete now, all messages are completed.
+}
+
+QPID_AUTO_TEST_CASE(testGetResult) {
+ SessionFixture fix;
+ AsyncSession s = fix.session;
+
+ s.queueDeclare("q", arg::durable=true);
+ TypedResult<QueueQueryResult> tr = s.queueQuery("q");
+ QueueQueryResult qq = tr.get();
+ BOOST_CHECK_EQUAL(qq.getQueue(), "q");
+ BOOST_CHECK_EQUAL(qq.getMessageCount(), 0U);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/AtomicValue.cpp b/qpid/cpp/src/tests/AtomicValue.cpp
new file mode 100644
index 0000000000..d855d993a7
--- /dev/null
+++ b/qpid/cpp/src/tests/AtomicValue.cpp
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/sys/AtomicValue.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AtomicValueTestSuite)
+
+QPID_AUTO_TEST_CASE(test) {
+ qpid::sys::AtomicValue<int> x(0);
+ BOOST_CHECK_EQUAL(++x, 1);
+ BOOST_CHECK_EQUAL(--x,0);
+ BOOST_CHECK_EQUAL(x+=5,5);
+ BOOST_CHECK_EQUAL(x-=10,-5);
+ BOOST_CHECK_EQUAL(x.fetchAndAdd(7), -5);
+ BOOST_CHECK_EQUAL(x.get(),2);
+ BOOST_CHECK_EQUAL(x.fetchAndSub(3), 2);
+ BOOST_CHECK_EQUAL(x.get(),-1);
+
+ BOOST_CHECK_EQUAL(x.valueCompareAndSwap(-1,10), -1);
+ BOOST_CHECK_EQUAL(x.get(), 10);
+ BOOST_CHECK_EQUAL(x.valueCompareAndSwap(5, 6), 10);
+ BOOST_CHECK_EQUAL(x.get(), 10);
+
+ BOOST_CHECK(!x.boolCompareAndSwap(5, 6));
+ BOOST_CHECK_EQUAL(x.get(), 10);
+ BOOST_CHECK(x.boolCompareAndSwap(10, 6));
+ BOOST_CHECK_EQUAL(x.get(), 6);
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Blob.cpp b/qpid/cpp/src/tests/Blob.cpp
new file mode 100644
index 0000000000..9878d92fe4
--- /dev/null
+++ b/qpid/cpp/src/tests/Blob.cpp
@@ -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.
+ *
+ */
+
diff --git a/qpid/cpp/src/tests/BrokerFixture.h b/qpid/cpp/src/tests/BrokerFixture.h
new file mode 100644
index 0000000000..672d954572
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerFixture.h
@@ -0,0 +1,154 @@
+#ifndef TESTS_BROKERFIXTURE_H
+#define TESTS_BROKERFIXTURE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "SocketProxy.h"
+
+#include "qpid/broker/Broker.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/LocalQueue.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/sys/Thread.h"
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+namespace tests {
+
+/**
+ * A fixture with an in-process broker.
+ */
+struct BrokerFixture : private boost::noncopyable {
+ typedef qpid::broker::Broker Broker;
+ typedef boost::intrusive_ptr<Broker> BrokerPtr;
+
+ BrokerPtr broker;
+ qpid::sys::Thread brokerThread;
+
+ BrokerFixture(Broker::Options opts=Broker::Options(), bool enableMgmt=false) {
+ // Keep the tests quiet unless logging env. vars have been set by user.
+ if (!::getenv("QPID_LOG_ENABLE") && !::getenv("QPID_TRACE")) {
+ qpid::log::Options logOpts;
+ logOpts.selectors.clear();
+ logOpts.selectors.push_back("error+");
+ qpid::log::Logger::instance().configure(logOpts);
+ }
+ opts.port=0;
+ // Management doesn't play well with multiple in-process brokers.
+ opts.enableMgmt=enableMgmt;
+ opts.workerThreads=1;
+ opts.dataDir="";
+ opts.auth=false;
+ broker = Broker::create(opts);
+ // TODO aconway 2007-12-05: At one point BrokerFixture
+ // tests could hang in Connection ctor if the following
+ // line is removed. This may not be an issue anymore.
+ broker->accept();
+ broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+ brokerThread = qpid::sys::Thread(*broker);
+ };
+
+ void shutdownBroker()
+ {
+ broker->shutdown();
+ broker = BrokerPtr();
+ }
+
+ ~BrokerFixture() {
+ if (broker) broker->shutdown();
+ brokerThread.join();
+ }
+
+ /** Open a connection to the broker. */
+ void open(qpid::client::Connection& c) {
+ c.open("localhost", broker->getPort(qpid::broker::Broker::TCP_TRANSPORT));
+ }
+
+ uint16_t getPort() { return broker->getPort(qpid::broker::Broker::TCP_TRANSPORT); }
+};
+
+/** Connection that opens in its constructor */
+struct LocalConnection : public qpid::client::Connection {
+ LocalConnection(uint16_t port) { open("localhost", port); }
+ LocalConnection(const qpid::client::ConnectionSettings& s) { open(s); }
+ ~LocalConnection() { close(); }
+};
+
+/** A local client connection via a socket proxy. */
+struct ProxyConnection : public qpid::client::Connection {
+ SocketProxy proxy;
+ ProxyConnection(int brokerPort) : proxy(brokerPort) {
+ open("localhost", proxy.getPort());
+ }
+ ProxyConnection(const qpid::client::ConnectionSettings& s) : proxy(s.port) {
+ qpid::client::ConnectionSettings proxySettings(s);
+ proxySettings.port = proxy.getPort();
+ open(proxySettings);
+ }
+ ~ProxyConnection() { close(); }
+};
+
+/** Convenience class to create and open a connection and session
+ * and some related useful objects.
+ */
+template <class ConnectionType=LocalConnection, class SessionType=qpid::client::Session>
+struct ClientT {
+ ConnectionType connection;
+ SessionType session;
+ qpid::client::SubscriptionManager subs;
+ qpid::client::LocalQueue lq;
+ std::string name;
+
+ ClientT(uint16_t port, const std::string& name_=std::string(), int timeout=0)
+ : connection(port), session(connection.newSession(name_,timeout)), subs(session), name(name_) {}
+ ClientT(const qpid::client::ConnectionSettings& settings, const std::string& name_=std::string(), int timeout=0)
+ : connection(settings), session(connection.newSession(name_, timeout)), subs(session), name(name_) {}
+
+ ~ClientT() { close(); }
+ void close() { session.close(); connection.close(); }
+};
+
+typedef ClientT<> Client;
+
+/**
+ * A BrokerFixture and ready-connected BrokerFixture::Client all in one.
+ */
+template <class ConnectionType, class SessionType=qpid::client::Session>
+struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> {
+
+ SessionFixtureT(Broker::Options opts=Broker::Options()) :
+ BrokerFixture(opts),
+ ClientT<ConnectionType,SessionType>(broker->getPort(qpid::broker::Broker::TCP_TRANSPORT))
+ {}
+
+};
+
+typedef SessionFixtureT<LocalConnection> SessionFixture;
+typedef SessionFixtureT<ProxyConnection> ProxySessionFixture;
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_BROKERFIXTURE_H*/
diff --git a/qpid/cpp/src/tests/BrokerMgmtAgent.cpp b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
new file mode 100644
index 0000000000..d0c6668b72
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
@@ -0,0 +1,792 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+#include "MessagingFixture.h"
+#include "qpid/management/Buffer.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+
+#include "qmf/org/apache/qpid/broker/mgmt/test/TestObject.h"
+
+#include <iomanip>
+
+
+using qpid::management::Mutex;
+using qpid::management::Manageable;
+using qpid::management::Buffer;
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+
+
+namespace qpid {
+ namespace tests {
+
+ namespace _qmf = qmf::org::apache::qpid::broker::mgmt::test;
+ namespace {
+
+ typedef boost::shared_ptr<_qmf::TestObject> TestObjectPtr;
+ typedef std::vector<TestObjectPtr> TestObjectVector;
+
+ // Instantiates a broker and its internal management agent. Provides
+ // factories for constructing Receivers for object indication messages.
+ //
+ class AgentFixture
+ {
+ MessagingFixture *mFix;
+
+ public:
+ AgentFixture( unsigned int pubInterval=10,
+ bool qmfV2=false,
+ qpid::broker::Broker::Options opts = qpid::broker::Broker::Options())
+ {
+ opts.enableMgmt=true;
+ opts.qmf2Support=qmfV2;
+ opts.mgmtPubInterval=pubInterval;
+ mFix = new MessagingFixture(opts, true);
+
+ _qmf::TestObject::registerSelf(getBrokerAgent());
+ };
+ ~AgentFixture()
+ {
+ delete mFix;
+ };
+ ::qpid::management::ManagementAgent *getBrokerAgent() { return mFix->broker->getManagementAgent(); }
+ Receiver createV1DataIndRcvr( const std::string package, const std::string klass )
+ {
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qpid.management, "
+ "key: 'console.obj.1.0.")
+ + package + std::string(".") + klass
+ + std::string("'}]}}"));
+ };
+ Receiver createV2DataIndRcvr( const std::string package, const std::string klass )
+ {
+ std::string p(package);
+ std::replace(p.begin(), p.end(), '.', '_');
+ std::string k(klass);
+ std::replace(k.begin(), k.end(), '.', '_');
+
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qmf.default.topic, "
+ "key: 'agent.ind.data.")
+ + p + std::string(".") + k
+ + std::string("'}]}}"));
+ };
+ };
+
+
+ // A "management object" that supports the TestObject
+ //
+ class TestManageable : public qpid::management::Manageable
+ {
+ management::ManagementObject* mgmtObj;
+ const std::string key;
+ public:
+ TestManageable(management::ManagementAgent *agent, std::string _key)
+ : key(_key)
+ {
+ _qmf::TestObject *tmp = new _qmf::TestObject(agent, this);
+
+ // seed it with some default values...
+ tmp->set_string1(key);
+ tmp->set_bool1(true);
+ qpid::types::Variant::Map vMap;
+ vMap["one"] = qpid::types::Variant(1);
+ vMap["two"] = qpid::types::Variant("two");
+ vMap["three"] = qpid::types::Variant("whatever");
+ tmp->set_map1(vMap);
+
+ mgmtObj = tmp;
+ };
+ ~TestManageable() { mgmtObj = 0; /* deleted by agent on shutdown */ };
+ management::ManagementObject* GetManagementObject() const { return mgmtObj; };
+ static void validateTestObjectProperties(_qmf::TestObject& to)
+ {
+ // verify the default values are as expected. We don't check 'string1',
+ // as it is the object key, and is unique for each object (no default value).
+ BOOST_CHECK(to.get_bool1() == true);
+ BOOST_CHECK(to.get_map1().size() == 3);
+ qpid::types::Variant::Map mappy = to.get_map1();
+ BOOST_CHECK(1 == (unsigned int)mappy["one"]);
+ BOOST_CHECK(mappy["two"].asString() == std::string("two"));
+ BOOST_CHECK(mappy["three"].asString() == std::string("whatever"));
+ };
+ };
+
+
+ // decode a V1 Content Indication message
+ //
+ void decodeV1ObjectUpdates(const Message& inMsg, TestObjectVector& objs, const size_t objLen)
+ {
+ const size_t MAX_BUFFER_SIZE=65536;
+ char tmp[MAX_BUFFER_SIZE];
+
+ objs.clear();
+
+ BOOST_CHECK(inMsg.getContent().size() <= MAX_BUFFER_SIZE);
+
+ ::memcpy(tmp, inMsg.getContent().data(), inMsg.getContent().size());
+ Buffer buf(tmp, inMsg.getContent().size());
+
+ while (buf.available() > 8) { // 8 == qmf v1 header size
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'A');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'M');
+ BOOST_CHECK_EQUAL(buf.getOctet(), '2');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'c'); // opcode == content indication
+ // @@todo: kag: how do we skip 'i' entries???
+ buf.getLong(); // ignore sequence
+
+ std::string str1; // decode content body as string
+ buf.getRawData(str1, objLen);
+
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readProperties( str1 );
+ objs.push_back(fake);
+ }
+ }
+
+
+ // decode a V2 Content Indication message
+ //
+ void decodeV2ObjectUpdates(const qpid::messaging::Message& inMsg, TestObjectVector& objs)
+ {
+ objs.clear();
+
+ BOOST_CHECK_EQUAL(inMsg.getContentType(), std::string("amqp/list"));
+
+ const ::qpid::types::Variant::Map& m = inMsg.getProperties();
+ Variant::Map::const_iterator iter = m.find(std::string("qmf.opcode"));
+ BOOST_CHECK(iter != m.end());
+ BOOST_CHECK_EQUAL(iter->second.asString(), std::string("_data_indication"));
+
+ Variant::List vList;
+ ::qpid::amqp_0_10::ListCodec::decode(inMsg.getContent(), vList);
+
+ for (Variant::List::iterator lIter = vList.begin(); lIter != vList.end(); lIter++) {
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readTimestamps(lIter->asMap());
+ fake->mapDecodeValues((lIter->asMap())["_values"].asMap());
+ objs.push_back(fake);
+ }
+ }
+ }
+
+ QPID_AUTO_TEST_SUITE(BrokerMgmtAgent)
+
+ // verify that an object that is added to the broker's management database is
+ // published correctly. Furthermore, verify that it is published once after
+ // it has been deleted.
+ //
+ QPID_AUTO_TEST_CASE(v1ObjPublish)
+ {
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("obj1"));
+ uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
+
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ agent->addObject(tm->GetManagementObject(), 1);
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64()); // not deleted
+ }
+
+ // destroy the object
+
+ tm->GetManagementObject()->resourceDestroy();
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ r1.close();
+ delete fix;
+ delete tm;
+ }
+
+ // Repeat the previous test, but with V2-based object support
+ //
+ QPID_AUTO_TEST_CASE(v2ObjPublish)
+ {
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ TestManageable *tm = new TestManageable(agent, std::string("obj2"));
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm->GetManagementObject()->getPackageName(), "#");
+
+ agent->addObject(tm->GetManagementObject(), "testobj-1");
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64());
+ }
+
+ // destroy the object
+
+ tm->GetManagementObject()->resourceDestroy();
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ r1.close();
+ delete fix;
+ delete tm;
+ }
+
+
+ // verify that a deleted object is exported correctly using the
+ // exportDeletedObjects() method. V1 testcase.
+ //
+ QPID_AUTO_TEST_CASE(v1ExportDelObj)
+ {
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("myObj"));
+ uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
+
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ agent->addObject(tm->GetManagementObject(), 1);
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ // destroy the object, then immediately export (before the next poll cycle)
+
+ ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
+ tm->GetManagementObject()->resourceDestroy();
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 1);
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ // verify there are no deleted objects to export now.
+
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 0);
+
+ r1.close();
+ delete fix;
+ delete tm;
+ }
+
+
+ // verify that a deleted object is imported correctly using the
+ // importDeletedObjects() method. V1 testcase.
+ //
+ QPID_AUTO_TEST_CASE(v1ImportDelObj)
+ {
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("anObj"));
+ uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
+
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ agent->addObject(tm->GetManagementObject(), 1);
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ // destroy the object, then immediately export (before the next poll cycle)
+
+ ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
+ tm->GetManagementObject()->resourceDestroy();
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 1);
+
+ // destroy the broker, and reinistantiate a new one without populating it
+ // with a TestObject.
+
+ r1.close();
+ delete fix;
+ delete tm; // should no longer be necessary
+
+ fix = new AgentFixture(3);
+ r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+ agent = fix->getBrokerAgent();
+ agent->importDeletedObjects( delObjs );
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ // verify there are no deleted objects to export now.
+
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 0);
+
+ r1.close();
+ delete fix;
+ }
+
+
+ // verify that an object that is added and deleted prior to the
+ // first poll cycle is accounted for by the export
+ //
+ QPID_AUTO_TEST_CASE(v1ExportFastDelObj)
+ {
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("objectifyMe"));
+
+ // add, then immediately delete and export the object...
+
+ ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
+ agent->addObject(tm->GetManagementObject(), 999);
+ tm->GetManagementObject()->resourceDestroy();
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 1);
+
+ delete fix;
+ delete tm;
+ }
+
+
+ // Verify that we can export and import multiple deleted objects correctly.
+ //
+ QPID_AUTO_TEST_CASE(v1ImportMultiDelObj)
+ {
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ // populate the agent with multiple test objects
+ const size_t objCount = 50;
+ std::vector<TestManageable *> tmv;
+ uint32_t objLen;
+
+ for (size_t i = 0; i < objCount; i++) {
+ std::stringstream key;
+ key << "testobj-" << std::setfill('x') << std::setw(4) << i;
+ // (no, seriously, I didn't just do that.)
+ // Note well: we have to keep the key string length EXACTLY THE SAME
+ // FOR ALL OBJECTS, so objLen will be the same. Otherwise the
+ // decodeV1ObjectUpdates() will fail (v1 lacks explict encoded length).
+ TestManageable *tm = new TestManageable(agent, key.str());
+ objLen = tm->GetManagementObject()->writePropertiesSize();
+ agent->addObject(tm->GetManagementObject(), i + 1);
+ tmv.push_back(tm);
+ }
+
+ // wait for the objects to be published
+ Message m1;
+ uint32_t msgCount = 0;
+ while(r1.fetch(m1, Duration::SECOND * 6)) {
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ msgCount += objs.size();
+ }
+
+ BOOST_CHECK_EQUAL(msgCount, objCount);
+
+ // destroy some of the objects, then immediately export (before the next poll cycle)
+
+ uint32_t delCount = 0;
+ for (size_t i = 0; i < objCount; i += 2) {
+ tmv[i]->GetManagementObject()->resourceDestroy();
+ delCount++;
+ }
+
+ ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK_EQUAL(delObjs.size(), delCount);
+
+ // destroy the broker, and reinistantiate a new one without populating it
+ // with TestObjects.
+
+ r1.close();
+ delete fix;
+ while (tmv.size()) {
+ delete tmv.back();
+ tmv.pop_back();
+ }
+
+ fix = new AgentFixture(3);
+ r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+ agent = fix->getBrokerAgent();
+ agent->importDeletedObjects( delObjs );
+
+ // wait for the deleted object to be published, verify the count
+
+ uint32_t countDels = 0;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ countDels++;
+ }
+ }
+
+ // make sure we get the correct # of deleted objects
+ BOOST_CHECK_EQUAL(countDels, delCount);
+
+ // verify there are no deleted objects to export now.
+
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 0);
+
+ r1.close();
+ delete fix;
+ }
+
+ // Verify that we can export and import multiple deleted objects correctly.
+ // QMF V2 variant
+ QPID_AUTO_TEST_CASE(v2ImportMultiDelObj)
+ {
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ Receiver r1 = fix->createV2DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ // populate the agent with multiple test objects
+ const size_t objCount = 50;
+ std::vector<TestManageable *> tmv;
+ uint32_t objLen;
+
+ for (size_t i = 0; i < objCount; i++) {
+ std::stringstream key;
+ key << "testobj-" << i;
+ TestManageable *tm = new TestManageable(agent, key.str());
+ objLen = tm->GetManagementObject()->writePropertiesSize();
+ agent->addObject(tm->GetManagementObject(), key.str());
+ tmv.push_back(tm);
+ }
+
+ // wait for the objects to be published
+ Message m1;
+ uint32_t msgCount = 0;
+ while(r1.fetch(m1, Duration::SECOND * 6)) {
+ TestObjectVector objs;
+ decodeV2ObjectUpdates(m1, objs);
+ msgCount += objs.size();
+ }
+
+ BOOST_CHECK_EQUAL(msgCount, objCount);
+
+ // destroy some of the objects, then immediately export (before the next poll cycle)
+
+ uint32_t delCount = 0;
+ for (size_t i = 0; i < objCount; i += 2) {
+ tmv[i]->GetManagementObject()->resourceDestroy();
+ delCount++;
+ }
+
+ ::qpid::management::ManagementAgent::DeletedObjectList delObjs;
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK_EQUAL(delObjs.size(), delCount);
+
+ // destroy the broker, and reinistantiate a new one without populating it
+ // with TestObjects.
+
+ r1.close();
+ delete fix;
+ while (tmv.size()) {
+ delete tmv.back();
+ tmv.pop_back();
+ }
+
+ fix = new AgentFixture(3, true);
+ r1 = fix->createV2DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+ agent = fix->getBrokerAgent();
+ agent->importDeletedObjects( delObjs );
+
+ // wait for the deleted object to be published, verify the count
+
+ uint32_t countDels = 0;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+ TestObjectVector objs;
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ countDels++;
+ }
+ }
+
+ // make sure we get the correct # of deleted objects
+ BOOST_CHECK_EQUAL(countDels, delCount);
+
+ // verify there are no deleted objects to export now.
+
+ agent->exportDeletedObjects( delObjs );
+ BOOST_CHECK(delObjs.size() == 0);
+
+ r1.close();
+ delete fix;
+ }
+
+ // See QPID-2997
+ QPID_AUTO_TEST_CASE(v2RapidRestoreObj)
+ {
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // two objects, same ObjID
+ TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
+ TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
+
+ // add, then immediately delete and re-add a copy of the object
+ agent->addObject(tm1->GetManagementObject(), "testobj-1");
+ tm1->GetManagementObject()->resourceDestroy();
+ agent->addObject(tm2->GetManagementObject(), "testobj-1");
+
+ // expect: a delete notification, then an update notification
+ TestObjectVector objs;
+ bool isDeleted = false;
+ bool isAdvertised = false;
+ size_t count = 0;
+ Message m1;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ count++;
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0) {
+ isDeleted = true;
+ BOOST_CHECK(isAdvertised == false); // delete must be first
+ } else {
+ isAdvertised = true;
+ BOOST_CHECK(isDeleted == true); // delete must be first
+ }
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+ BOOST_CHECK(isAdvertised);
+ BOOST_CHECK(count == 2);
+
+ r1.close();
+ delete fix;
+ delete tm1;
+ delete tm2;
+ }
+
+ // See QPID-2997
+ QPID_AUTO_TEST_CASE(v2DuplicateErrorObj)
+ {
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // turn off the expected error log message
+ qpid::log::Options logOpts;
+ logOpts.selectors.clear();
+ logOpts.selectors.push_back("critical+");
+ qpid::log::Logger::instance().configure(logOpts);
+
+ // two objects, same ObjID
+ TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
+ TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
+ // Keep a pointer to the ManagementObject. This test simulates a user-caused error
+ // case (duplicate objects) where the broker has no choice but to leak a management
+ // object (safest assumption). To prevent valgrind from flagging this leak, we
+ // manually clean up the object at the end of the test.
+ management::ManagementObject *save = tm2->GetManagementObject();
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
+
+ // add, then immediately delete and re-add a copy of the object
+ agent->addObject(tm1->GetManagementObject(), "testobj-1");
+ agent->addObject(tm2->GetManagementObject(), "testobj-1");
+
+ TestObjectVector objs;
+ size_t count = 0;
+ Message m1;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ count++;
+ TestManageable::validateTestObjectProperties(**oIter);
+ }
+ }
+
+ BOOST_CHECK(count == 1); // only one should be accepted.
+
+ r1.close();
+ delete fix;
+ delete tm1;
+ delete tm2;
+ delete save;
+ }
+
+ QPID_AUTO_TEST_SUITE_END()
+ }
+}
+
+
diff --git a/qpid/cpp/src/tests/BrokerMgmtAgent.xml b/qpid/cpp/src/tests/BrokerMgmtAgent.xml
new file mode 100644
index 0000000000..202b8debf3
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerMgmtAgent.xml
@@ -0,0 +1,38 @@
+<schema package="org.apache.qpid.broker.mgmt.test">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+ <!--
+ ===============================================================
+ TestObject
+ ===============================================================
+ -->
+ <class name="TestObject">
+
+ A test object defined for the BrokerMgmtAgent unit test.
+
+ <property name="string1" type="lstr" access="RW" index="y"/>
+ <property name="bool1" type="bool" access="RW"/>
+ <property name="map1" type="map" access="RW"/>
+
+ </class>
+
+</schema>
+
diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt
new file mode 100644
index 0000000000..405718f12b
--- /dev/null
+++ b/qpid/cpp/src/tests/CMakeLists.txt
@@ -0,0 +1,356 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Enable dashboard reporting.
+include (CTest)
+
+# Make sure that everything get built before the tests
+# Need to create a var with all the necessary top level targets
+
+add_definitions(-DBOOST_TEST_DYN_LINK)
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+
+include (FindPythonInterp)
+
+# Create the environment scripts for tests
+set (abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+set (abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+set (abs_top_srcdir ${CMAKE_SOURCE_DIR})
+set (abs_top_builddir ${CMAKE_BINARY_DIR})
+set (builddir_lib_suffix "")
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.sh.in
+ ${CMAKE_CURRENT_BINARY_DIR}/test_env.sh)
+
+
+# If valgrind is selected in the configuration step, set up the path to it
+# for CTest.
+if (ENABLE_VALGRIND)
+ set (MEMORYCHECK_COMMAND ${VALGRIND})
+ set (MEMORYCHECK_COMMAND_OPTIONS "--gen-suppressions=all
+--leak-check=full
+--demangle=yes
+--suppressions=${CMAKE_CURRENT_SOURCE_DIR}/.valgrind.supp
+--num-callers=25
+--log-file=ctest_valgrind.vglog")
+endif (ENABLE_VALGRIND)
+
+# Using the Boost DLLs triggers warning 4275 on Visual Studio
+# (non dll-interface class used as base for dll-interface class).
+# This is ok, so suppress the warning.
+# Also, boost lengthy names trigger warning 4503, decorated name length exceeded
+# and using getenv() triggers insecure CRT warnings which we can silence in the
+# test environment.
+if (MSVC)
+ add_definitions( /wd4275 /wd4503 /D_CRT_SECURE_NO_WARNINGS)
+endif (MSVC)
+
+# Like this to work with cmake 2.4 on Unix
+set (qpid_test_boost_libs
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_SYSTEM_LIBRARY})
+
+# Macro to make it easier to remember where the tests are built
+macro(remember_location testname)
+ set (${testname}_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${testname}${CMAKE_EXECUTABLE_SUFFIX})
+endmacro(remember_location)
+
+# Windows uses some process-startup calls to ensure that errors, etc. don't
+# result in error boxes being thrown up. Since it's expected that most test
+# runs will be in scripts, the default is to force these outputs to stderr
+# instead of windows. If you want to remove this code, build without the
+# QPID_WINDOWS_DEFAULT_TEST_OUTPUTS ON.
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ option(QPID_WINDOWS_DEFAULT_TEST_OUTPUTS "Use default error-handling on Windows tests" OFF)
+ if (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+ set(platform_test_additions windows/DisableWin32ErrorWindows.cpp)
+ endif (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+#
+# Unit test program
+#
+# Unit tests are built as a single program to reduce valgrind overhead
+# when running the tests. If you want to build a subset of the tests run
+# ccmake and set unit_tests_to_build to the set you want to build.
+
+set(unit_tests_to_build
+ exception_test
+ RefCounted
+ SessionState
+ logging
+ AsyncCompletion
+ Url
+ Uuid
+ Shlib
+ FieldValue
+ FieldTable
+ Array
+ QueueOptionsTest
+ InlineAllocator
+ InlineVector
+ ClientSessionTest
+ MessagingSessionTests
+ SequenceSet
+ StringUtils
+ RangeSet
+ AtomicValue
+ QueueTest
+ AccumulatedAckTest
+ DtxWorkRecordTest
+ DeliveryRecordTest
+ ExchangeTest
+ HeadersExchangeTest
+ MessageTest
+ QueueRegistryTest
+ QueuePolicyTest
+ QueueFlowLimitTest
+ FramingTest
+ HeaderTest
+ SequenceNumberTest
+ TimerTest
+ TopicExchangeTest
+ TxBufferTest
+ TxPublishTest
+ MessageBuilderTest
+ ManagementTest
+ MessageReplayTracker
+ ConsoleTest
+ QueueEvents
+ ProxyTest
+ RetryList
+ RateFlowcontrolTest
+ FrameDecoder
+ ReplicationTest
+ ClientMessageTest
+ PollableCondition
+ Variant
+ ClientMessage
+ ${xml_tests}
+ CACHE STRING "Which unit tests to build"
+ )
+
+mark_as_advanced(unit_tests_to_build)
+
+# Disabled till we move to amqp_0_10 codec.
+# amqp_0_10/serialize.cpp allSegmentTypes.h \
+# amqp_0_10/ProxyTemplate.cpp \
+# amqp_0_10/apply.cpp \
+# amqp_0_10/Map.cpp \
+# amqp_0_10/handlers.cpp
+
+add_executable (unit_test unit_test
+ ${unit_tests_to_build} ${platform_test_additions})
+target_link_libraries (unit_test
+ ${qpid_test_boost_libs}
+ qpidmessaging qpidbroker qmfconsole)
+remember_location(unit_test)
+
+add_library (shlibtest MODULE shlibtest.cpp)
+
+if (BUILD_CLUSTER)
+ include (cluster.cmake)
+endif (BUILD_CLUSTER)
+
+# FIXME aconway 2009-11-30: enable SSL
+#if SSL
+#include ssl.mk
+#endif
+
+#
+# Other test programs
+#
+add_executable (qpid-perftest qpid-perftest.cpp ${platform_test_additions})
+target_link_libraries (qpid-perftest qpidclient)
+#qpid_perftest_SOURCES=qpid-perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h
+remember_location(qpid-perftest)
+
+add_executable (qpid-txtest qpid-txtest.cpp ${platform_test_additions})
+target_link_libraries (qpid-txtest qpidclient)
+#qpid_txtest_SOURCES=qpid-txtest.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-txtest)
+
+add_executable (qpid-latency-test qpid-latency-test.cpp ${platform_test_additions})
+target_link_libraries (qpid-latency-test qpidclient)
+#qpid_latencytest_SOURCES=qpid-latency-test.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-latency-test)
+
+add_executable (echotest echotest.cpp ${platform_test_additions})
+target_link_libraries (echotest qpidclient)
+#echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h
+remember_location(echotest)
+
+add_executable (qpid-client-test qpid-client-test.cpp ${platform_test_additions})
+target_link_libraries (qpid-client-test qpidclient)
+#qpid_client_test_SOURCES=qpid-client-test.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-client-test)
+
+add_executable (qpid-topic-listener qpid-topic-listener.cpp ${platform_test_additions})
+target_link_libraries (qpid-topic-listener qpidclient)
+#qpid_topic_listener_SOURCES=qpid-topic-listener.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-topic-listener)
+
+add_executable (qpid-topic-publisher qpid-topic-publisher.cpp ${platform_test_additions})
+target_link_libraries (qpid-topic-publisher qpidclient)
+#qpid_topic_publisher_SOURCES=qpid-topic-publisher.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-topic-publisher)
+
+add_executable (publish publish.cpp ${platform_test_additions})
+target_link_libraries (publish qpidclient)
+#publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h
+remember_location(publish)
+
+add_executable (consume consume.cpp ${platform_test_additions})
+target_link_libraries (consume qpidclient)
+#consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h
+remember_location(consume)
+
+add_executable (header_test header_test.cpp ${platform_test_additions})
+target_link_libraries (header_test qpidclient)
+#header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h
+remember_location(header_test)
+
+add_executable (declare_queues declare_queues.cpp ${platform_test_additions})
+target_link_libraries (declare_queues qpidclient)
+remember_location(declare_queues)
+
+add_executable (replaying_sender replaying_sender.cpp ${platform_test_additions})
+target_link_libraries (replaying_sender qpidclient)
+remember_location(replaying_sender)
+
+add_executable (resuming_receiver resuming_receiver.cpp ${platform_test_additions})
+target_link_libraries (resuming_receiver qpidclient)
+remember_location(resuming_receiver)
+
+add_executable (txshift txshift.cpp ${platform_test_additions})
+target_link_libraries (txshift qpidclient)
+#txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h
+remember_location(txshift)
+
+add_executable (txjob txjob.cpp ${platform_test_additions})
+target_link_libraries (txjob qpidclient)
+#txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h
+remember_location(txjob)
+
+add_executable (receiver receiver.cpp ${platform_test_additions})
+target_link_libraries (receiver qpidclient)
+#receiver_SOURCES=receiver.cpp TestOptions.h ConnectionOptions.h
+remember_location(receiver)
+
+add_executable (sender sender.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (sender qpidmessaging)
+#sender_SOURCES=sender.cpp TestOptions.h ConnectionOptions.h
+remember_location(sender)
+
+add_executable (qpid-receive qpid-receive.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (qpid-receive qpidmessaging)
+remember_location(qpid-receive)
+
+add_executable (qpid-send qpid-send.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (qpid-send qpidmessaging)
+remember_location(qpid-send)
+
+# qpid-perftest and qpid-latency-test are generally useful so install them
+install (TARGETS qpid-perftest qpid-latency-test RUNTIME
+ DESTINATION ${QPID_INSTALL_BINDIR})
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set (ENV{OUTDIR} ${EXECUTABLE_OUTPUT_PATH})
+ set (test_script_suffix ".ps1")
+ set (shell "powershell")
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix})
+
+add_test (unit_test ${test_wrap} ${unit_test_LOCATION})
+add_test (start_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/start_broker${test_script_suffix})
+add_test (qpid-client-test ${test_wrap} ${qpid-client_test_LOCATION})
+add_test (quick_perftest ${test_wrap} ${qpid-perftest_LOCATION} --summary --count 100)
+add_test (quick_topictest ${test_wrap} ${CMAKE_CURRENT_SOURCE_DIR}/quick_topictest${test_script_suffix})
+add_test (quick_txtest ${test_wrap} ${qpid-txtest_LOCATION} --queues 4 --tx-count 10 --quiet)
+if (PYTHON_EXECUTABLE)
+ add_test (run_header_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_header_test${test_script_suffix})
+ add_test (python_tests ${test_wrap} ${CMAKE_CURRENT_SOURCE_DIR}/python_tests${test_script_suffix})
+endif (PYTHON_EXECUTABLE)
+add_test (stop_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/stop_broker${test_script_suffix})
+if (PYTHON_EXECUTABLE)
+ add_test (federation_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_tests${test_script_suffix})
+if (BUILD_ACL)
+ add_test (acl_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_acl_tests${test_script_suffix})
+endif (BUILD_ACL)
+add_test (dynamic_log_level_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_log_level_test${test_script_suffix})
+if (BUILD_MSSQL)
+ add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL)
+endif (BUILD_MSSQL)
+if (BUILD_MSCLFS)
+ add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS)
+endif (BUILD_MSCLFS)
+endif (PYTHON_EXECUTABLE)
+
+add_library(test_store MODULE test_store.cpp)
+target_link_libraries (test_store qpidbroker qpidcommon)
+set_target_properties (test_store PROPERTIES PREFIX "")
+
+#EXTRA_DIST += \
+# run_test vg_check \
+# run-unit-tests start_broker python_tests stop_broker \
+# quick_topictest \
+# quick_perftest \
+# quick_txtest \
+# topictest \
+# run_header_test \
+# header_test.py \
+# ssl_test \
+# config.null \
+# ais_check \
+# run_federation_tests \
+# run_acl_tests \
+# .valgrind.supp \
+# MessageUtils.h \
+# TestMessageStore.h \
+# TxMocks.h \
+# start_cluster stop_cluster restart_cluster
+
+add_library (dlclose_noop MODULE dlclose_noop.c)
+#libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
+
+#CLEANFILES+=valgrind.out *.log *.vglog* dummy_test $(unit_wrappers)
+#
+## FIXME aconway 2008-05-23: Disabled interop_runner because it uses
+## the obsolete Channel class. Convert to Session and re-enable.
+##
+## check_PROGRAMS += interop_runner
+#
+## interop_runner_SOURCES = \
+## interop_runner.cpp \
+## SimpleTestCaseBase.cpp \
+## BasicP2PTest.cpp \
+## BasicPubSubTest.cpp \
+## SimpleTestCaseBase.h \
+## BasicP2PTest.h \
+## BasicPubSubTest.h \
+## TestCase.h \
+## TestOptions.h ConnectionOptions.h
+## interop_runner_LDADD = $(lib_client) $(lib_common) $(extra_libs)
+#
+#
+## Longer running stability tests, not run by default check: target.
+## Not run under valgrind, too slow
+#LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak
+#EXTRA_DIST+=$(LONG_TESTS) run_perftest
+#check-long:
+# $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND=
diff --git a/qpid/cpp/src/tests/ClientMessage.cpp b/qpid/cpp/src/tests/ClientMessage.cpp
new file mode 100644
index 0000000000..994c46552c
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientMessage.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/messaging/Message.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientMessageSuite)
+
+QPID_AUTO_TEST_CASE(testCopyConstructor)
+{
+ Message m("my-data");
+ m.setSubject("my-subject");
+ m.getProperties()["a"] = "ABC";
+ Message c(m);
+ BOOST_CHECK_EQUAL(m.getContent(), c.getContent());
+ BOOST_CHECK_EQUAL(m.getSubject(), c.getSubject());
+ BOOST_CHECK_EQUAL(m.getProperties()["a"], c.getProperties()["a"]);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClientMessageTest.cpp b/qpid/cpp/src/tests/ClientMessageTest.cpp
new file mode 100644
index 0000000000..f925f1c234
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientMessageTest.cpp
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/**@file Unit tests for the client::Message class. */
+
+#include "unit_test.h"
+#include "qpid/client/Message.h"
+
+using namespace qpid::client;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientMessageTestSuite)
+
+QPID_AUTO_TEST_CASE(MessageCopyAssign) {
+ // Verify that message has normal copy semantics.
+ Message m("foo");
+ BOOST_CHECK_EQUAL("foo", m.getData());
+ Message c(m);
+ BOOST_CHECK_EQUAL("foo", c.getData());
+ Message a;
+ BOOST_CHECK_EQUAL("", a.getData());
+ a = m;
+ BOOST_CHECK_EQUAL("foo", a.getData());
+ a.setData("a");
+ BOOST_CHECK_EQUAL("a", a.getData());
+ c.setData("c");
+ BOOST_CHECK_EQUAL("c", c.getData());
+ BOOST_CHECK_EQUAL("foo", m.getData());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClientSessionTest.cpp b/qpid/cpp/src/tests/ClientSessionTest.cpp
new file mode 100644
index 0000000000..3c0cff7350
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientSessionTest.cpp
@@ -0,0 +1,682 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Time.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/Message.h"
+#include "qpid/framing/reply_exceptions.h"
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientSessionTest)
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid;
+using qpid::sys::Monitor;
+using qpid::sys::Thread;
+using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
+using std::string;
+using std::cout;
+using std::endl;
+
+
+struct DummyListener : public sys::Runnable, public MessageListener {
+ std::vector<Message> messages;
+ string name;
+ uint expected;
+ SubscriptionManager submgr;
+
+ DummyListener(Session& session, const string& n, uint ex) :
+ name(n), expected(ex), submgr(session) {}
+
+ void run()
+ {
+ submgr.subscribe(*this, name);
+ submgr.run();
+ }
+
+ void received(Message& msg)
+ {
+ messages.push_back(msg);
+ if (--expected == 0) {
+ submgr.stop();
+ }
+ }
+};
+
+struct SimpleListener : public MessageListener
+{
+ Monitor lock;
+ std::vector<Message> messages;
+
+ void received(Message& msg)
+ {
+ Monitor::ScopedLock l(lock);
+ messages.push_back(msg);
+ lock.notifyAll();
+ }
+
+ void waitFor(const uint n)
+ {
+ Monitor::ScopedLock l(lock);
+ while (messages.size() < n) {
+ lock.wait();
+ }
+ }
+};
+
+struct ClientSessionFixture : public ProxySessionFixture
+{
+ ClientSessionFixture(Broker::Options opts = Broker::Options()) : ProxySessionFixture(opts) {
+ session.queueDeclare(arg::queue="my-queue");
+ }
+};
+
+QPID_AUTO_TEST_CASE(testQueueQuery) {
+ ClientSessionFixture fix;
+ fix.session = fix.connection.newSession();
+ fix.session.queueDeclare(arg::queue="q", arg::alternateExchange="amq.fanout",
+ arg::exclusive=true, arg::autoDelete=true);
+ QueueQueryResult result = fix.session.queueQuery("q");
+ BOOST_CHECK_EQUAL(false, result.getDurable());
+ BOOST_CHECK_EQUAL(true, result.getExclusive());
+ BOOST_CHECK_EQUAL("amq.fanout", result.getAlternateExchange());
+}
+
+QPID_AUTO_TEST_CASE(testDispatcher)
+{
+ ClientSessionFixture fix;
+ fix.session =fix.connection.newSession();
+ size_t count = 100;
+ for (size_t i = 0; i < count; ++i)
+ fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue"));
+ DummyListener listener(fix.session, "my-queue", count);
+ listener.run();
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
+}
+
+QPID_AUTO_TEST_CASE(testDispatcherThread)
+{
+ ClientSessionFixture fix;
+ fix.session =fix.connection.newSession();
+ size_t count = 10;
+ DummyListener listener(fix.session, "my-queue", count);
+ sys::Thread t(listener);
+ for (size_t i = 0; i < count; ++i) {
+ fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue"));
+ }
+ t.join();
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
+}
+
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testSuspend0Timeout() {
+ ClientSessionFixture fix;
+ fix.session.suspend(); // session has 0 timeout.
+ try {
+ fix.connection.resume(fix.session);
+ BOOST_FAIL("Expected InvalidArgumentException.");
+ } catch(const InternalErrorException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testUseSuspendedError)
+{
+ ClientSessionFixture fix;
+ fix.session.timeout(60);
+ fix.session.suspend();
+ try {
+ fix.session.exchangeQuery(arg::exchange="amq.fanout");
+ BOOST_FAIL("Expected session suspended exception");
+ } catch(const NotAttachedException&) {}
+}
+
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testSuspendResume() {
+ ClientSessionFixture fix;
+ fix.session.timeout(60);
+ fix.session.suspend();
+ // Make sure we are still subscribed after resume.
+ fix.connection.resume(fix.session);
+ fix.session.messageTransfer(arg::content=Message("my-message", "my-queue"));
+ BOOST_CHECK_EQUAL("my-message", fix.subs.get("my-queue", TIME_SEC).getData());
+}
+
+
+QPID_AUTO_TEST_CASE(testSendToSelf) {
+ ClientSessionFixture fix;
+ SimpleListener mylistener;
+ fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true);
+ fix.subs.subscribe(mylistener, "myq");
+ sys::Thread runner(fix.subs);//start dispatcher thread
+ string data("msg");
+ Message msg(data, "myq");
+ const uint count=10;
+ for (uint i = 0; i < count; ++i) {
+ fix.session.messageTransfer(arg::content=msg);
+ }
+ mylistener.waitFor(count);
+ fix.subs.cancel("myq");
+ fix.subs.stop();
+ runner.join();
+ fix.session.close();
+ BOOST_CHECK_EQUAL(mylistener.messages.size(), count);
+ for (uint j = 0; j < count; ++j) {
+ BOOST_CHECK_EQUAL(mylistener.messages[j].getData(), data);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testLocalQueue) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="lq", arg::exclusive=true, arg::autoDelete=true);
+ LocalQueue lq;
+ fix.subs.subscribe(lq, "lq", FlowControl(2, FlowControl::UNLIMITED, false));
+ fix.session.messageTransfer(arg::content=Message("foo0", "lq"));
+ fix.session.messageTransfer(arg::content=Message("foo1", "lq"));
+ fix.session.messageTransfer(arg::content=Message("foo2", "lq"));
+ BOOST_CHECK_EQUAL("foo0", lq.pop().getData());
+ BOOST_CHECK_EQUAL("foo1", lq.pop().getData());
+ BOOST_CHECK(lq.empty()); // Credit exhausted.
+ fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited());
+ BOOST_CHECK_EQUAL("foo2", lq.pop().getData());
+}
+
+struct DelayedTransfer : sys::Runnable
+{
+ ClientSessionFixture& fixture;
+
+ DelayedTransfer(ClientSessionFixture& f) : fixture(f) {}
+
+ void run()
+ {
+ qpid::sys::sleep(1);
+ fixture.session.messageTransfer(arg::content=Message("foo2", "getq"));
+ }
+};
+
+QPID_AUTO_TEST_CASE(testGet) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="getq", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.messageTransfer(arg::content=Message("foo0", "getq"));
+ fix.session.messageTransfer(arg::content=Message("foo1", "getq"));
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC));
+ BOOST_CHECK_EQUAL("foo0", got.getData());
+ BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC));
+ BOOST_CHECK_EQUAL("foo1", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "getq"));
+ DelayedTransfer sender(fix);
+ Thread t(sender);
+ //test timed get where message shows up after a short delay
+ BOOST_CHECK(fix.subs.get(got, "getq", 5*TIME_SEC));
+ BOOST_CHECK_EQUAL("foo2", got.getData());
+ t.join();
+}
+
+QPID_AUTO_TEST_CASE(testOpenFailure) {
+ BrokerFixture b;
+ Connection c;
+ string host("unknowable-host");
+ try {
+ c.open(host);
+ } catch (const Exception&) {
+ BOOST_CHECK(!c.isOpen());
+ }
+ b.open(c);
+ BOOST_CHECK(c.isOpen());
+ c.close();
+ BOOST_CHECK(!c.isOpen());
+}
+
+QPID_AUTO_TEST_CASE(testPeriodicExpiration) {
+ Broker::Options opts;
+ opts.queueCleanInterval = 1;
+ opts.queueFlowStopRatio = 0;
+ opts.queueFlowResumeRatio = 0;
+ ClientSessionFixture fix(opts);
+ FieldTable args;
+ args.setInt("qpid.max_count",10);
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(500);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 10u);
+ qpid::sys::sleep(2);
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 5u);
+ fix.session.messageTransfer(arg::content=Message("Message_11", "my-queue"));//ensure policy is also updated
+}
+
+QPID_AUTO_TEST_CASE(testExpirationOnPop) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(200);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ qpid::sys::usleep(300* 1000);
+
+ for (uint i = 0; i < 10; i++) {
+ if (i % 2) continue;
+ Message m;
+ BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRelease) {
+ ClientSessionFixture fix;
+
+ const uint count=10;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ fix.subs.setAutoStop(false);
+ fix.subs.start();
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+
+ SimpleListener l1;
+ Subscription s1 = fix.subs.subscribe(l1, "my-queue", settings);
+ l1.waitFor(count);
+ s1.cancel();
+
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l1.messages[i].getData());
+ }
+ s1.release(s1.getUnaccepted());
+
+ //check that released messages are redelivered
+ settings.autoAck = 1;
+ SimpleListener l2;
+ Subscription s2 = fix.subs.subscribe(l2, "my-queue", settings);
+ l2.waitFor(count);
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData());
+ }
+
+ fix.subs.stop();
+ fix.subs.wait();
+ fix.session.close();
+}
+
+QPID_AUTO_TEST_CASE(testCompleteOnAccept) {
+ ClientSessionFixture fix;
+ const uint count = 8;
+ const uint chunk = 4;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+ settings.completionMode = COMPLETE_ON_ACCEPT;
+ settings.flowControl = FlowControl::messageWindow(chunk);
+
+ LocalQueue q;
+ Subscription s = fix.subs.subscribe(q, "my-queue", settings);
+ fix.session.messageFlush(arg::destination=s.getName());
+ SequenceSet accepted;
+ for (uint i = 0; i < chunk; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ Message m;
+ BOOST_CHECK(!q.get(m));
+
+ s.accept(accepted);
+ fix.session.messageFlush(arg::destination=s.getName());
+ accepted.clear();
+
+ for (uint i = chunk; i < count; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ fix.session.messageAccept(accepted);
+}
+
+namespace
+{
+struct Publisher : qpid::sys::Runnable
+{
+ AsyncSession session;
+ Message message;
+ uint count;
+ Thread thread;
+
+ Publisher(Connection& con, Message m, uint c) : session(con.newSession()), message(m), count(c) {}
+
+ void start()
+ {
+ thread = Thread(*this);
+ }
+
+ void join()
+ {
+ thread.join();
+ }
+
+ void run()
+ {
+ for (uint i = 0; i < count; i++) {
+ session.messageTransfer(arg::content=message);
+ }
+ session.sync();
+ session.close();
+ }
+};
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentSenders)
+{
+ //Ensure concurrent publishing sessions on a connection don't
+ //cause assertions, deadlocks or other undesirables:
+ BrokerFixture fix;
+ Connection connection;
+ ConnectionSettings settings;
+ settings.maxFrameSize = 1024;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+ connection.open(settings);
+ AsyncSession session = connection.newSession();
+ Message message(string(512, 'X'));
+
+ boost::ptr_vector<Publisher> publishers;
+ for (size_t i = 0; i < 5; i++) {
+ publishers.push_back(new Publisher(connection, message, 100));
+ }
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::start, _1));
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::join, _1));
+ connection.close();
+}
+
+
+QPID_AUTO_TEST_CASE(testExclusiveSubscribe)
+{
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true);
+ SubscriptionSettings settings;
+ settings.exclusive = true;
+ LocalQueue q;
+ fix.subs.subscribe(q, "myq", settings, "first");
+ //attempt to create new subscriber should fail
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException);
+ ;
+
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveBinding) {
+ FieldTable options;
+ options.setString("qpid.exclusive-binding", "anything");
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="queue-1", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.queueDeclare(arg::queue="queue-2", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-1", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message1", "my-key"));
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-2", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message2", "my-key"));
+
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "queue-1"));
+ BOOST_CHECK_EQUAL("message1", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-1"));
+
+ BOOST_CHECK(fix.subs.get(got, "queue-2"));
+ BOOST_CHECK_EQUAL("message2", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-2"));
+}
+
+QPID_AUTO_TEST_CASE(testResubscribeWithLocalQueue) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="some-queue", arg::exclusive=true, arg::autoDelete=true);
+ LocalQueue p, q;
+ fix.subs.subscribe(p, "some-queue");
+ fix.subs.cancel("some-queue");
+ fix.subs.subscribe(q, "some-queue");
+
+ fix.session.messageTransfer(arg::content=Message("some-data", "some-queue"));
+ fix.session.messageFlush(arg::destination="some-queue");
+
+ Message got;
+ BOOST_CHECK(!p.get(got));
+
+ BOOST_CHECK(q.get(got));
+ BOOST_CHECK_EQUAL("some-data", got.getData());
+ BOOST_CHECK(!q.get(got));
+}
+
+QPID_AUTO_TEST_CASE(testReliableDispatch) {
+ ClientSessionFixture fix;
+ std::string queue("a-queue");
+ fix.session.queueDeclare(arg::queue=queue, arg::autoDelete=true);
+
+ ConnectionSettings settings;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+
+ Connection c1;
+ c1.open(settings);
+ Session s1 = c1.newSession();
+ SubscriptionManager subs1(s1);
+ LocalQueue q1;
+ subs1.subscribe(q1, queue, FlowControl());//first subscriber has no credit
+
+ Connection c2;
+ c2.open(settings);
+ Session s2 = c2.newSession();
+ SubscriptionManager subs2(s2);
+ LocalQueue q2;
+ subs2.subscribe(q2, queue);//second subscriber has credit
+
+ fix.session.messageTransfer(arg::content=Message("my-message", queue));
+
+ //check that the second consumer gets the message
+ Message got;
+ BOOST_CHECK(q2.get(got, 1*TIME_SEC));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+
+ c1.close();
+ c2.close();
+}
+
+QPID_AUTO_TEST_CASE(testSessionCloseOnInvalidSession) {
+ Session session;
+ session.close();
+}
+
+QPID_AUTO_TEST_CASE(testLVQVariedSize) {
+ ClientSessionFixture fix;
+ std::string queue("my-lvq");
+ QueueOptions args;
+ args.setOrdering(LVQ_NO_BROWSE);
+ fix.session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ std::string key;
+ args.getLVQKey(key);
+
+ for (size_t i = 0; i < 10; i++) {
+ std::ostringstream data;
+ size_t size = 100 - ((i % 10) * 10);
+ data << std::string(size, 'x');
+
+ Message m(data.str(), queue);
+ m.getHeaders().setString(key, "abc");
+ fix.session.messageTransfer(arg::content=m);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionManagerSetFlowControl) {
+ ClientSessionFixture fix;
+ std::string name("dummy");
+ LocalQueue queue;
+ SubscriptionSettings settings;
+ settings.flowControl = FlowControl();
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.subs.subscribe(queue, name, settings);
+ fix.session.messageTransfer(arg::content=Message("my-message", name));
+ fix.subs.setFlowControl(name, 1, FlowControl::UNLIMITED, false);
+ fix.session.messageFlush(name);
+ Message got;
+ BOOST_CHECK(queue.get(got, 0));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+}
+
+QPID_AUTO_TEST_CASE(testGetThenSubscribe) {
+ ClientSessionFixture fix;
+ std::string name("myqueue");
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.session.messageTransfer(arg::content=Message("one", name));
+ fix.session.messageTransfer(arg::content=Message("two", name));
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, name));
+ BOOST_CHECK_EQUAL("one", got.getData());
+
+ DummyListener listener(fix.session, name, 1);
+ listener.run();
+ BOOST_CHECK_EQUAL(1u, listener.messages.size());
+ if (!listener.messages.empty()) {
+ BOOST_CHECK_EQUAL("two", listener.messages[0].getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionIsValid) {
+ ClientSessionFixture fix;
+ BOOST_CHECK(fix.session.isValid());
+ Session session;
+ BOOST_CHECK(!session.isValid());
+}
+
+QPID_AUTO_TEST_CASE(testExpirationNotAltered) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ Message m("my-message", "my-queue");
+ m.getDeliveryProperties().setTtl(60000);
+ m.getDeliveryProperties().setExpiration(12345);
+ fix.session.messageTransfer(arg::content=m);
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "my-queue"));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+ BOOST_CHECK_EQUAL(12345u, got.getDeliveryProperties().getExpiration());
+}
+
+QPID_AUTO_TEST_CASE(testGetConnectionFromSession) {
+ ClientSessionFixture fix;
+ FieldTable options;
+ options.setInt("no-local", 1);
+ fix.session.queueDeclare(arg::queue="a", arg::exclusive=true, arg::autoDelete=true, arg::arguments=options);
+ fix.session.queueDeclare(arg::queue="b", arg::exclusive=true, arg::autoDelete=true);
+
+ Connection c = fix.session.getConnection();
+ Session s = c.newSession();
+ //If this new session was created as expected on the same connection as
+ //fix.session, then the no-local behaviour means that queue 'a'
+ //will not enqueue messages from this new session but queue 'b'
+ //will.
+ s.messageTransfer(arg::content=Message("a", "a"));
+ s.messageTransfer(arg::content=Message("b", "b"));
+
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "b"));
+ BOOST_CHECK_EQUAL("b", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "a"));
+}
+
+
+QPID_AUTO_TEST_CASE(testQueueDeleted)
+{
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue");
+ LocalQueue queue;
+ fix.subs.subscribe(queue, "my-queue");
+
+ ScopedSuppressLogging sl;
+ fix.session.queueDelete(arg::queue="my-queue");
+ BOOST_CHECK_THROW(queue.get(1*qpid::sys::TIME_SEC), qpid::framing::ResourceDeletedException);
+}
+
+QPID_AUTO_TEST_CASE(testTtl)
+{
+ const uint64_t ms = 1000ULL; // convert sec to ms
+ const uint64_t us = 1000ULL * 1000ULL; // convert sec to us
+
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="ttl-test", arg::exclusive=true, arg::autoDelete=true);
+ Message msg1 = Message("AAA", "ttl-test");
+ uint64_t ttl = 2 * ms; // 2 sec
+ msg1.getDeliveryProperties().setTtl(ttl);
+ Connection c = fix.session.getConnection();
+ Session s = c.newSession();
+ s.messageTransfer(arg::content=msg1);
+
+ Message msg2 = Message("BBB", "ttl-test");
+ ttl = 10 * ms; // 10 sec
+ msg2.getDeliveryProperties().setTtl(ttl);
+ s.messageTransfer(arg::content=msg2);
+
+ qpid::sys::usleep(5 * us); // 5 sec
+
+ // Message "AAA" should be expired and never be delivered
+ // Check "BBB" has ttl somewhere between 1 and 5 secs
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "ttl-test"));
+ BOOST_CHECK_EQUAL("BBB", got.getData());
+ BOOST_CHECK(got.getDeliveryProperties().getTtl() > 1 * ms);
+ BOOST_CHECK(got.getDeliveryProperties().getTtl() < ttl - (5 * ms));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClusterFailover.cpp b/qpid/cpp/src/tests/ClusterFailover.cpp
new file mode 100644
index 0000000000..bf5c147f19
--- /dev/null
+++ b/qpid/cpp/src/tests/ClusterFailover.cpp
@@ -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.
+ *
+ */
+
+/**@file Tests for partial failure in a cluster.
+ * Partial failure means some nodes experience a failure while others do not.
+ * In this case the failed nodes must shut down.
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ClusterFixture.h"
+#include "qpid/client/FailoverManager.h"
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClusterFailoverTestSuite)
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid::client::arg;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
+
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=sys::TIME_SEC/4;
+
+ClusterFixture::Args getArgs(bool durable=std::getenv("STORE_LIB"))
+{
+ ClusterFixture::Args args;
+ args += "--auth", "no", "--no-module-dir",
+ "--load-module", getLibPath("CLUSTER_LIB");
+ if (durable)
+ args += "--load-module", getLibPath("STORE_LIB"), "TMP_DATA_DIR";
+ else
+ args += "--no-data-dir";
+ return args;
+}
+
+// Test re-connecting with same session name after a failure.
+QPID_AUTO_TEST_CASE(testReconnectSameSessionName) {
+ ClusterFixture cluster(2, getArgs(), -1);
+ // Specify a timeout to make sure it is ignored, session resume is
+ // not implemented so sessions belonging to dead brokers should
+ // not be kept.
+ Client c0(cluster[0], "foo", 5);
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size()); // wait for both.
+ c0.session.queueDeclare("q");
+ c0.session.messageTransfer(arg::content=Message("sendme", "q"));
+ BOOST_CHECK_EQUAL(c0.subs.get("q").getData(), "sendme");
+ cluster.killWithSilencer(0, c0.connection, 9);
+ Client c1(cluster[1], "foo", 5);
+ c1.session.queueQuery(); // Try to use the session.
+}
+
+QPID_AUTO_TEST_CASE(testReconnectExclusiveQueue) {
+ // Regresion test. Session timeouts should be ignored
+ // by the broker as session resume is not implemented.
+ ClusterFixture cluster(2, getArgs(), -1);
+ Client c0(cluster[0], "foo", 5);
+ c0.session.queueDeclare("exq", arg::exclusive=true);
+ SubscriptionSettings settings;
+ settings.exclusive = true;
+ settings.autoAck = 0;
+ Subscription s0 = c0.subs.subscribe(c0.lq, "exq", settings, "exsub");
+ c0.session.messageTransfer(arg::content=Message("sendme", "exq"));
+ BOOST_CHECK_EQUAL(c0.lq.get().getData(), "sendme");
+
+ // Regression: core dump on exit if unacked messages were left in
+ // a session with a timeout.
+ cluster.killWithSilencer(0, c0.connection);
+
+ // Regression: session timeouts prevented re-connecting to
+ // exclusive queue.
+ Client c1(cluster[1]);
+ c1.session.queueDeclare("exq", arg::exclusive=true);
+ Subscription s1 = c1.subs.subscribe(c1.lq, "exq", settings, "exsub");
+ s1.cancel();
+
+ // Regression: session timeouts prevented new member joining
+ // cluster with exclusive queues.
+ cluster.add();
+ Client c2(cluster[2]);
+ c2.session.queueQuery();
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClusterFixture.cpp b/qpid/cpp/src/tests/ClusterFixture.cpp
new file mode 100644
index 0000000000..6b62cb6fc7
--- /dev/null
+++ b/qpid/cpp/src/tests/ClusterFixture.cpp
@@ -0,0 +1,160 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ForkedBroker.h"
+#include "BrokerFixture.h"
+
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Logger.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/assign.hpp>
+
+#include <string>
+#include <iostream>
+#include <iterator>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
+using boost::shared_ptr;
+using qpid::cluster::Cluster;
+using boost::assign::list_of;
+
+
+#include "ClusterFixture.h"
+
+namespace qpid {
+namespace tests {
+
+ClusterFixture::ClusterFixture(size_t n, const Args& args_, int localIndex_)
+ : name(Uuid(true).str()), localIndex(localIndex_), userArgs(args_)
+{
+ add(n);
+}
+
+ClusterFixture::ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs_, int localIndex_)
+ : name(Uuid(true).str()), localIndex(localIndex_), updateArgs(updateArgs_)
+{
+ add(n);
+}
+
+ClusterFixture::Args ClusterFixture::makeArgs(const std::string& prefix, size_t index) {
+ Args args = list_of<string>("qpidd ")
+ ("--cluster-name")(name)
+ ("--log-prefix")(prefix);
+ args.insert(args.end(), userArgs.begin(), userArgs.end());
+ if (updateArgs) updateArgs(args, index);
+ return args;
+}
+
+void ClusterFixture::add() {
+ if (size() != size_t(localIndex)) { // fork a broker process.
+ std::ostringstream os; os << "fork" << size();
+ std::string prefix = os.str();
+ forkedBrokers.push_back(shared_ptr<ForkedBroker>(new ForkedBroker(makeArgs(prefix, size()))));
+ push_back(forkedBrokers.back()->getPort());
+ }
+ else { // Run in this process
+ addLocal();
+ }
+}
+
+namespace {
+/** Parse broker & cluster options */
+Broker::Options parseOpts(size_t argc, const char* argv[]) {
+ Broker::Options opts;
+ Plugin::addOptions(opts); // Pick up cluster options.
+ opts.parse(argc, argv, "", true); // Allow-unknown for --load-module
+ return opts;
+}
+}
+
+void ClusterFixture::addLocal() {
+ assert(int(size()) == localIndex);
+ ostringstream os; os << "local" << localIndex;
+ string prefix = os.str();
+ Args args(makeArgs(prefix, localIndex));
+ vector<const char*> argv(args.size());
+ transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
+ qpid::log::Logger::instance().setPrefix(prefix);
+ localBroker.reset(new BrokerFixture(parseOpts(argv.size(), &argv[0])));
+ push_back(localBroker->getPort());
+ forkedBrokers.push_back(shared_ptr<ForkedBroker>());
+}
+
+bool ClusterFixture::hasLocal() const { return localIndex >= 0 && size_t(localIndex) < size(); }
+
+/** Kill a forked broker with sig, or shutdown localBroker if n==0. */
+void ClusterFixture::kill(size_t n, int sig) {
+ if (n == size_t(localIndex))
+ localBroker->broker->shutdown();
+ else
+ forkedBrokers[n]->kill(sig);
+}
+
+/** Kill a broker and suppress errors from closing connection c. */
+void ClusterFixture::killWithSilencer(size_t n, client::Connection& c, int sig) {
+ ScopedSuppressLogging sl;
+ try { c.close(); } catch(...) {}
+ kill(n,sig);
+}
+
+/**
+ * Get the known broker ports from a Connection.
+ *@param n if specified wait for the cluster size to be n, up to a timeout.
+ */
+std::set<int> knownBrokerPorts(qpid::client::Connection& c, int n) {
+ FailoverListener fl(c, false);
+ std::vector<qpid::Url> urls = fl.getKnownBrokers();
+ if (n >= 0 && unsigned(n) != urls.size()) {
+ // Retry up to 10 secs in .1 second intervals.
+ for (size_t retry=100; urls.size() != unsigned(n) && retry != 0; --retry) {
+ qpid::sys::usleep(1000*100); // 0.1 secs
+ urls = fl.getKnownBrokers();
+ }
+ }
+ std::set<int> s;
+ for (std::vector<qpid::Url>::const_iterator i = urls.begin(); i != urls.end(); ++i)
+ s.insert((*i)[0].port);
+ return s;
+}
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClusterFixture.h b/qpid/cpp/src/tests/ClusterFixture.h
new file mode 100644
index 0000000000..f548ff9376
--- /dev/null
+++ b/qpid/cpp/src/tests/ClusterFixture.h
@@ -0,0 +1,115 @@
+#ifndef CLUSTER_FIXTURE_H
+#define CLUSTER_FIXTURE_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ForkedBroker.h"
+#include "BrokerFixture.h"
+
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Logger.h"
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <iostream>
+#include <iterator>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
+using boost::shared_ptr;
+using qpid::cluster::Cluster;
+
+namespace qpid {
+namespace tests {
+
+/** Cluster fixture is a vector of ports for the replicas.
+ *
+ * At most one replica (by default replica 0) is in the current
+ * process, all others are forked as children.
+ */
+class ClusterFixture : public vector<uint16_t> {
+ public:
+ typedef std::vector<std::string> Args;
+
+ /** @param localIndex can be -1 meaning don't automatically start a local broker.
+ * A local broker can be started with addLocal().
+ */
+ ClusterFixture(size_t n, const Args& args, int localIndex=-1);
+
+ /**@param updateArgs function is passed the index of the cluster member and can update the arguments. */
+ ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs, int localIndex=-1);
+
+ void add(size_t n) { for (size_t i=0; i < n; ++i) add(); }
+ void add(); // Add a broker.
+ void setup();
+
+ bool hasLocal() const;
+
+ /** Kill a forked broker with sig, or shutdown localBroker. */
+ void kill(size_t n, int sig=SIGINT);
+
+ /** Kill a broker and suppress errors from closing connection c. */
+ void killWithSilencer(size_t n, client::Connection& c, int sig=SIGINT);
+
+ private:
+
+ void addLocal(); // Add a local broker.
+ Args makeArgs(const std::string& prefix, size_t index);
+ string name;
+ std::auto_ptr<BrokerFixture> localBroker;
+ int localIndex;
+ std::vector<shared_ptr<ForkedBroker> > forkedBrokers;
+ Args userArgs;
+ boost::function<void (Args&, size_t)> updateArgs;
+};
+
+/**
+ * Get the known broker ports from a Connection.
+ *@param n if specified wait for the cluster size to be n, up to a timeout.
+ */
+std::set<int> knownBrokerPorts(qpid::client::Connection& source, int n=-1);
+
+}} // namespace qpid::tests
+
+#endif /*!CLUSTER_FIXTURE_H*/
diff --git a/qpid/cpp/src/tests/ConnectionOptions.h b/qpid/cpp/src/tests/ConnectionOptions.h
new file mode 100644
index 0000000000..fe945e9ddd
--- /dev/null
+++ b/qpid/cpp/src/tests/ConnectionOptions.h
@@ -0,0 +1,62 @@
+#ifndef QPID_CLIENT_CONNECTIONOPTIONS_H
+#define QPID_CLIENT_CONNECTIONOPTIONS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/Options.h"
+
+namespace qpid {
+
+/**
+ * Options parser for ConnectionOptions.
+ */
+struct ConnectionOptions : public qpid::Options,
+ public qpid::client::ConnectionSettings
+{
+ ConnectionOptions() : qpid::Options("Connection Settings")
+ {
+ using namespace qpid;
+ addOptions()
+ ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
+ ("port,p", optValue(port, "PORT"), "Broker port to connect to")
+ ("protocol,P", optValue(protocol, "tcp|ssl|rdma"), "Protocol to use for broker connection")
+ ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host")
+ ("username", optValue(username, "USER"), "user name for broker log in.")
+ ("password", optValue(password, "PASSWORD"), "password for broker log in.")
+ ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
+ ("locale", optValue(locale, "LOCALE"), "locale to use.")
+ ("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.")
+ ("heartbeat", optValue(heartbeat, "N"), "Desired heartbeat interval in seconds.")
+ ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.")
+ ("bounds-multiplier", optValue(bounds, "N"),
+ "bound size of write queue (as a multiple of the max frame size).")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("service", optValue(service, "SERVICE-NAME"), "SASL service name.")
+ ("min-ssf", optValue(minSsf, "N"), "Minimum acceptable strength for SASL security layer")
+ ("max-ssf", optValue(maxSsf, "N"), "Maximum acceptable strength for SASL security layer");
+ }
+};
+
+} // namespace qpid
+
+#endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/
diff --git a/qpid/cpp/src/tests/ConsoleTest.cpp b/qpid/cpp/src/tests/ConsoleTest.cpp
new file mode 100644
index 0000000000..107472ed9e
--- /dev/null
+++ b/qpid/cpp/src/tests/ConsoleTest.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/console/Package.h"
+#include "qpid/console/ClassKey.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ConsoleTestSuite)
+
+using namespace qpid::framing;
+using namespace qpid::console;
+
+QPID_AUTO_TEST_CASE(testClassKey) {
+ uint8_t hash[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ ClassKey k("com.redhat.test", "class", hash);
+
+ BOOST_CHECK_EQUAL(k.getPackageName(), "com.redhat.test");
+ BOOST_CHECK_EQUAL(k.getClassName(), "class");
+ BOOST_CHECK_EQUAL(k.getHashString(), "00010203-04050607-08090a0b-0c0d0e0f");
+ BOOST_CHECK_EQUAL(k.str(), "com.redhat.test:class(00010203-04050607-08090a0b-0c0d0e0f)");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/qpid/cpp/src/tests/DeliveryRecordTest.cpp
new file mode 100644
index 0000000000..f7013014ff
--- /dev/null
+++ b/qpid/cpp/src/tests/DeliveryRecordTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/Queue.h"
+#include "unit_test.h"
+#include <iostream>
+#include <memory>
+#include <boost/format.hpp>
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+using namespace qpid::framing;
+using boost::dynamic_pointer_cast;
+using std::list;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite)
+
+QPID_AUTO_TEST_CASE(testSort)
+{
+ list<SequenceNumber> ids;
+ ids.push_back(SequenceNumber(6));
+ ids.push_back(SequenceNumber(2));
+ ids.push_back(SequenceNumber(4));
+ ids.push_back(SequenceNumber(5));
+ ids.push_back(SequenceNumber(1));
+ ids.push_back(SequenceNumber(3));
+
+ list<DeliveryRecord> records;
+ for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) {
+ DeliveryRecord r(QueuedMessage(0), Queue::shared_ptr(), "tag", false, false, false);
+ r.setId(*i);
+ records.push_back(r);
+ }
+ records.sort();
+
+ SequenceNumber expected(0);
+ for (list<DeliveryRecord>::iterator i = records.begin(); i != records.end(); i++) {
+ BOOST_CHECK(i->getId() == ++expected);
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/DispatcherTest.cpp b/qpid/cpp/src/tests/DispatcherTest.cpp
new file mode 100644
index 0000000000..e1691db584
--- /dev/null
+++ b/qpid/cpp/src/tests/DispatcherTest.cpp
@@ -0,0 +1,241 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/sys/Thread.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <iostream>
+#include <boost/bind.hpp>
+
+using namespace std;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+int writeALot(int fd, const string& s) {
+ int bytesWritten = 0;
+ do {
+ errno = 0;
+ int lastWrite = ::write(fd, s.c_str(), s.size());
+ if ( lastWrite >= 0) {
+ bytesWritten += lastWrite;
+ }
+ } while (errno != EAGAIN);
+ return bytesWritten;
+}
+
+int readALot(int fd) {
+ int bytesRead = 0;
+ char buf[10240];
+
+ do {
+ errno = 0;
+ int lastRead = ::read(fd, buf, sizeof(buf));
+ if ( lastRead >= 0) {
+ bytesRead += lastRead;
+ }
+ } while (errno != EAGAIN);
+ return bytesRead;
+}
+
+int64_t writtenBytes = 0;
+int64_t readBytes = 0;
+
+void writer(DispatchHandle& h, int fd, const string& s) {
+ writtenBytes += writeALot(fd, s);
+ h.rewatch();
+}
+
+void reader(DispatchHandle& h, int fd) {
+ readBytes += readALot(fd);
+ h.rewatch();
+}
+
+void rInterrupt(DispatchHandle&) {
+ cerr << "R";
+}
+
+void wInterrupt(DispatchHandle&) {
+ cerr << "W";
+}
+
+DispatchHandle::Callback rcb = rInterrupt;
+DispatchHandle::Callback wcb = wInterrupt;
+
+DispatchHandleRef *volatile rh = 0;
+DispatchHandleRef *volatile wh = 0;
+
+volatile bool stopWait = false;
+volatile bool phase1finished = false;
+
+timer_t timer;
+
+void stop_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ stopWait = true;
+}
+
+void timer_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ static int count = 0;
+ if (count++ < 10) {
+ rh->call(rcb);
+ wh->call(wcb);
+ } else {
+ phase1finished = true;
+ assert(::timer_delete(timer) == 0);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ // Create poller
+ Poller::shared_ptr poller(new Poller);
+
+ // Create dispatcher thread
+ Thread dt(*poller);
+ Thread dt1(*poller);
+ Thread dt2(*poller);
+ Thread dt3(*poller);
+
+ // Setup sender and receiver
+ int sv[2];
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ assert(rc >= 0);
+
+ // Set non-blocking
+ rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ // Make up a large string
+ string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
+ for (int i = 0; i < 8; i++)
+ testString += testString;
+
+ PosixIOHandle f0(sv[0]);
+ PosixIOHandle f1(sv[1]);
+
+ rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+
+ rh->startWatch(poller);
+ wh->startWatch(poller);
+
+ // Set up a regular itimer interupt
+ // We assume that this thread will handle the signals whilst sleeping
+ // as the Poller threads have signal handling blocked
+
+ // Signal handling
+ struct ::sigaction sa;
+ sa.sa_sigaction = timer_handler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ ::sigemptyset(&sa.sa_mask);
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ ::sigevent se;
+ ::memset(&se, 0, sizeof(se)); // Clear to make valgrind happy (this *is* the neatest way to do this portably - sigh)
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = SIGRTMIN;
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+
+ itimerspec ts = {
+ /*.it_value = */ {2, 0}, // s, ns
+ /*.it_interval = */ {2, 0}}; // s, ns
+
+ rc = ::timer_settime(timer, 0, &ts, 0);
+ assert(rc == 0);
+
+ // wait
+ while (!phase1finished) {
+ ::sleep(1);
+ }
+
+ // Now test deleting/creating DispatchHandles in tight loop, so that we are likely to still be using the
+ // attached PollerHandles after deleting the DispatchHandle
+ DispatchHandleRef* t = wh;
+ wh = 0;
+ delete t;
+ t = rh;
+ rh = 0;
+ delete t;
+
+ sa.sa_sigaction = stop_handler;
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ itimerspec nts = {
+ /*.it_value = */ {30, 0}, // s, ns
+ /*.it_interval = */ {30, 0}}; // s, ns
+
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+ rc = ::timer_settime(timer, 0, &nts, 0);
+ assert(rc == 0);
+
+ DispatchHandleRef* rh1;
+ DispatchHandleRef* wh1;
+
+ struct timespec w = {0, 1000000};
+ while (!stopWait) {
+ rh1 = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh1 = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+ rh1->startWatch(poller);
+ wh1->startWatch(poller);
+
+ ::nanosleep(&w, 0);
+
+ delete wh1;
+ delete rh1;
+ }
+
+ rc = ::timer_delete(timer);
+ assert(rc == 0);
+
+ poller->shutdown();
+ dt.join();
+ dt1.join();
+ dt2.join();
+ dt3.join();
+
+ cout << "\nWrote: " << writtenBytes << "\n";
+ cout << "Read: " << readBytes << "\n";
+
+ return 0;
+}
diff --git a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp
new file mode 100644
index 0000000000..9d7666dca4
--- /dev/null
+++ b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/DtxWorkRecord.h"
+#include "unit_test.h"
+#include <iostream>
+#include <vector>
+#include "TxMocks.h"
+
+using namespace qpid::broker;
+using boost::static_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite)
+
+QPID_AUTO_TEST_CASE(testOnePhaseCommit){
+ MockTransactionalStore store;
+ store.expectBegin().expectCommit();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectCommit();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectPrepare().expectCommit();
+
+ DtxBuffer::shared_ptr bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ DtxBuffer::shared_ptr bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+
+ DtxWorkRecord work("my-xid", &store);
+ work.add(bufferA);
+ work.add(bufferB);
+
+ work.commit(true);
+
+ store.check();
+ BOOST_CHECK(store.isCommitted());
+ opA->check();
+ opB->check();
+}
+
+QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){
+ MockTransactionalStore store;
+ store.expectBegin().expectAbort();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp(true));
+ opB->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opC(new MockTxOp());
+ opC->expectRollback();
+
+ DtxBuffer::shared_ptr bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ DtxBuffer::shared_ptr bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+ DtxBuffer::shared_ptr bufferC(new DtxBuffer());
+ bufferC->enlist(static_pointer_cast<TxOp>(opC));
+ bufferC->markEnded();
+
+ DtxWorkRecord work("my-xid", &store);
+ work.add(bufferA);
+ work.add(bufferB);
+ work.add(bufferC);
+
+ work.commit(true);
+
+ BOOST_CHECK(store.isAborted());
+ store.check();
+
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testTwoPhaseCommit){
+ MockTransactionalStore store;
+ store.expectBegin2PC().expectPrepare().expectCommit();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectCommit();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectPrepare().expectCommit();
+
+ DtxBuffer::shared_ptr bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ DtxBuffer::shared_ptr bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+
+ DtxWorkRecord work("my-xid", &store);
+ work.add(bufferA);
+ work.add(bufferB);
+
+ BOOST_CHECK(work.prepare());
+ BOOST_CHECK(store.isPrepared());
+ work.commit(false);
+ store.check();
+ BOOST_CHECK(store.isCommitted());
+ opA->check();
+ opB->check();
+}
+
+QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){
+ MockTransactionalStore store;
+ store.expectBegin2PC().expectAbort();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp(true));
+ opB->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opC(new MockTxOp());
+ opC->expectRollback();
+
+ DtxBuffer::shared_ptr bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ DtxBuffer::shared_ptr bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+ DtxBuffer::shared_ptr bufferC(new DtxBuffer());
+ bufferC->enlist(static_pointer_cast<TxOp>(opC));
+ bufferC->markEnded();
+
+ DtxWorkRecord work("my-xid", &store);
+ work.add(bufferA);
+ work.add(bufferB);
+ work.add(bufferC);
+
+ BOOST_CHECK(!work.prepare());
+ BOOST_CHECK(store.isAborted());
+ store.check();
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testRollback){
+ MockTransactionalStore store;
+ store.expectBegin2PC().expectPrepare().expectAbort();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectPrepare().expectRollback();
+
+ DtxBuffer::shared_ptr bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ DtxBuffer::shared_ptr bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+
+ DtxWorkRecord work("my-xid", &store);
+ work.add(bufferA);
+ work.add(bufferB);
+
+ BOOST_CHECK(work.prepare());
+ BOOST_CHECK(store.isPrepared());
+ work.rollback();
+ store.check();
+ BOOST_CHECK(store.isAborted());
+ opA->check();
+ opB->check();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp
new file mode 100644
index 0000000000..88a1cd99c2
--- /dev/null
+++ b/qpid/cpp/src/tests/ExchangeTest.cpp
@@ -0,0 +1,289 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/DirectExchange.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/HeadersExchange.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "unit_test.h"
+#include <iostream>
+#include "MessageUtils.h"
+
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace qpid;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ExchangeTestSuite)
+
+QPID_AUTO_TEST_CASE(testMe)
+{
+ Queue::shared_ptr queue(new Queue("queue", true));
+ Queue::shared_ptr queue2(new Queue("queue2", true));
+
+ TopicExchange topic("topic");
+ topic.bind(queue, "abc", 0);
+ topic.bind(queue2, "abc", 0);
+
+ DirectExchange direct("direct");
+ direct.bind(queue, "abc", 0);
+ direct.bind(queue2, "abc", 0);
+
+ queue.reset();
+ queue2.reset();
+
+ intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", false, "id"));
+ DeliverableMessage msg(msgPtr);
+ topic.route(msg, "abc", 0);
+ direct.route(msg, "abc", 0);
+
+}
+
+QPID_AUTO_TEST_CASE(testIsBound)
+{
+ Queue::shared_ptr a(new Queue("a", true));
+ Queue::shared_ptr b(new Queue("b", true));
+ Queue::shared_ptr c(new Queue("c", true));
+ Queue::shared_ptr d(new Queue("d", true));
+
+ string k1("abc");
+ string k2("def");
+ string k3("xyz");
+
+ FanOutExchange fanout("fanout");
+ BOOST_CHECK(fanout.bind(a, "", 0));
+ BOOST_CHECK(fanout.bind(b, "", 0));
+ BOOST_CHECK(fanout.bind(c, "", 0));
+
+ BOOST_CHECK(fanout.isBound(a, 0, 0));
+ BOOST_CHECK(fanout.isBound(b, 0, 0));
+ BOOST_CHECK(fanout.isBound(c, 0, 0));
+ BOOST_CHECK(!fanout.isBound(d, 0, 0));
+
+ DirectExchange direct("direct");
+ BOOST_CHECK(direct.bind(a, k1, 0));
+ BOOST_CHECK(direct.bind(a, k3, 0));
+ BOOST_CHECK(direct.bind(b, k2, 0));
+ BOOST_CHECK(direct.bind(c, k1, 0));
+
+ BOOST_CHECK(direct.isBound(a, 0, 0));
+ BOOST_CHECK(direct.isBound(a, &k1, 0));
+ BOOST_CHECK(direct.isBound(a, &k3, 0));
+ BOOST_CHECK(!direct.isBound(a, &k2, 0));
+ BOOST_CHECK(direct.isBound(b, 0, 0));
+ BOOST_CHECK(direct.isBound(b, &k2, 0));
+ BOOST_CHECK(direct.isBound(c, &k1, 0));
+ BOOST_CHECK(!direct.isBound(d, 0, 0));
+ BOOST_CHECK(!direct.isBound(d, &k1, 0));
+ BOOST_CHECK(!direct.isBound(d, &k2, 0));
+ BOOST_CHECK(!direct.isBound(d, &k3, 0));
+
+ TopicExchange topic("topic");
+ BOOST_CHECK(topic.bind(a, k1, 0));
+ BOOST_CHECK(topic.bind(a, k3, 0));
+ BOOST_CHECK(topic.bind(b, k2, 0));
+ BOOST_CHECK(topic.bind(c, k1, 0));
+
+ BOOST_CHECK(topic.isBound(a, 0, 0));
+ BOOST_CHECK(topic.isBound(a, &k1, 0));
+ BOOST_CHECK(topic.isBound(a, &k3, 0));
+ BOOST_CHECK(!topic.isBound(a, &k2, 0));
+ BOOST_CHECK(topic.isBound(b, 0, 0));
+ BOOST_CHECK(topic.isBound(b, &k2, 0));
+ BOOST_CHECK(topic.isBound(c, &k1, 0));
+ BOOST_CHECK(!topic.isBound(d, 0, 0));
+ BOOST_CHECK(!topic.isBound(d, &k1, 0));
+ BOOST_CHECK(!topic.isBound(d, &k2, 0));
+ BOOST_CHECK(!topic.isBound(d, &k3, 0));
+
+ HeadersExchange headers("headers");
+ FieldTable args1;
+ args1.setString("x-match", "all");
+ args1.setString("a", "A");
+ args1.setInt("b", 1);
+ FieldTable args2;
+ args2.setString("x-match", "any");
+ args2.setString("a", "A");
+ args2.setInt("b", 1);
+ FieldTable args3;
+ args3.setString("x-match", "any");
+ args3.setString("c", "C");
+ args3.setInt("b", 6);
+
+ headers.bind(a, "", &args1);
+ headers.bind(a, "", &args3);
+ headers.bind(b, "", &args2);
+ headers.bind(c, "", &args1);
+
+ BOOST_CHECK(headers.isBound(a, 0, 0));
+ BOOST_CHECK(headers.isBound(a, 0, &args1));
+ BOOST_CHECK(headers.isBound(a, 0, &args3));
+ BOOST_CHECK(!headers.isBound(a, 0, &args2));
+ BOOST_CHECK(headers.isBound(b, 0, 0));
+ BOOST_CHECK(headers.isBound(b, 0, &args2));
+ BOOST_CHECK(headers.isBound(c, 0, &args1));
+ BOOST_CHECK(!headers.isBound(d, 0, 0));
+ BOOST_CHECK(!headers.isBound(d, 0, &args1));
+ BOOST_CHECK(!headers.isBound(d, 0, &args2));
+ BOOST_CHECK(!headers.isBound(d, 0, &args3));
+}
+
+QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare)
+{
+ ExchangeRegistry exchanges;
+ exchanges.declare("my-exchange", "direct", false, FieldTable());
+ exchanges.destroy("my-exchange");
+ try {
+ exchanges.get("my-exchange");
+ } catch (const NotFoundException&) {}
+ std::pair<Exchange::shared_ptr, bool> response = exchanges.declare("my-exchange", "direct", false, FieldTable());
+ BOOST_CHECK_EQUAL(string("direct"), response.first->getType());
+}
+
+intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) {
+ intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ return msg;
+}
+
+QPID_AUTO_TEST_CASE(testSequenceOptions)
+{
+ FieldTable args;
+ args.setInt("qpid.msg_sequence",1);
+ char* buff = new char[10000];
+ framing::Buffer buffer(buff,10000);
+ {
+ DirectExchange direct("direct1", false, args);
+
+ intrusive_ptr<Message> msg1 = cmessage("e", "A");
+ intrusive_ptr<Message> msg2 = cmessage("e", "B");
+ intrusive_ptr<Message> msg3 = cmessage("e", "C");
+
+ DeliverableMessage dmsg1(msg1);
+ DeliverableMessage dmsg2(msg2);
+ DeliverableMessage dmsg3(msg3);
+
+ direct.route(dmsg1, "abc", 0);
+ direct.route(dmsg2, "abc", 0);
+ direct.route(dmsg3, "abc", 0);
+
+ BOOST_CHECK_EQUAL(1, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ BOOST_CHECK_EQUAL(2, msg2->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ BOOST_CHECK_EQUAL(3, msg3->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ FanOutExchange fanout("fanout1", false, args);
+ HeadersExchange header("headers1", false, args);
+ TopicExchange topic ("topic1", false, args);
+
+ // check other exchanges, that they preroute
+ intrusive_ptr<Message> msg4 = cmessage("e", "A");
+ intrusive_ptr<Message> msg5 = cmessage("e", "B");
+ intrusive_ptr<Message> msg6 = cmessage("e", "C");
+
+ DeliverableMessage dmsg4(msg4);
+ DeliverableMessage dmsg5(msg5);
+ DeliverableMessage dmsg6(msg6);
+
+ fanout.route(dmsg4, "abc", 0);
+ BOOST_CHECK_EQUAL(1, msg4->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ FieldTable headers;
+ header.route(dmsg5, "abc", &headers);
+ BOOST_CHECK_EQUAL(1, msg5->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ topic.route(dmsg6, "abc", 0);
+ BOOST_CHECK_EQUAL(1, msg6->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ direct.encode(buffer);
+ }
+ {
+
+ ExchangeRegistry exchanges;
+ buffer.reset();
+ DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer);
+
+ intrusive_ptr<Message> msg1 = cmessage("e", "A");
+ DeliverableMessage dmsg1(msg1);
+ exch_dec->route(dmsg1, "abc", 0);
+
+ BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ }
+ delete [] buff;
+}
+
+QPID_AUTO_TEST_CASE(testIVEOption)
+{
+ FieldTable args;
+ args.setInt("qpid.ive",1);
+ DirectExchange direct("direct1", false, args);
+ FanOutExchange fanout("fanout1", false, args);
+ HeadersExchange header("headers1", false, args);
+ TopicExchange topic ("topic1", false, args);
+
+ intrusive_ptr<Message> msg1 = cmessage("direct1", "abc");
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString("a", "abc");
+ DeliverableMessage dmsg1(msg1);
+
+ FieldTable args2;
+ args2.setString("x-match", "any");
+ args2.setString("a", "abc");
+
+ direct.route(dmsg1, "abc", 0);
+ fanout.route(dmsg1, "abc", 0);
+ header.route(dmsg1, "abc", &args2);
+ topic.route(dmsg1, "abc", 0);
+ Queue::shared_ptr queue(new Queue("queue", true));
+ Queue::shared_ptr queue1(new Queue("queue1", true));
+ Queue::shared_ptr queue2(new Queue("queue2", true));
+ Queue::shared_ptr queue3(new Queue("queue3", true));
+
+ BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties<MessageProperties>()->getApplicationHeaders()));
+
+ BOOST_CHECK(direct.bind(queue, "abc", 0));
+ BOOST_CHECK(fanout.bind(queue1, "abc", 0));
+ BOOST_CHECK(header.bind(queue2, "", &args2));
+ BOOST_CHECK(topic.bind(queue3, "abc", 0));
+
+ BOOST_CHECK_EQUAL(1u,queue->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue1->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue2->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue3->getMessageCount());
+
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp
new file mode 100644
index 0000000000..fe2a14ec03
--- /dev/null
+++ b/qpid/cpp/src/tests/FieldTable.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/framing/Array.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/List.h"
+#include "qpid/sys/alloca.h"
+
+#include "unit_test.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FieldTableTestSuite)
+
+QPID_AUTO_TEST_CASE(testMe)
+{
+ FieldTable ft;
+ ft.setString("A", "BCDE");
+ BOOST_CHECK(string("BCDE") == ft.getAsString("A"));
+
+ char buff[100];
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(ft);
+
+ Buffer rbuffer(buff, 100);
+ FieldTable ft2;
+ rbuffer.get(ft2);
+ BOOST_CHECK(string("BCDE") == ft2.getAsString("A"));
+
+}
+
+QPID_AUTO_TEST_CASE(testAssignment)
+{
+ FieldTable a;
+ FieldTable b;
+
+ a.setString("A", "BBBB");
+ a.setInt("B", 1234);
+ b = a;
+ a.setString("A", "CCCC");
+
+ BOOST_CHECK(string("CCCC") == a.getAsString("A"));
+ BOOST_CHECK(string("BBBB") == b.getAsString("A"));
+ BOOST_CHECK_EQUAL(1234, a.getAsInt("B"));
+ BOOST_CHECK_EQUAL(1234, b.getAsInt("B"));
+ BOOST_CHECK(IntegerValue(1234) == *a.get("B"));
+ BOOST_CHECK(IntegerValue(1234) == *b.get("B"));
+
+ FieldTable d;
+ {
+ FieldTable c;
+ c = a;
+
+ char* buff = static_cast<char*>(::alloca(c.encodedSize()));
+ Buffer wbuffer(buff, c.encodedSize());
+ wbuffer.put(c);
+
+ Buffer rbuffer(buff, c.encodedSize());
+ rbuffer.get(d);
+ BOOST_CHECK_EQUAL(c, d);
+ BOOST_CHECK(string("CCCC") == c.getAsString("A"));
+ BOOST_CHECK(IntegerValue(1234) == *c.get("B"));
+ }
+ BOOST_CHECK(string("CCCC") == d.getAsString("A"));
+ BOOST_CHECK(IntegerValue(1234) == *d.get("B"));
+}
+
+
+QPID_AUTO_TEST_CASE(testNestedValues)
+{
+ double d = 1.2345;
+ uint32_t u = 101;
+ char buff[1000];
+ {
+ FieldTable a;
+ FieldTable b;
+ std::vector<std::string> items;
+ items.push_back("one");
+ items.push_back("two");
+ Array c(items);
+ List list;
+ list.push_back(List::ValuePtr(new Str16Value("red")));
+ list.push_back(List::ValuePtr(new Unsigned32Value(u)));
+ list.push_back(List::ValuePtr(new Str8Value("yellow")));
+ list.push_back(List::ValuePtr(new DoubleValue(d)));
+
+ a.setString("id", "A");
+ b.setString("id", "B");
+ a.setTable("B", b);
+ a.setArray("C", c);
+ a.set("my-list", FieldTable::ValuePtr(new ListValue(list)));
+
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ FieldTable b;
+ Array c;
+ rbuffer.get(a);
+ BOOST_CHECK(string("A") == a.getAsString("id"));
+ a.getTable("B", b);
+ BOOST_CHECK(string("B") == b.getAsString("id"));
+ a.getArray("C", c);
+ std::vector<std::string> items;
+ c.collect(items);
+ BOOST_CHECK((uint) 2 == items.size());
+ BOOST_CHECK(string("one") == items[0]);
+ BOOST_CHECK(string("two") == items[1]);
+
+ List list;
+ BOOST_CHECK(a.get("my-list")->get<List>(list));
+ List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("red"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(u, (uint32_t) (*i)->get<int>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("yellow"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(d, (*i)->get<double>());
+
+ i++;
+ BOOST_CHECK(i == list.end());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testFloatAndDouble)
+{
+ char buff[100];
+ float f = 5.672f;
+ double d = 56.720001;
+ {
+ FieldTable a;
+ a.setString("string", "abc");
+ a.setInt("int", 5672);
+ a.setFloat("float", f);
+ a.setDouble("double", d);
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ rbuffer.get(a);
+ BOOST_CHECK(string("abc") == a.getAsString("string"));
+ BOOST_CHECK(5672 == a.getAsInt("int"));
+ float f2;
+ BOOST_CHECK(!a.getFloat("string", f2));
+ BOOST_CHECK(!a.getFloat("int", f2));
+ BOOST_CHECK(a.getFloat("float", f2));
+ BOOST_CHECK(f2 == f);
+
+ double d2;
+ BOOST_CHECK(!a.getDouble("string", d2));
+ BOOST_CHECK(!a.getDouble("int", d2));
+ BOOST_CHECK(a.getDouble("double", d2));
+ BOOST_CHECK(d2 == d);
+ }
+}
+
+QPID_AUTO_TEST_CASE(test64GetAndSetConverts)
+{
+ FieldTable args;
+ args.setInt64("a",100);
+ args.setInt64("b",-(int64_t) ((int64_t) 1<<34));
+
+ args.setUInt64("c",1u);
+ args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34));
+ BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c"));
+ BOOST_CHECK_EQUAL(100u, args.getAsUInt64("a"));
+ BOOST_CHECK_EQUAL(1, args.getAsInt64("c"));
+ BOOST_CHECK_EQUAL(100, args.getAsInt64("a"));
+ BOOST_CHECK_EQUAL(-(int64_t) ((int64_t) 1<<34), args.getAsInt64("b"));
+ BOOST_CHECK_EQUAL((uint64_t) ((uint64_t) 1<<34), args.getAsUInt64("d"));
+ BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d"));
+
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp
new file mode 100644
index 0000000000..0ebd0d7d44
--- /dev/null
+++ b/qpid/cpp/src/tests/FieldValue.cpp
@@ -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.
+ */
+#include "qpid/framing/FieldValue.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FieldValueTestSuite)
+
+using namespace qpid::framing;
+
+Str16Value s("abc");
+IntegerValue i(42);
+//DecimalValue d(1234,2);
+//FieldTableValue ft;
+//EmptyValue e;
+
+QPID_AUTO_TEST_CASE(testStr16ValueEquals)
+{
+
+ BOOST_CHECK(Str16Value("abc") == s);
+ BOOST_CHECK(Str16Value("foo") != s);
+ BOOST_CHECK(s != i);
+ BOOST_CHECK(s.convertsTo<std::string>() == true);
+ BOOST_CHECK(s.convertsTo<int>() == false);
+ BOOST_CHECK(s.get<std::string>() == "abc");
+ BOOST_CHECK_THROW(s.get<int>(), InvalidConversionException);
+// BOOST_CHECK(s != ft);
+
+}
+
+QPID_AUTO_TEST_CASE(testIntegerValueEquals)
+{
+ BOOST_CHECK(IntegerValue(42) == i);
+ BOOST_CHECK(IntegerValue(5) != i);
+ BOOST_CHECK(i != s);
+ BOOST_CHECK(i.convertsTo<std::string>() == false);
+ BOOST_CHECK(i.convertsTo<int>() == true);
+ BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException);
+ BOOST_CHECK(i.get<int>() == 42);
+// BOOST_CHECK(i != ft);
+}
+
+#if 0
+QPID_AUTO_TEST_CASE(testDecimalValueEquals)
+{
+ BOOST_CHECK(DecimalValue(1234, 2) == d);
+ BOOST_CHECK(DecimalValue(12345, 2) != d);
+ BOOST_CHECK(DecimalValue(1234, 3) != d);
+ BOOST_CHECK(d != s);
+}
+
+QPID_AUTO_TEST_CASE(testFieldTableValueEquals)
+{
+ ft.getValue().setString("foo", "FOO");
+ ft.getValue().setInt("magic", 7);
+
+ BOOST_CHECK_EQUAL(std::string("FOO"),
+ ft.getValue().getString("foo"));
+ BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic"));
+
+ FieldTableValue f2;
+ BOOST_CHECK(ft != f2);
+ f2.getValue().setString("foo", "FOO");
+ BOOST_CHECK(ft != f2);
+ f2.getValue().setInt("magic", 7);
+ BOOST_CHECK_EQUAL(ft,f2);
+ BOOST_CHECK(ft == f2);
+ f2.getValue().setString("foo", "BAR");
+ BOOST_CHECK(ft != f2);
+ BOOST_CHECK(ft != i);
+}
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ForkedBroker.cpp b/qpid/cpp/src/tests/ForkedBroker.cpp
new file mode 100644
index 0000000000..53eaa7e1ce
--- /dev/null
+++ b/qpid/cpp/src/tests/ForkedBroker.cpp
@@ -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.
+ *
+ */
+
+#include "ForkedBroker.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <signal.h>
+
+using namespace std;
+using qpid::ErrnoException;
+
+namespace std {
+static ostream& operator<<(ostream& o, const qpid::tests::ForkedBroker::Args& a) {
+copy(a.begin(), a.end(), ostream_iterator<string>(o, " "));
+return o;
+}
+}
+
+namespace qpid {
+namespace tests {
+
+ForkedBroker::ForkedBroker(const Args& constArgs) : running(false), exitStatus(0) {
+ Args args(constArgs);
+ // Substitute the special value "TMP_DATA_DIR" with a temporary data dir.
+ Args::iterator i = find(args.begin(), args.end(), string("TMP_DATA_DIR"));
+ if (i != args.end()) {
+ args.erase(i);
+ char dd[] = "/tmp/ForkedBroker.XXXXXX";
+ if (!mkdtemp(dd))
+ throw qpid::ErrnoException("Can't create data dir");
+ dataDir = dd;
+ args.push_back("--data-dir");
+ args.push_back(dataDir);
+ }
+ // Never use the default data directory, set --no-data-dir if no other data-dir arg.
+ Args::iterator j = find(args.begin(), args.end(), string("--data-dir"));
+ Args::iterator k = find(args.begin(), args.end(), string("--no-data-dir"));
+ if (j == args.end() && k == args.end())
+ args.push_back("--no-data-dir");
+ init(args);
+}
+
+ForkedBroker::~ForkedBroker() {
+ try { kill(); }
+ catch (const std::exception& e) {
+ QPID_LOG(error, QPID_MSG("Killing forked broker: " << e.what()));
+ }
+ if (!dataDir.empty())
+ {
+ int unused_ret; // Suppress warnings about ignoring return value.
+ unused_ret = ::system(("rm -rf "+dataDir).c_str());
+ }
+}
+
+void ForkedBroker::kill(int sig) {
+ if (pid == 0) return;
+ int savePid = pid;
+ pid = 0; // Reset pid here in case of an exception.
+ using qpid::ErrnoException;
+ if (::kill(savePid, sig) < 0)
+ throw ErrnoException("kill failed");
+ int status;
+ if (::waitpid(savePid, &status, 0) < 0 && sig != 9)
+ throw ErrnoException("wait for forked process failed");
+ if (WEXITSTATUS(status) != 0 && sig != 9)
+ throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status)));
+ running = false;
+ exitStatus = status;
+}
+
+bool isLogOption(const std::string& s) {
+ const char * log_enable = "--log-enable",
+ * trace = "--trace";
+ return( (! strncmp(s.c_str(), log_enable, strlen(log_enable))) ||
+ (! strncmp(s.c_str(), trace, strlen(trace)))
+ );
+}
+
+namespace {
+ void ignore_signal(int)
+ {
+ }
+}
+
+void ForkedBroker::init(const Args& userArgs) {
+ using qpid::ErrnoException;
+ port = 0;
+ int pipeFds[2];
+ if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
+
+ // Ignore the SIGCHLD signal generated by an exitting child
+ // We will clean up any exitting children in the waitpid above
+ // This should really be neater (like only once not per fork)
+ struct ::sigaction sa;
+ sa.sa_handler = ignore_signal;
+ ::sigemptyset(&sa.sa_mask);
+ ::sigaddset(&sa.sa_mask, SIGCHLD);
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ ::sigaction(SIGCHLD, &sa, 0);
+
+ pid = ::fork();
+ if (pid < 0) throw ErrnoException("Fork failed");
+ if (pid) { // parent
+ ::close(pipeFds[1]);
+ FILE* f = ::fdopen(pipeFds[0], "r");
+ if (!f) throw ErrnoException("fopen failed");
+ if (::fscanf(f, "%d", &port) != 1) {
+ if (ferror(f)) throw ErrnoException("Error reading port number from child.");
+ else throw qpid::Exception("EOF reading port number from child.");
+ }
+ ::fclose(f);
+ running = true;
+ }
+ else { // child
+ ::close(pipeFds[0]);
+ int fd = ::dup2(pipeFds[1], 1); // pipe stdout to the parent.
+ if (fd < 0) throw ErrnoException("dup2 failed");
+ const char* prog = ::getenv("QPIDD_EXEC");
+ if (!prog) prog = "../qpidd"; // This only works from within svn checkout
+ Args args(userArgs);
+ args.push_back("--port=0");
+ // Keep quiet except for errors.
+ if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")
+ && find_if(userArgs.begin(), userArgs.end(), isLogOption) == userArgs.end())
+ args.push_back("--log-enable=error+");
+ std::vector<const char*> argv(args.size());
+ std::transform(args.begin(), args.end(), argv.begin(), boost::bind(&std::string::c_str, _1));
+ argv.push_back(0);
+ QPID_LOG(debug, "ForkedBroker exec " << prog << ": " << args);
+
+ execv(prog, const_cast<char* const*>(&argv[0]));
+ QPID_LOG(critical, "execv failed to start broker: prog=\"" << prog << "\"; args=\"" << args << "\"; errno=" << errno << " (" << std::strerror(errno) << ")");
+ ::exit(1);
+ }
+}
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ForkedBroker.h b/qpid/cpp/src/tests/ForkedBroker.h
new file mode 100644
index 0000000000..87e141a425
--- /dev/null
+++ b/qpid/cpp/src/tests/ForkedBroker.h
@@ -0,0 +1,82 @@
+#ifndef TESTS_FORKEDBROKER_H
+#define TESTS_FORKEDBROKER_H
+
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/broker/Broker.h"
+#include <boost/lexical_cast.hpp>
+#include <string>
+#include <stdio.h>
+#include <sys/wait.h>
+
+namespace qpid {
+namespace tests {
+
+/**
+ * Class to fork a broker child process.
+ *
+ * For most tests a BrokerFixture may be more convenient as it starts
+ * a broker in the same process which allows you to easily debug into
+ * the broker.
+ *
+ * This useful for tests that need to start multiple brokers where
+ * those brokers can't coexist in the same process (e.g. for cluster
+ * tests where CPG doesn't allow multiple group members in a single
+ * process.)
+ *
+ */
+class ForkedBroker {
+ public:
+ typedef std::vector<std::string> Args;
+
+ // argv args are passed to broker.
+ //
+ // Special value "TMP_DATA_DIR" is substituted with a temporary
+ // data directory for the broker.
+ //
+ ForkedBroker(const Args& argv);
+ ~ForkedBroker();
+
+ void kill(int sig=SIGINT);
+ int wait(); // Wait for exit, return exit status.
+ uint16_t getPort() { return port; }
+ pid_t getPID() { return pid; }
+ bool isRunning() { return running; }
+
+ private:
+
+ void init(const Args& args);
+
+ bool running;
+ int exitStatus;
+
+ pid_t pid;
+ int port;
+ std::string dataDir;
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_FORKEDBROKER_H*/
diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp
new file mode 100644
index 0000000000..1270eabba3
--- /dev/null
+++ b/qpid/cpp/src/tests/Frame.cpp
@@ -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.
+ *
+ */
+#include "qpid/framing/Frame.h"
+
+#include <boost/lexical_cast.hpp>
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FrameTestSuite)
+
+using namespace std;
+using namespace qpid::framing;
+using namespace boost;
+
+QPID_AUTO_TEST_CASE(testContentBody) {
+ Frame f(42, AMQContentBody("foobar"));
+ AMQBody* body=f.getBody();
+ BOOST_CHECK(dynamic_cast<AMQContentBody*>(body));
+ Buffer b(f.encodedSize();
+ f.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ AMQContentBody* content=dynamic_cast<AMQContentBody*>(g.getBody());
+ BOOST_REQUIRE(content);
+ BOOST_CHECK_EQUAL(content->getData(), "foobar");
+}
+
+QPID_AUTO_TEST_CASE(testMethodBody) {
+ FieldTable args;
+ args.setString("foo", "bar");
+ Frame f(
+ 42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex",
+ true, false, true, false, true, args));
+ BOOST_CHECK_EQUAL(f.getChannel(), 42);
+ Buffer b(f.encodedSize();
+ f.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ BOOST_CHECK_EQUAL(f.getChannel(), g.getChannel());
+ QueueDeclareBody* declare=dynamic_cast<QueueDeclareBody*>(g.getBody());
+ BOOST_REQUIRE(declare);
+ BOOST_CHECK_EQUAL(declare->getAlternateExchange(), "altex");
+ BOOST_CHECK_EQUAL(lexical_cast<string>(*f.getBody()), lexical_cast<string>(*g.getBody()));
+}
+
+QPID_AUTO_TEST_CASE(testLoop) {
+ // Run in a loop so heap profiler can spot any allocations.
+ Buffer b(1024);
+ for (int i = 0; i < 100; ++i) {
+ Frame ctor(2, AccessRequestOkBody(ProtocolVersion(), 42));
+ Frame assign(3);
+ assign.body = AccessRequestOkBody(ProtocolVersion(), 42);
+ assign.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ BOOST_REQUIRE(dynamic_cast<AccessRequestOkBody*>(g.getBody())->getTicket() == 42);
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FrameDecoder.cpp b/qpid/cpp/src/tests/FrameDecoder.cpp
new file mode 100644
index 0000000000..9eeff2a41e
--- /dev/null
+++ b/qpid/cpp/src/tests/FrameDecoder.cpp
@@ -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.
+ *
+ */
+
+#include "unit_test.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameDecoder.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/Buffer.h"
+#include <string>
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FrameDecoderTest)
+
+using namespace std;
+using namespace qpid::framing;
+
+
+string makeData(int size) {
+ string data;
+ data.resize(size);
+ for (int i =0; i < size; ++i)
+ data[i] = 'a' + (i%26);
+ return data;
+}
+string encodeFrame(string data) {
+ AMQFrame f((AMQContentBody(data)));
+ string encoded;
+ encoded.resize(f.encodedSize());
+ Buffer b(&encoded[0], encoded.size());
+ f.encode(b);
+ return encoded;
+}
+
+string getData(const AMQFrame& frame) {
+ const AMQContentBody* content = dynamic_cast<const AMQContentBody*>(frame.getBody());
+ BOOST_CHECK(content);
+ return content->getData();
+}
+
+QPID_AUTO_TEST_CASE(testByteFragments) {
+ string data = makeData(42);
+ string encoded = encodeFrame(data);
+ FrameDecoder decoder;
+ for (size_t i = 0; i < encoded.size()-1; ++i) {
+ Buffer buf(&encoded[i], 1);
+ BOOST_CHECK(!decoder.decode(buf));
+ }
+ Buffer buf(&encoded[encoded.size()-1], 1);
+ BOOST_CHECK(decoder.decode(buf));
+ BOOST_CHECK_EQUAL(data, getData(decoder.getFrame()));
+}
+
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp
new file mode 100644
index 0000000000..f8795316cc
--- /dev/null
+++ b/qpid/cpp/src/tests/FramingTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/client/Connection.h"
+#include "qpid/client/Connector.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/framing/all_method_bodies.h"
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "unit_test.h"
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+#include <iostream>
+
+#include <memory>
+#include <sstream>
+#include <typeinfo>
+
+using namespace qpid;
+using namespace qpid::framing;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+template <class T>
+std::string tostring(const T& x)
+{
+ std::ostringstream out;
+ out << x;
+ return out.str();
+}
+
+QPID_AUTO_TEST_SUITE(FramingTestSuite)
+
+QPID_AUTO_TEST_CASE(testMessageTransferBody)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ MessageTransferBody in(version, "my-exchange", 1, 1);
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ MessageTransferBody out(version);
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(testConnectionSecureBody)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ std::string s = "security credential";
+ ConnectionSecureBody in(version, s);
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ ConnectionSecureBody out(version);
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(testConnectionRedirectBody)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ std::string a = "hostA";
+ std::string b = "hostB";
+ Array hosts(0x95);
+ hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a)));
+ hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b)));
+
+ ConnectionRedirectBody in(version, a, hosts);
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ ConnectionRedirectBody out(version);
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(testQueueDeclareBody)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ QueueDeclareBody in(version, "name", "dlq", true, false, true, false, FieldTable());
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ QueueDeclareBody out(version);
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ std::string a = "hostA";
+ std::string b = "hostB";
+ Array hosts(0x95);
+ hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a)));
+ hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b)));
+
+ AMQFrame in((ConnectionRedirectBody(version, a, hosts)));
+ in.setChannel(999);
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ AMQFrame out;
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame)
+{
+ char buffer[1024];
+ ProtocolVersion version(highestProtocolVersion);
+ Buffer wbuff(buffer, sizeof(buffer));
+ AMQFrame in((MessageCancelBody(version, "tag")));
+ in.setChannel(999);
+ in.encode(wbuff);
+
+ Buffer rbuff(buffer, sizeof(buffer));
+ AMQFrame out;
+ out.decode(rbuff);
+ BOOST_CHECK_EQUAL(tostring(in), tostring(out));
+}
+
+QPID_AUTO_TEST_CASE(badStrings) {
+ char data[(65535 + 2) + (255 + 1)];
+ Buffer b(data, sizeof(data));
+ BOOST_CHECK_THROW(b.putShortString(std::string(256, 'X')),
+ Exception);
+ BOOST_CHECK_THROW(b.putMediumString(std::string(65536, 'X')),
+ Exception);
+ b.putShortString(std::string(255, 'X'));
+ b.putMediumString(std::string(65535, 'X'));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/HeaderTest.cpp b/qpid/cpp/src/tests/HeaderTest.cpp
new file mode 100644
index 0000000000..4b16f3c793
--- /dev/null
+++ b/qpid/cpp/src/tests/HeaderTest.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/framing/amqp_framing.h"
+#include "qpid/framing/FieldValue.h"
+#include "unit_test.h"
+
+using namespace qpid::framing;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(HeaderTestSuite)
+
+QPID_AUTO_TEST_CASE(testGenericProperties)
+{
+ AMQHeaderBody body;
+ body.get<MessageProperties>(true)->getApplicationHeaders().setString(
+ "A", "BCDE");
+ char buff[100];
+ Buffer wbuffer(buff, 100);
+ body.encode(wbuffer);
+
+ Buffer rbuffer(buff, 100);
+ AMQHeaderBody body2;
+ body2.decode(rbuffer, body.encodedSize());
+ MessageProperties* props =
+ body2.get<MessageProperties>(true);
+ BOOST_CHECK_EQUAL(
+ string("BCDE"),
+ props->getApplicationHeaders().get("A")->get<string>());
+}
+
+QPID_AUTO_TEST_CASE(testMessageProperties)
+{
+ AMQFrame out((AMQHeaderBody()));
+ MessageProperties* props1 =
+ out.castBody<AMQHeaderBody>()->get<MessageProperties>(true);
+
+ props1->setContentLength(42);
+ props1->setMessageId(Uuid(true));
+ props1->setCorrelationId("correlationId");
+ props1->setReplyTo(ReplyTo("ex","key"));
+ props1->setContentType("contentType");
+ props1->setContentEncoding("contentEncoding");
+ props1->setUserId("userId");
+ props1->setAppId("appId");
+
+ char buff[10000];
+ Buffer wbuffer(buff, 10000);
+ out.encode(wbuffer);
+
+ Buffer rbuffer(buff, 10000);
+ AMQFrame in;
+ in.decode(rbuffer);
+ MessageProperties* props2 =
+ in.castBody<AMQHeaderBody>()->get<MessageProperties>(true);
+
+ BOOST_CHECK_EQUAL(props1->getContentLength(), props2->getContentLength());
+ BOOST_CHECK_EQUAL(props1->getMessageId(), props2->getMessageId());
+ BOOST_CHECK_EQUAL(props1->getCorrelationId(), props2->getCorrelationId());
+ BOOST_CHECK_EQUAL(props1->getContentType(), props2->getContentType());
+ BOOST_CHECK_EQUAL(props1->getContentEncoding(), props2->getContentEncoding());
+ BOOST_CHECK_EQUAL(props1->getUserId(), props2->getUserId());
+ BOOST_CHECK_EQUAL(props1->getAppId(), props2->getAppId());
+
+}
+
+QPID_AUTO_TEST_CASE(testDeliveryProperies)
+{
+ AMQFrame out((AMQHeaderBody()));
+ DeliveryProperties* props1 =
+ out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true);
+
+ props1->setDiscardUnroutable(true);
+ props1->setExchange("foo");
+
+ char buff[10000];
+ Buffer wbuffer(buff, 10000);
+ out.encode(wbuffer);
+
+ Buffer rbuffer(buff, 10000);
+ AMQFrame in;
+ in.decode(rbuffer);
+ DeliveryProperties* props2 =
+ in.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true);
+
+ BOOST_CHECK(props2->getDiscardUnroutable());
+ BOOST_CHECK_EQUAL(string("foo"), props2->getExchange());
+ BOOST_CHECK(!props2->hasTimestamp());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/qpid/cpp/src/tests/HeadersExchangeTest.cpp
new file mode 100644
index 0000000000..40deb59c86
--- /dev/null
+++ b/qpid/cpp/src/tests/HeadersExchangeTest.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/broker/HeadersExchange.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "unit_test.h"
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite)
+
+QPID_AUTO_TEST_CASE(testMatchAll)
+{
+ FieldTable b, m, n;
+ b.setString("x-match", "all");
+ b.setString("foo", "FOO");
+ b.setInt("n", 42);
+ m.setString("foo", "FOO");
+ m.setInt("n", 42);
+ BOOST_CHECK(HeadersExchange::match(b, m));
+
+ // Ignore extras.
+ m.setString("extra", "x");
+ BOOST_CHECK(HeadersExchange::match(b, m));
+
+ // Fail mismatch, wrong value.
+ m.setString("foo", "NotFoo");
+ BOOST_CHECK(!HeadersExchange::match(b, m));
+
+ // Fail mismatch, missing value
+ n.setInt("n", 42);
+ n.setString("extra", "x");
+ BOOST_CHECK(!HeadersExchange::match(b, n));
+}
+
+QPID_AUTO_TEST_CASE(testMatchAny)
+{
+ FieldTable b, m, n;
+ b.setString("x-match", "any");
+ b.setString("foo", "FOO");
+ b.setInt("n", 42);
+ m.setString("foo", "FOO");
+ BOOST_CHECK(!HeadersExchange::match(b, n));
+ BOOST_CHECK(HeadersExchange::match(b, m));
+ m.setInt("n", 42);
+ BOOST_CHECK(HeadersExchange::match(b, m));
+}
+
+QPID_AUTO_TEST_CASE(testMatchEmptyValue)
+{
+ FieldTable b, m;
+ b.setString("x-match", "all");
+ b.set("foo", FieldTable::ValuePtr());
+ b.set("n", FieldTable::ValuePtr());
+ BOOST_CHECK(!HeadersExchange::match(b, m));
+ m.setString("foo", "blah");
+ m.setInt("n", 123);
+}
+
+QPID_AUTO_TEST_CASE(testMatchEmptyArgs)
+{
+ FieldTable b, m;
+ m.setString("foo", "FOO");
+
+ b.setString("x-match", "all");
+ BOOST_CHECK(HeadersExchange::match(b, m));
+ b.setString("x-match", "any");
+ BOOST_CHECK(!HeadersExchange::match(b, m));
+}
+
+
+QPID_AUTO_TEST_CASE(testMatchNoXMatch)
+{
+ FieldTable b, m;
+ b.setString("foo", "FOO");
+ m.setString("foo", "FOO");
+ BOOST_CHECK(!HeadersExchange::match(b, m));
+}
+
+QPID_AUTO_TEST_CASE(testBindNoXMatch)
+{
+ HeadersExchange exchange("test");
+ Queue::shared_ptr queue;
+ std::string key;
+ FieldTable args;
+ try {
+ //just checking this doesn't cause assertion etc
+ exchange.bind(queue, key, &args);
+ } catch(qpid::Exception&) {
+ //expected
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/InitialStatusMap.cpp b/qpid/cpp/src/tests/InitialStatusMap.cpp
new file mode 100644
index 0000000000..ecbe2d4161
--- /dev/null
+++ b/qpid/cpp/src/tests/InitialStatusMap.cpp
@@ -0,0 +1,235 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/cluster/InitialStatusMap.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::framing::cluster;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(InitialStatusMapTestSuite)
+
+typedef InitialStatusMap::Status Status;
+
+Status activeStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet()) {
+ return Status(ProtocolVersion(), 0, true, id, STORE_STATE_NO_STORE, Uuid(),
+ encodeMemberSet(ms));
+}
+
+Status newcomerStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet()) {
+ return Status(ProtocolVersion(), 0, false, id, STORE_STATE_NO_STORE, Uuid(),
+ encodeMemberSet(ms));
+}
+
+Status storeStatus(bool active, StoreState state, Uuid start=Uuid(), Uuid stop=Uuid(),
+ const MemberSet& ms=MemberSet())
+{
+ return Status(ProtocolVersion(), 0, active, start, state, stop,
+ encodeMemberSet(ms));
+}
+
+QPID_AUTO_TEST_CASE(testFirstInCluster) {
+ // Single member is first in cluster.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ BOOST_CHECK(!map.isComplete());
+ MemberSet members = list_of(MemberId(0));
+ map.configChange(members);
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(0), newcomerStatus(id, list_of<MemberId>(0)));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.getElders().empty());
+ BOOST_CHECK(!map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(id, map.getClusterId());
+}
+
+QPID_AUTO_TEST_CASE(testJoinExistingCluster) {
+ // Single member 0 joins existing cluster 1,2
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), activeStatus(id));
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(2), activeStatus(id));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1)(2));
+ BOOST_CHECK(map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+
+ // Check that transitionToComplete is reset.
+ map.configChange(list_of<MemberId>(0)(1));
+ BOOST_CHECK(!map.transitionToComplete());
+}
+
+QPID_AUTO_TEST_CASE(testMultipleFirstInCluster) {
+ // Multiple members 0,1,2 join at same time.
+ InitialStatusMap map(MemberId(1), 1); // self is 1
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+
+ // All new members
+ map.received(MemberId(0), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
+ map.received(MemberId(1), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
+ map.received(MemberId(2), newcomerStatus(id, list_of<MemberId>(0)(1)(2)));
+ BOOST_CHECK(!map.isResendNeeded());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(2)));
+ BOOST_CHECK(!map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testMultipleJoinExisting) {
+ // Multiple members 2,3 join simultaneously a cluster containing 0,1.
+ InitialStatusMap map(MemberId(2), 1); // self is 2
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2))(MemberId(3));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+ map.received(MemberId(0), activeStatus(id, list_of<MemberId>(0)));
+ map.received(MemberId(1), newcomerStatus(id, list_of<MemberId>(0)(1)));
+ map.received(MemberId(2), newcomerStatus(id, list_of<MemberId>(0)(1)(2)(3)));
+ map.received(MemberId(3), newcomerStatus(id, list_of<MemberId>(0)(1)(2)(3)));
+ BOOST_CHECK(!map.isResendNeeded());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(0)(1)(3));
+ BOOST_CHECK(map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testMembersLeave) {
+ // Test that map completes if members leave rather than send status.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ map.configChange(list_of(MemberId(0))(MemberId(1))(MemberId(2)));
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), activeStatus(id));
+ BOOST_CHECK(!map.isComplete());
+ map.configChange(list_of(MemberId(0))(MemberId(1))); // 2 left
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(1)));
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testInteveningConfig) {
+ // Multiple config changes arrives before we complete the map.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+
+ map.configChange(list_of<MemberId>(0)(1));
+ BOOST_CHECK(map.isResendNeeded());
+ map.received(MemberId(0), newcomerStatus());
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(!map.isResendNeeded());
+ // New member 2 joins before we receive 1
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(map.isResendNeeded());
+ map.received(1, activeStatus(id));
+ map.received(2, newcomerStatus());
+ // We should not be complete as we haven't received 0 since new member joined
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(!map.isResendNeeded());
+
+ map.received(0, newcomerStatus());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1));
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testAllCleanNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testAllEmptyNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testAllNoStoreNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_NO_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_NO_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_NO_STORE));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testDirtyNeedUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_DIRTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testEmptyNeedUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testEmptyAlone) {
+ InitialStatusMap map(MemberId(0), 1);
+ map.configChange(list_of<MemberId>(0));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/InlineAllocator.cpp b/qpid/cpp/src/tests/InlineAllocator.cpp
new file mode 100644
index 0000000000..a4c4d64cea
--- /dev/null
+++ b/qpid/cpp/src/tests/InlineAllocator.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/InlineAllocator.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite)
+
+using namespace qpid;
+using namespace std;
+
+QPID_AUTO_TEST_CASE(testAllocate) {
+ InlineAllocator<std::allocator<char>, 2> alloc;
+
+ char* p = alloc.allocate(1);
+ BOOST_CHECK(p == (char*)&alloc);
+ alloc.deallocate(p,1);
+
+ p = alloc.allocate(2);
+ BOOST_CHECK(p == (char*)&alloc);
+ alloc.deallocate(p,2);
+
+ p = alloc.allocate(3);
+ BOOST_CHECK(p != (char*)&alloc);
+ alloc.deallocate(p,3);
+}
+
+QPID_AUTO_TEST_CASE(testAllocateFull) {
+ InlineAllocator<std::allocator<char>, 1> alloc;
+
+ char* p = alloc.allocate(1);
+ BOOST_CHECK(p == (char*)&alloc);
+
+ char* q = alloc.allocate(1);
+ BOOST_CHECK(q != (char*)&alloc);
+
+ alloc.deallocate(p,1);
+ p = alloc.allocate(1);
+ BOOST_CHECK(p == (char*)&alloc);
+
+ alloc.deallocate(p,1);
+ alloc.deallocate(q,1);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/InlineVector.cpp b/qpid/cpp/src/tests/InlineVector.cpp
new file mode 100644
index 0000000000..ba5165886d
--- /dev/null
+++ b/qpid/cpp/src/tests/InlineVector.cpp
@@ -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.
+ *
+ */
+
+#include "qpid/InlineVector.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(InlineVectorTestSuite)
+
+using namespace qpid;
+using namespace std;
+
+typedef InlineVector<int, 3> Vec;
+
+bool isInline(const Vec& v) {
+ // If nothing, give it the benefit of the doubt;
+ // can't take address of nothing.
+ if (v.size() <= 0)
+ return true;
+ return (const char*)&v <= (const char*)(&v[0]) &&
+ (const char*)(&v[0]) < (const char*)&v+sizeof(v);
+}
+
+QPID_AUTO_TEST_CASE(testCtor) {
+ {
+ Vec v;
+ BOOST_CHECK(isInline(v));
+ BOOST_CHECK(v.empty());
+ }
+ {
+ Vec v(3, 42);
+ BOOST_CHECK(isInline(v));
+ BOOST_CHECK_EQUAL(3u, v.size());
+ BOOST_CHECK_EQUAL(v[0], 42);
+ BOOST_CHECK_EQUAL(v[2], 42);
+
+ Vec u(v);
+ BOOST_CHECK(isInline(u));
+ BOOST_CHECK_EQUAL(3u, u.size());
+ BOOST_CHECK_EQUAL(u[0], 42);
+ BOOST_CHECK_EQUAL(u[2], 42);
+ }
+
+ {
+ Vec v(4, 42);
+
+ BOOST_CHECK_EQUAL(v.size(), 4u);
+ BOOST_CHECK(!isInline(v));
+ Vec u(v);
+ BOOST_CHECK_EQUAL(u.size(), 4u);
+ BOOST_CHECK(!isInline(u));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testInsert) {
+ {
+ Vec v;
+ v.push_back(1);
+ BOOST_CHECK_EQUAL(v.size(), 1u);
+ BOOST_CHECK_EQUAL(v.back(), 1);
+ BOOST_CHECK(isInline(v));
+
+ v.insert(v.begin(), 2);
+ BOOST_CHECK_EQUAL(v.size(), 2u);
+ BOOST_CHECK_EQUAL(v.back(), 1);
+ BOOST_CHECK(isInline(v));
+
+ v.push_back(3);
+ BOOST_CHECK(isInline(v));
+
+ v.push_back(4);
+
+ BOOST_CHECK(!isInline(v));
+ }
+ {
+ Vec v(3,42);
+ v.insert(v.begin(), 9);
+ BOOST_CHECK_EQUAL(v.size(), 4u);
+ BOOST_CHECK(!isInline(v));
+ }
+ {
+ Vec v(3,42);
+ v.insert(v.begin()+1, 9);
+ BOOST_CHECK(!isInline(v));
+ BOOST_CHECK_EQUAL(v.size(), 4u);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testAssign) {
+ Vec v(3,42);
+ Vec u;
+ u = v;
+ BOOST_CHECK(isInline(u));
+ u.push_back(4);
+ BOOST_CHECK(!isInline(u));
+ v = u;
+ BOOST_CHECK(!isInline(v));
+}
+
+QPID_AUTO_TEST_CASE(testResize) {
+ Vec v;
+ v.resize(5);
+ BOOST_CHECK(!isInline(v));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am
new file mode 100644
index 0000000000..ed97c41bff
--- /dev/null
+++ b/qpid/cpp/src/tests/Makefile.am
@@ -0,0 +1,398 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
+PUBLIC_INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include # Use public API only
+QMF_GEN=$(top_srcdir)/managementgen/qmf-gen
+
+abs_builddir=@abs_builddir@
+abs_srcdir=@abs_srcdir@
+
+extra_libs =
+lib_client = $(abs_builddir)/../libqpidclient.la
+lib_messaging = $(abs_builddir)/../libqpidmessaging.la
+lib_common = $(abs_builddir)/../libqpidcommon.la
+lib_broker = $(abs_builddir)/../libqpidbroker.la
+lib_console = $(abs_builddir)/../libqmfconsole.la
+lib_qmf2 = $(abs_builddir)/../libqmf2.la
+# lib_amqp_0_10 = $(abs_builddir)/../libqpidamqp_0_10.la
+
+#
+# Initialize variables that are incremented with +=
+#
+check_PROGRAMS=
+check_LTLIBRARIES=
+TESTS=
+EXTRA_DIST=
+CLEANFILES=
+LONG_TESTS=
+CLEAN_LOCAL=
+
+#
+# Destination for intalled programs and tests defined here
+#
+qpidexecdir = $(libexecdir)/qpid
+qpidexec_PROGRAMS =
+qpidexec_SCRIPTS =
+qpidtestdir = $(qpidexecdir)/tests
+qpidtest_PROGRAMS =
+qpidtest_SCRIPTS =
+tmoduledir = $(libdir)/qpid/tests
+tmodule_LTLIBRARIES=
+
+#
+# Unit test program
+#
+# Unit tests are built as a single program to reduce valgrind overhead
+# when running the tests. If you want to build a subset of the tests do
+# rm -f unit_test; make unit_test unit_test_OBJECTS="unit_test.o SelectedTest.o"
+#
+
+TESTS+=unit_test
+check_PROGRAMS+=unit_test
+unit_test_LDADD=-lboost_unit_test_framework \
+ $(lib_messaging) $(lib_broker) $(lib_console) $(lib_qmf2)
+
+unit_test_SOURCES= unit_test.cpp unit_test.h \
+ MessagingSessionTests.cpp \
+ MessagingThreadTests.cpp \
+ MessagingFixture.h \
+ ClientSessionTest.cpp \
+ BrokerFixture.h SocketProxy.h \
+ exception_test.cpp \
+ RefCounted.cpp \
+ SessionState.cpp logging.cpp \
+ AsyncCompletion.cpp \
+ Url.cpp Uuid.cpp \
+ Shlib.cpp FieldValue.cpp FieldTable.cpp Array.cpp \
+ QueueOptionsTest.cpp \
+ InlineAllocator.cpp \
+ InlineVector.cpp \
+ SequenceSet.cpp \
+ StringUtils.cpp \
+ RangeSet.cpp \
+ AtomicValue.cpp \
+ QueueTest.cpp \
+ AccumulatedAckTest.cpp \
+ DtxWorkRecordTest.cpp \
+ DeliveryRecordTest.cpp \
+ ExchangeTest.cpp \
+ HeadersExchangeTest.cpp \
+ MessageTest.cpp \
+ QueueRegistryTest.cpp \
+ QueuePolicyTest.cpp \
+ QueueFlowLimitTest.cpp \
+ FramingTest.cpp \
+ HeaderTest.cpp \
+ SequenceNumberTest.cpp \
+ TimerTest.cpp \
+ TopicExchangeTest.cpp \
+ TxBufferTest.cpp \
+ TxPublishTest.cpp \
+ MessageBuilderTest.cpp \
+ ConnectionOptions.h \
+ ForkedBroker.h \
+ ForkedBroker.cpp \
+ ManagementTest.cpp \
+ MessageReplayTracker.cpp \
+ ConsoleTest.cpp \
+ QueueEvents.cpp \
+ ProxyTest.cpp \
+ RetryList.cpp \
+ RateFlowcontrolTest.cpp \
+ FrameDecoder.cpp \
+ ReplicationTest.cpp \
+ ClientMessageTest.cpp \
+ PollableCondition.cpp \
+ Variant.cpp \
+ Address.cpp \
+ ClientMessage.cpp \
+ Qmf2.cpp
+
+if HAVE_XML
+unit_test_SOURCES+= XmlClientSessionTest.cpp
+endif
+
+TESTLIBFLAGS = -module -rpath $(abs_builddir)
+
+check_LTLIBRARIES += libshlibtest.la
+libshlibtest_la_LDFLAGS = $(TESTLIBFLAGS)
+libshlibtest_la_SOURCES = shlibtest.cpp
+
+tmodule_LTLIBRARIES += test_store.la
+test_store_la_SOURCES = test_store.cpp
+test_store_la_LIBADD = $(lib_broker)
+test_store_la_LDFLAGS = -module
+
+include cluster.mk
+include sasl.mk
+if SSL
+include ssl.mk
+endif
+
+# Test programs that are installed and therefore built as part of make, not make check
+
+qpidtest_SCRIPTS += qpid-cpp-benchmark install_env.sh
+EXTRA_DIST += qpid-cpp-benchmark install_env.sh
+
+qpidtest_PROGRAMS += receiver
+receiver_SOURCES = \
+ receiver.cpp \
+ TestOptions.h \
+ ConnectionOptions.h
+receiver_LDADD = $(lib_client)
+
+qpidtest_PROGRAMS += sender
+sender_SOURCES = \
+ sender.cpp \
+ TestOptions.h \
+ ConnectionOptions.h \
+ Statistics.cpp
+sender_LDADD = $(lib_messaging)
+
+qpidtest_PROGRAMS += qpid-receive
+qpid_receive_SOURCES = \
+ qpid-receive.cpp \
+ TestOptions.h \
+ ConnectionOptions.h \
+ Statistics.h \
+ Statistics.cpp
+qpid_receive_LDADD = $(lib_messaging)
+
+qpidtest_PROGRAMS += qpid-send
+qpid_send_SOURCES = \
+ qpid-send.cpp \
+ TestOptions.h \
+ ConnectionOptions.h \
+ Statistics.h \
+ Statistics.cpp
+qpid_send_LDADD = $(lib_messaging)
+
+qpidtest_PROGRAMS+=qpid-perftest
+qpid_perftest_SOURCES=qpid-perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h
+qpid_perftest_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_perftest_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-txtest
+qpid_txtest_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_txtest_SOURCES=qpid-txtest.cpp TestOptions.h ConnectionOptions.h
+qpid_txtest_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-latency-test
+qpid_latency_test_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_latency_test_SOURCES=qpid-latency-test.cpp TestOptions.h ConnectionOptions.h
+qpid_latency_test_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-client-test
+qpid_client_test_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_client_test_SOURCES=qpid-client-test.cpp TestOptions.h ConnectionOptions.h
+qpid_client_test_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-topic-listener
+qpid_topic_listener_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_topic_listener_SOURCES=qpid-topic-listener.cpp TestOptions.h ConnectionOptions.h
+qpid_topic_listener_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-topic-publisher
+qpid_topic_publisher_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_topic_publisher_SOURCES=qpid-topic-publisher.cpp TestOptions.h ConnectionOptions.h
+qpid_topic_publisher_LDADD=$(lib_client)
+
+qpidtest_PROGRAMS+=qpid-ping
+qpid_ping_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_ping_SOURCES=qpid-ping.cpp test_tools.h TestOptions.h ConnectionOptions.h
+qpid_ping_LDADD=$(lib_client)
+
+#
+# Other test programs
+#
+
+check_PROGRAMS+=echotest
+echotest_INCLUDES=$(PUBLIC_INCLUDES)
+echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h
+echotest_LDADD=$(lib_client)
+
+check_PROGRAMS+=publish
+publish_INCLUDES=$(PUBLIC_INCLUDES)
+publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h
+publish_LDADD=$(lib_client)
+
+check_PROGRAMS+=consume
+consume_INCLUDES=$(PUBLIC_INCLUDES)
+consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h
+consume_LDADD=$(lib_client)
+
+check_PROGRAMS+=header_test
+header_test_INCLUDES=$(PUBLIC_INCLUDES)
+header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h
+header_test_LDADD=$(lib_client)
+
+check_PROGRAMS+=failover_soak
+failover_soak_INCLUDES=$(PUBLIC_INCLUDES)
+failover_soak_SOURCES=failover_soak.cpp ForkedBroker.h ForkedBroker.cpp
+failover_soak_LDADD=$(lib_client) $(lib_broker)
+
+check_PROGRAMS+=declare_queues
+declare_queues_INCLUDES=$(PUBLIC_INCLUDES)
+declare_queues_SOURCES=declare_queues.cpp
+declare_queues_LDADD=$(lib_client)
+
+check_PROGRAMS+=replaying_sender
+replaying_sender_INCLUDES=$(PUBLIC_INCLUDES)
+replaying_sender_SOURCES=replaying_sender.cpp
+replaying_sender_LDADD=$(lib_client)
+
+check_PROGRAMS+=resuming_receiver
+resuming_receiver_INCLUDES=$(PUBLIC_INCLUDES)
+resuming_receiver_SOURCES=resuming_receiver.cpp
+resuming_receiver_LDADD=$(lib_client)
+
+check_PROGRAMS+=txshift
+txshift_INCLUDES=$(PUBLIC_INCLUDES)
+txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h
+txshift_LDADD=$(lib_client)
+
+check_PROGRAMS+=txjob
+txjob_INCLUDES=$(PUBLIC_INCLUDES)
+txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h
+txjob_LDADD=$(lib_client)
+
+check_PROGRAMS+=PollerTest
+PollerTest_SOURCES=PollerTest.cpp
+PollerTest_LDADD=$(lib_common) $(lib_client) $(SOCKLIBS)
+
+check_PROGRAMS+=DispatcherTest
+DispatcherTest_SOURCES=DispatcherTest.cpp
+DispatcherTest_LDADD=$(lib_common) $(lib_client) $(SOCKLIBS)
+
+check_PROGRAMS+=datagen
+datagen_SOURCES=datagen.cpp
+datagen_LDADD=$(lib_common) $(lib_client)
+
+check_PROGRAMS+=qpid-stream
+qpid_stream_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_stream_SOURCES=qpid-stream.cpp
+qpid_stream_LDADD=$(lib_messaging)
+
+TESTS_ENVIRONMENT = \
+ VALGRIND=$(VALGRIND) \
+ LIBTOOL="$(LIBTOOL)" \
+ QPID_DATA_DIR= \
+ $(srcdir)/run_test
+
+system_tests = qpid-client-test quick_perftest quick_topictest run_header_test quick_txtest
+TESTS += start_broker $(system_tests) python_tests stop_broker run_federation_tests \
+ run_acl_tests run_cli_tests replication_test dynamic_log_level_test \
+ run_queue_flow_limit_tests
+
+EXTRA_DIST += \
+ run_test vg_check \
+ run-unit-tests start_broker python_tests stop_broker \
+ quick_topictest \
+ quick_perftest \
+ quick_txtest \
+ topictest \
+ run_header_test \
+ header_test.py \
+ ssl_test \
+ config.null \
+ ais_check \
+ run_federation_tests \
+ run_cli_tests \
+ run_acl_tests \
+ .valgrind.supp \
+ MessageUtils.h \
+ TestMessageStore.h \
+ TxMocks.h \
+ replication_test \
+ run_perftest \
+ ring_queue_test \
+ run_ring_queue_test \
+ dynamic_log_level_test \
+ qpid-ctrl \
+ CMakeLists.txt \
+ cluster.cmake \
+ windows/DisableWin32ErrorWindows.cpp \
+ background.ps1 \
+ find_prog.ps1 \
+ python_tests.ps1 \
+ quick_topictest.ps1 \
+ run_federation_tests.ps1 \
+ run_header_test.ps1 \
+ run_test.ps1 \
+ start_broker.ps1 \
+ stop_broker.ps1 \
+ topictest.ps1 \
+ run_queue_flow_limit_tests
+
+check_LTLIBRARIES += libdlclose_noop.la
+libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
+libdlclose_noop_la_SOURCES = dlclose_noop.c
+
+CLEANFILES+=valgrind.out *.log *.vglog* dummy_test qpidd.port $(unit_wrappers)
+
+# Longer running stability tests, not run by default check: target.
+# Not run under valgrind, too slow
+
+LONG_TESTS+=start_broker fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test stop_broker \
+ run_failover_soak reliable_replication_test \
+ federated_cluster_test_with_node_failure
+
+EXTRA_DIST+= \
+ fanout_perftest \
+ shared_perftest \
+ multiq_perftest \
+ topic_perftest \
+ run_failover_soak \
+ reliable_replication_test \
+ federated_cluster_test_with_node_failure \
+ sasl_test_setup.sh
+
+check-long:
+ $(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND=
+
+# Things that should be built before the check target runs.
+check-am: python_prep test_env.sh install_env.sh sasl_config
+
+PYTHON_SRC_DIR=$(abs_srcdir)/../../../python
+PYTHON_BLD_DIR=$(abs_builddir)/python
+
+# Generate python client as part of the all-am target so it gets built before tests.
+all-am: python_prep
+
+python_prep:
+ if test -d $(PYTHON_SRC_DIR); \
+ then cd $(PYTHON_SRC_DIR) && python $(PYTHON_SRC_DIR)/setup.py install \
+ --prefix=$(PYTHON_BLD_DIR) --install-lib=$(PYTHON_BLD_DIR) \
+ --install-scripts=$(PYTHON_BLD_DIR)/commands; \
+ else echo "WARNING: python client not built, missing $(PYTHON_SRC_DIR)"; fi
+
+sasl_config: sasl_test_setup.sh
+ sh $(srcdir)/sasl_test_setup.sh
+ touch sasl_config
+
+CLEAN_LOCAL += sasl_config
+
+clean-local:
+ rm -rf $(CLEAN_LOCAL)
+
+include testagent.mk
+include brokermgmt.mk
+
diff --git a/qpid/cpp/src/tests/ManagementTest.cpp b/qpid/cpp/src/tests/ManagementTest.cpp
new file mode 100644
index 0000000000..8944c084c0
--- /dev/null
+++ b/qpid/cpp/src/tests/ManagementTest.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/management/ManagementObject.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/console/ObjectId.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ManagementTestSuite)
+
+using namespace qpid::framing;
+using namespace qpid::management;
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeStream) {
+ std::string text("0-10-4-2500-80000000000()");
+ std::stringstream input(text);
+
+ ObjectId oid(input);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeString) {
+ std::string text("0-10-4-2500-80000000000()");
+
+ ObjectId oid(text);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdEncode) {
+ qpid::types::Variant::Map oidMap;
+
+ ObjectId oid(1, 2, 3, 9999);
+ oid.setV2Key("testkey");
+ oid.setAgentName("myAgent");
+
+ std::stringstream out1;
+ out1 << oid;
+
+ BOOST_CHECK_EQUAL(out1.str(), "1-2-3-myAgent-9999(testkey)");
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdAttach) {
+ AgentAttachment agent;
+ ObjectId oid(&agent, 10, 20);
+ oid.setV2Key("GabbaGabbaHey");
+ oid.setAgentName("MrSmith");
+
+ std::stringstream out1;
+ out1 << oid;
+
+ BOOST_CHECK_EQUAL(out1.str(), "10-20-0-MrSmith-0(GabbaGabbaHey)");
+
+ agent.setBanks(30, 40);
+ std::stringstream out2;
+ out2 << oid;
+
+ BOOST_CHECK_EQUAL(out2.str(), "10-20-30-MrSmith-0(GabbaGabbaHey)");
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdCreate) {
+ ObjectId oid("some-agent-name", "an-object-name");
+
+ BOOST_CHECK_EQUAL(oid.getAgentName(), "some-agent-name");
+ BOOST_CHECK_EQUAL(oid.getV2Key(), "an-object-name");
+}
+
+QPID_AUTO_TEST_CASE(testConsoleObjectId) {
+ qpid::console::ObjectId oid1, oid2;
+
+ oid1.setValue(1, 2);
+ oid2.setValue(3, 4);
+
+ BOOST_CHECK(oid1 < oid2);
+ BOOST_CHECK(oid1 <= oid2);
+ BOOST_CHECK(oid2 > oid1);
+ BOOST_CHECK(oid2 >= oid1);
+ BOOST_CHECK(oid1 != oid2);
+ BOOST_CHECK(oid1 == oid1);
+
+ oid1.setValue(3, 6);
+ oid2.setValue(3, 4);
+
+ BOOST_CHECK(oid1 > oid2);
+ BOOST_CHECK(oid1 >= oid2);
+ BOOST_CHECK(oid2 < oid1);
+ BOOST_CHECK(oid2 <= oid1);
+ BOOST_CHECK(oid1 != oid2);
+
+ oid2.setValue(3, 6);
+ BOOST_CHECK(oid1 == oid2);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageBuilderTest.cpp b/qpid/cpp/src/tests/MessageBuilderTest.cpp
new file mode 100644
index 0000000000..c3d40ed88a
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageBuilderTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageBuilder.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/framing/frame_functors.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/TypeFilter.h"
+#include "unit_test.h"
+#include <list>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+class MockMessageStore : public NullMessageStore
+{
+ enum Op {STAGE=1, APPEND=2};
+
+ uint64_t id;
+ boost::intrusive_ptr<PersistableMessage> expectedMsg;
+ string expectedData;
+ std::list<Op> ops;
+
+ void checkExpectation(Op actual)
+ {
+ BOOST_CHECK_EQUAL(ops.front(), actual);
+ ops.pop_front();
+ }
+
+ public:
+ MockMessageStore() : id(0), expectedMsg(0) {}
+
+ void expectStage(PersistableMessage& msg)
+ {
+ expectedMsg = &msg;
+ ops.push_back(STAGE);
+ }
+
+ void expectAppendContent(PersistableMessage& msg, const string& data)
+ {
+ expectedMsg = &msg;
+ expectedData = data;
+ ops.push_back(APPEND);
+ }
+
+ void stage(const boost::intrusive_ptr<PersistableMessage>& msg)
+ {
+ checkExpectation(STAGE);
+ BOOST_CHECK_EQUAL(expectedMsg, msg);
+ msg->setPersistenceId(++id);
+ }
+
+ void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
+ const string& data)
+ {
+ checkExpectation(APPEND);
+ BOOST_CHECK_EQUAL(boost::static_pointer_cast<const PersistableMessage>(expectedMsg), msg);
+ BOOST_CHECK_EQUAL(expectedData, data);
+ }
+
+ bool expectationsMet()
+ {
+ return ops.empty();
+ }
+
+ //don't treat this store as a null impl
+ bool isNull() const
+ {
+ return false;
+ }
+
+};
+
+QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite)
+
+QPID_AUTO_TEST_CASE(testHeaderOnly)
+{
+ MessageBuilder builder(0);
+ builder.start(SequenceNumber());
+
+ std::string exchange("builder-exchange");
+ std::string key("builder-exchange");
+
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0);
+ header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
+
+ builder.handle(method);
+ builder.handle(header);
+
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK_EQUAL(exchange, builder.getMessage()->getExchangeName());
+ BOOST_CHECK_EQUAL(key, builder.getMessage()->getRoutingKey());
+ BOOST_CHECK(builder.getMessage()->getFrames().isComplete());
+}
+
+QPID_AUTO_TEST_CASE(test1ContentFrame)
+{
+ MessageBuilder builder(0);
+ builder.start(SequenceNumber());
+
+ std::string data("abcdefg");
+ std::string exchange("builder-exchange");
+ std::string key("builder-exchange");
+
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content((AMQContentBody(data)));
+ method.setEof(false);
+ header.setBof(false);
+ header.setEof(false);
+ content.setBof(false);
+
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size());
+ header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
+
+ builder.handle(method);
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK(!builder.getMessage()->getFrames().isComplete());
+
+ builder.handle(header);
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK(!builder.getMessage()->getFrames().isComplete());
+
+ builder.handle(content);
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK(builder.getMessage()->getFrames().isComplete());
+}
+
+QPID_AUTO_TEST_CASE(test2ContentFrames)
+{
+ MessageBuilder builder(0);
+ builder.start(SequenceNumber());
+
+ std::string data1("abcdefg");
+ std::string data2("hijklmn");
+ std::string exchange("builder-exchange");
+ std::string key("builder-exchange");
+
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+ AMQFrame content2((AMQContentBody(data2)));
+ method.setEof(false);
+ header.setBof(false);
+ header.setEof(false);
+ content1.setBof(false);
+ content1.setEof(false);
+ content2.setBof(false);
+
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size());
+ header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
+
+ builder.handle(method);
+ builder.handle(header);
+ builder.handle(content1);
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK(!builder.getMessage()->getFrames().isComplete());
+
+ builder.handle(content2);
+ BOOST_CHECK(builder.getMessage());
+ BOOST_CHECK(builder.getMessage()->getFrames().isComplete());
+}
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageReplayTracker.cpp b/qpid/cpp/src/tests/MessageReplayTracker.cpp
new file mode 100644
index 0000000000..3d79ee53c2
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageReplayTracker.cpp
@@ -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.
+ *
+ */
+#include "unit_test.h"
+#include "BrokerFixture.h"
+#include "qpid/client/MessageReplayTracker.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests)
+
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+class ReplayBufferChecker
+{
+ public:
+
+ ReplayBufferChecker(uint from, uint to) : end(to), i(from) {}
+
+ void operator()(const Message& m)
+ {
+ if (i > end) BOOST_FAIL("Extra message found: " + m.getData());
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i++)).str(), m.getData());
+ }
+ private:
+ const uint end;
+ uint i;
+
+};
+
+QPID_AUTO_TEST_CASE(testReplay)
+{
+ ProxySessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ MessageReplayTracker tracker(10);
+ tracker.init(fix.session);
+ for (uint i = 0; i < 5; i++) {
+ Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ tracker.send(message);
+ }
+ ReplayBufferChecker checker(1, 10);
+ tracker.foreach(checker);
+
+ tracker.replay(fix.session);
+ for (uint j = 0; j < 2; j++) {//each message should have been sent twice
+ for (uint i = 0; i < 5; i++) {
+ Message m;
+ BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ }
+ }
+ Message m;
+ BOOST_CHECK(!fix.subs.get(m, "my-queue"));
+}
+
+QPID_AUTO_TEST_CASE(testCheckCompletion)
+{
+ ProxySessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ MessageReplayTracker tracker(10);
+ tracker.init(fix.session);
+ for (uint i = 0; i < 5; i++) {
+ Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ tracker.send(message);
+ }
+ fix.session.sync();//ensures all messages are complete
+ tracker.checkCompletion();
+ tracker.replay(fix.session);
+ Message received;
+ for (uint i = 0; i < 5; i++) {
+ BOOST_CHECK(fix.subs.get(received, "my-queue"));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData());
+ }
+ BOOST_CHECK(!fix.subs.get(received, "my-queue"));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp
new file mode 100644
index 0000000000..7d67c92b37
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/Message.h"
+#include "qpid/framing/AMQP_HighestVersion.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/alloca.h"
+
+#include "unit_test.h"
+
+#include <iostream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessageTestSuite)
+
+QPID_AUTO_TEST_CASE(testEncodeDecode)
+{
+ string exchange = "MyExchange";
+ string routingKey = "MyRoutingKey";
+ Uuid messageId(true);
+ string data1("abcdefg");
+ string data2("hijklmn");
+
+ boost::intrusive_ptr<Message> msg(new Message());
+
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+ AMQFrame content2((AMQContentBody(data2)));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ msg->getFrames().append(content1);
+ msg->getFrames().append(content2);
+
+ MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ mProps->setContentLength(data1.size() + data2.size());
+ mProps->setMessageId(messageId);
+ FieldTable applicationHeaders;
+ applicationHeaders.setString("abc", "xyz");
+ mProps->setApplicationHeaders(applicationHeaders);
+ DeliveryProperties* dProps = msg->getFrames().getHeaders()->get<DeliveryProperties>(true);
+ dProps->setRoutingKey(routingKey);
+ dProps->setDeliveryMode(PERSISTENT);
+ BOOST_CHECK(msg->isPersistent());
+
+ char* buff = static_cast<char*>(::alloca(msg->encodedSize()));
+ Buffer wbuffer(buff, msg->encodedSize());
+ msg->encode(wbuffer);
+
+ Buffer rbuffer(buff, msg->encodedSize());
+ msg = new Message();
+ msg->decodeHeader(rbuffer);
+ msg->decodeContent(rbuffer);
+ BOOST_CHECK_EQUAL(exchange, msg->getExchangeName());
+ BOOST_CHECK_EQUAL(routingKey, msg->getRoutingKey());
+ BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->contentSize());
+ BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->getProperties<MessageProperties>()->getContentLength());
+ BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId());
+ BOOST_CHECK_EQUAL(string("xyz"), msg->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("abc"));
+ BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode());
+ BOOST_CHECK(msg->isPersistent());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h
new file mode 100644
index 0000000000..a1b140d484
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageUtils.h
@@ -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.
+ *
+ */
+
+#include "qpid/broker/Message.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/Uuid.h"
+
+using namespace qpid;
+using namespace broker;
+using namespace framing;
+
+namespace qpid {
+namespace tests {
+
+struct MessageUtils
+{
+ static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="",
+ const bool durable = false, const Uuid& messageId=Uuid(true),
+ uint64_t contentSize = 0)
+ {
+ boost::intrusive_ptr<broker::Message> msg(new broker::Message());
+
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(contentSize);
+ props->setMessageId(messageId);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (durable)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(2);
+ return msg;
+ }
+
+ static void addContent(boost::intrusive_ptr<Message> msg, const string& data)
+ {
+ AMQFrame content((AMQContentBody(data)));
+ msg->getFrames().append(content);
+ }
+};
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessagingFixture.h b/qpid/cpp/src/tests/MessagingFixture.h
new file mode 100644
index 0000000000..2312a87e9d
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingFixture.h
@@ -0,0 +1,345 @@
+#ifndef TESTS_MESSAGINGFIXTURE_H
+#define TESTS_MESSAGINGFIXTURE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "BrokerFixture.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/types/Variant.h"
+
+namespace qpid {
+namespace tests {
+
+using qpid::types::Variant;
+
+struct BrokerAdmin
+{
+ qpid::client::Connection connection;
+ qpid::client::Session session;
+
+ BrokerAdmin(uint16_t port)
+ {
+ connection.open("localhost", port);
+ session = connection.newSession();
+ }
+
+ void createQueue(const std::string& name)
+ {
+ session.queueDeclare(qpid::client::arg::queue=name);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ session.queueDelete(qpid::client::arg::queue=name);
+ }
+
+ void createExchange(const std::string& name, const std::string& type)
+ {
+ session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ session.exchangeDelete(qpid::client::arg::exchange=name);
+ }
+
+ bool checkQueueExists(const std::string& name)
+ {
+ return session.queueQuery(name).getQueue() == name;
+ }
+
+ bool checkExchangeExists(const std::string& name, std::string& type)
+ {
+ qpid::framing::ExchangeQueryResult result = session.exchangeQuery(name);
+ type = result.getType();
+ return !result.getNotFound();
+ }
+
+ void send(qpid::client::Message& message, const std::string& exchange=std::string())
+ {
+ session.messageTransfer(qpid::client::arg::destination=exchange, qpid::client::arg::content=message);
+ }
+
+ ~BrokerAdmin()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct MessagingFixture : public BrokerFixture
+{
+ messaging::Connection connection;
+ messaging::Session session;
+ BrokerAdmin admin;
+
+ MessagingFixture(Broker::Options opts = Broker::Options(), bool mgmtEnabled=false) :
+ BrokerFixture(opts, mgmtEnabled),
+ connection(open(broker->getPort(Broker::TCP_TRANSPORT))),
+ session(connection.createSession()),
+ admin(broker->getPort(Broker::TCP_TRANSPORT))
+ {
+ }
+
+ static messaging::Connection open(uint16_t port)
+ {
+ messaging::Connection connection(
+ (boost::format("amqp:tcp:localhost:%1%") % (port)).str());
+ connection.open();
+ return connection;
+ }
+
+ /** Open a connection to the broker. */
+ qpid::messaging::Connection newConnection()
+ {
+ qpid::messaging::Connection connection(
+ (boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(qpid::broker::Broker::TCP_TRANSPORT))).str());
+ return connection;
+ }
+
+ void ping(const qpid::messaging::Address& address)
+ {
+ messaging::Receiver r = session.createReceiver(address);
+ messaging::Sender s = session.createSender(address);
+ messaging::Message out(framing::Uuid(true).str());
+ s.send(out);
+ messaging::Message in;
+ BOOST_CHECK(r.fetch(in, 5*messaging::Duration::SECOND));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ r.close();
+ s.close();
+ }
+
+ ~MessagingFixture()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct QueueFixture : MessagingFixture
+{
+ std::string queue;
+
+ QueueFixture(const std::string& name = "test-queue") : queue(name)
+ {
+ admin.createQueue(queue);
+ }
+
+ ~QueueFixture()
+ {
+ admin.deleteQueue(queue);
+ }
+
+};
+
+struct TopicFixture : MessagingFixture
+{
+ std::string topic;
+
+ TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name)
+ {
+ admin.createExchange(topic, type);
+ }
+
+ ~TopicFixture()
+ {
+ admin.deleteExchange(topic);
+ }
+
+};
+
+struct MultiQueueFixture : MessagingFixture
+{
+ typedef std::vector<std::string>::const_iterator const_iterator;
+ std::vector<std::string> queues;
+
+ MultiQueueFixture(const std::vector<std::string>& names = boost::assign::list_of<std::string>("q1")("q2")("q3")) : queues(names)
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.createQueue(*i);
+ }
+ }
+
+ ~MultiQueueFixture()
+ {
+ connection.close();
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.deleteQueue(*i);
+ }
+ }
+
+};
+
+inline std::vector<std::string> fetch(messaging::Receiver& receiver, int count, messaging::Duration timeout=messaging::Duration::SECOND*5)
+{
+ std::vector<std::string> data;
+ messaging::Message message;
+ for (int i = 0; i < count && receiver.fetch(message, timeout); i++) {
+ data.push_back(message.getContent());
+ }
+ return data;
+}
+
+
+inline void send(messaging::Sender& sender, uint count = 1, uint start = 1,
+ const std::string& base = "Message")
+{
+ for (uint i = start; i < start + count; ++i) {
+ sender.send(messaging::Message((boost::format("%1%_%2%") % base % i).str()));
+ }
+}
+
+inline void receive(messaging::Receiver& receiver, uint count = 1, uint start = 1,
+ const std::string& base = "Message",
+ messaging::Duration timeout=messaging::Duration::SECOND*5)
+{
+ for (uint i = start; i < start + count; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch(timeout).getContent(), (boost::format("%1%_%2%") % base % i).str());
+ }
+}
+
+
+class MethodInvoker
+{
+ public:
+ MethodInvoker(messaging::Session& session) : replyTo("#; {create:always, node:{x-declare:{auto-delete:true}}}"),
+ sender(session.createSender("qmf.default.direct/broker")),
+ receiver(session.createReceiver(replyTo)) {}
+
+ void createExchange(const std::string& name, const std::string& type, bool durable=false)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ params["properties"] = Variant::Map();
+ params["properties"].asMap()["exchange-type"] = type;
+ params["properties"].asMap()["durable"] = durable;
+ methodRequest("create", params);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ methodRequest("delete", params);
+ }
+
+ void createQueue(const std::string& name, bool durable=false, bool autodelete=false,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ params["properties"] = options;
+ params["properties"].asMap()["durable"] = durable;
+ params["properties"].asMap()["auto-delete"] = autodelete;
+ methodRequest("create", params);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ methodRequest("delete", params);
+ }
+
+ void bind(const std::string& exchange, const std::string& queue, const std::string& key,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ params["properties"] = options;
+ methodRequest("create", params);
+ }
+
+ void unbind(const std::string& exchange, const std::string& queue, const std::string& key)
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ methodRequest("delete", params);
+ }
+
+ void methodRequest(const std::string& method, const Variant::Map& inParams, Variant::Map* outParams = 0)
+ {
+ Variant::Map content;
+ Variant::Map objectId;
+ objectId["_object_name"] = "org.apache.qpid.broker:broker:amqp-broker";
+ content["_object_id"] = objectId;
+ content["_method_name"] = method;
+ content["_arguments"] = inParams;
+
+ messaging::Message request;
+ request.setReplyTo(replyTo);
+ request.getProperties()["x-amqp-0-10.app-id"] = "qmf2";
+ request.getProperties()["qmf.opcode"] = "_method_request";
+ encode(content, request);
+
+ sender.send(request);
+
+ messaging::Message response;
+ if (receiver.fetch(response, messaging::Duration::SECOND*5)) {
+ if (response.getProperties()["x-amqp-0-10.app-id"] == "qmf2") {
+ std::string opcode = response.getProperties()["qmf.opcode"];
+ if (opcode == "_method_response") {
+ if (outParams) {
+ Variant::Map m;
+ decode(response, m);
+ *outParams = m["_arguments"].asMap();
+ }
+ } else if (opcode == "_exception") {
+ Variant::Map m;
+ decode(response, m);
+ throw Exception(QPID_MSG("Error: " << m["_values"]));
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, unexpected opcode: " << opcode));
+ }
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, not a qmfv2 message: app-id="
+ << response.getProperties()["x-amqp-0-10.app-id"]));
+ }
+ } else {
+ throw Exception(QPID_MSG("No response received"));
+ }
+ }
+ private:
+ messaging::Address replyTo;
+ messaging::Sender sender;
+ messaging::Receiver receiver;
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_MESSAGINGFIXTURE_H*/
diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp
new file mode 100644
index 0000000000..6aa4c63ed7
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp
@@ -0,0 +1,997 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "MessagingFixture.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/ExchangeQueryResult.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Time.h"
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <string>
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessagingSessionTests)
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace qpid;
+using qpid::broker::Broker;
+using qpid::framing::Uuid;
+
+
+QPID_AUTO_TEST_CASE(testSimpleSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ out.setSubject("test-subject");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getSubject(), out.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testSyncSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.acknowledge(true);
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testSendReceiveHeaders)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ for (uint i = 0; i < 10; ++i) {
+ out.getProperties()["a"] = i;
+ out.setProperty("b", i + 100);
+ sender.send(out);
+ }
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in;
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND * 5));
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getProperties()["a"].asUint32(), i);
+ BOOST_CHECK_EQUAL(in.getProperties()["b"].asUint32(), i + 100);
+ fix.session.acknowledge();
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSenderError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::messaging::NotFound);
+ fix.session = fix.connection.createSession();
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress; {create:receiver}"),
+ qpid::messaging::NotFound);
+}
+
+QPID_AUTO_TEST_CASE(testReceiverError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::messaging::NotFound);
+ fix.session = fix.connection.createSession();
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress; {create:sender}"),
+ qpid::messaging::NotFound);
+}
+
+QPID_AUTO_TEST_CASE(testSimpleTopic)
+{
+ TopicFixture fix;
+
+ Sender sender = fix.session.createSender(fix.topic);
+ Message msg("one");
+ sender.send(msg);
+ Receiver sub1 = fix.session.createReceiver(fix.topic);
+ sub1.setCapacity(10u);
+ msg.setContent("two");
+ sender.send(msg);
+ Receiver sub2 = fix.session.createReceiver(fix.topic);
+ sub2.setCapacity(10u);
+ msg.setContent("three");
+ sender.send(msg);
+ Receiver sub3 = fix.session.createReceiver(fix.topic);
+ sub3.setCapacity(10u);
+ msg.setContent("four");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of<std::string>("three")("four"));
+ sub2.close();
+
+ msg.setContent("five");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of<std::string>("two")("three")("four")("five"));
+ BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of<std::string>("four")("five"));
+ Message in;
+ BOOST_CHECK(!sub2.fetch(in, Duration::IMMEDIATE));//TODO: or should this raise an error?
+
+
+ //TODO: check pending messages...
+}
+
+QPID_AUTO_TEST_CASE(testNextReceiver)
+{
+ MultiQueueFixture fix;
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Receiver r = fix.session.createReceiver(fix.queues[i]);
+ r.setCapacity(10u);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Sender s = fix.session.createSender(fix.queues[i]);
+ Message msg((boost::format("Message_%1%") % (i+1)).str());
+ s.send(msg);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Message msg;
+ BOOST_CHECK(fix.session.nextReceiver().fetch(msg, Duration::SECOND));
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testMapMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::Map content;
+ content["abc"] = "def";
+ content["pi"] = 3.14f;
+ Variant utf8("A utf 8 string");
+ utf8.setEncoding("utf8");
+ content["utf8"] = utf8;
+ Variant utf16("\x00\x61\x00\x62\x00\x63");
+ utf16.setEncoding("utf16");
+ content["utf16"] = utf16;
+ encode(content, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::Map view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f);
+ BOOST_CHECK_EQUAL(view["utf8"].asString(), utf8.asString());
+ BOOST_CHECK_EQUAL(view["utf8"].getEncoding(), utf8.getEncoding());
+ BOOST_CHECK_EQUAL(view["utf16"].asString(), utf16.asString());
+ BOOST_CHECK_EQUAL(view["utf16"].getEncoding(), utf16.getEncoding());
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testMapMessageWithInitial)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::Map imap;
+ imap["abc"] = "def";
+ imap["pi"] = 3.14f;
+ encode(imap, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::Map view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::List content;
+ content.push_back(Variant("abc"));
+ content.push_back(Variant(1234));
+ content.push_back(Variant("def"));
+ content.push_back(Variant(56.789));
+ encode(content, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::List view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view.size(), content.size());
+ BOOST_CHECK_EQUAL(view.front().asString(), "abc");
+ BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789);
+
+ Variant::List::const_iterator i = view.begin();
+ BOOST_CHECK(i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "abc");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asInt64(), 1234);
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "def");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asDouble(), 56.789);
+ BOOST_CHECK(++i == view.end());
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessageWithInitial)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::List ilist;
+ ilist.push_back(Variant("abc"));
+ ilist.push_back(Variant(1234));
+ ilist.push_back(Variant("def"));
+ ilist.push_back(Variant(56.789));
+ encode(ilist, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::List view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view.size(), ilist.size());
+ BOOST_CHECK_EQUAL(view.front().asString(), "abc");
+ BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789);
+
+ Variant::List::const_iterator i = view.begin();
+ BOOST_CHECK(i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "abc");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asInt64(), 1234);
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "def");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asDouble(), 56.789);
+ BOOST_CHECK(++i == view.end());
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testReject)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message m1("reject-me");
+ sender.send(m1);
+ Message m2("accept-me");
+ sender.send(m2);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), m1.getContent());
+ fix.session.reject(in);
+ in = receiver.fetch(5 * Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), m2.getContent());
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testAvailable)
+{
+ MultiQueueFixture fix;
+
+ Receiver r1 = fix.session.createReceiver(fix.queues[0]);
+ r1.setCapacity(100);
+
+ Receiver r2 = fix.session.createReceiver(fix.queues[1]);
+ r2.setCapacity(100);
+
+ Sender s1 = fix.session.createSender(fix.queues[0]);
+ Sender s2 = fix.session.createSender(fix.queues[1]);
+
+ for (uint i = 0; i < 10; ++i) {
+ s1.send(Message((boost::format("A_%1%") % (i+1)).str()));
+ }
+ for (uint i = 0; i < 5; ++i) {
+ s2.send(Message((boost::format("B_%1%") % (i+1)).str()));
+ }
+ qpid::sys::sleep(1);//is there any avoid an arbitrary sleep while waiting for messages to be dispatched?
+ for (uint i = 0; i < 5; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.getReceivable(), 15u - 2*i);
+ BOOST_CHECK_EQUAL(r1.getAvailable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ BOOST_CHECK_EQUAL(r2.getAvailable(), 5u - i);
+ BOOST_CHECK_EQUAL(r2.fetch().getContent(), (boost::format("B_%1%") % (i+1)).str());
+ fix.session.acknowledge();
+ }
+ for (uint i = 5; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.getReceivable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.getAvailable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testUnsettledAcks)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch().getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 0u);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testUnsettledSend)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ //Note: this test relies on 'inside knowledge' of the sender
+ //implementation and the fact that the simple test case makes it
+ //possible to predict when completion information will be sent to
+ //the client. TODO: is there a better way of testing this?
+ BOOST_CHECK_EQUAL(sender.getUnsettled(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(sender.getUnsettled(), 0u);
+
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ receive(receiver, 10);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testBrowse)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ Receiver browser1 = fix.session.createReceiver(fix.queue + "; {mode:browse}");
+ receive(browser1, 10);
+ Receiver browser2 = fix.session.createReceiver(fix.queue + "; {mode:browse}");
+ receive(browser2, 10);
+ Receiver consumer = fix.session.createReceiver(fix.queue);
+ receive(consumer, 10);
+ fix.session.acknowledge();
+}
+
+struct QueueCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+
+ QueueCreatePolicyFixture(const std::string& a) : address(a) {}
+
+ void test()
+ {
+ ping(address);
+ BOOST_CHECK(admin.checkQueueExists(address.getName()));
+ }
+
+ ~QueueCreatePolicyFixture()
+ {
+ admin.deleteQueue(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueAlways)
+{
+ QueueCreatePolicyFixture fix("#; {create:always, node:{type:queue}}");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueReceiver)
+{
+ QueueCreatePolicyFixture fix("#; {create:receiver, node:{type:queue}}");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.close();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueSender)
+{
+ QueueCreatePolicyFixture fix("#; {create:sender, node:{type:queue}}");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.close();
+}
+
+struct ExchangeCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+ const std::string exchangeType;
+
+ ExchangeCreatePolicyFixture(const std::string& a, const std::string& t) :
+ address(a), exchangeType(t) {}
+
+ void test()
+ {
+ ping(address);
+ std::string actualType;
+ BOOST_CHECK(admin.checkExchangeExists(address.getName(), actualType));
+ BOOST_CHECK_EQUAL(exchangeType, actualType);
+ }
+
+ ~ExchangeCreatePolicyFixture()
+ {
+ admin.deleteExchange(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopic)
+{
+ ExchangeCreatePolicyFixture fix("#; {create:always, node:{type:topic}}",
+ "topic");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicReceiverFanout)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:receiver, node:{type:topic, x-declare:{type:fanout}}}", "fanout");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.close();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicSenderDirect)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:sender, node:{type:topic, x-declare:{type:direct}}}", "direct");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.close();
+}
+
+struct DeletePolicyFixture : public MessagingFixture
+{
+ enum Mode {RECEIVER, SENDER, ALWAYS, NEVER};
+
+ std::string getPolicy(Mode mode)
+ {
+ switch (mode) {
+ case SENDER:
+ return "{delete:sender}";
+ case RECEIVER:
+ return "{delete:receiver}";
+ case ALWAYS:
+ return "{delete:always}";
+ case NEVER:
+ return "{delete:never}";
+ }
+ return "";
+ }
+
+ void testAll()
+ {
+ test(RECEIVER);
+ test(SENDER);
+ test(ALWAYS);
+ test(NEVER);
+ }
+
+ virtual ~DeletePolicyFixture() {}
+ virtual void create(const qpid::messaging::Address&) = 0;
+ virtual void destroy(const qpid::messaging::Address&) = 0;
+ virtual bool exists(const qpid::messaging::Address&) = 0;
+
+ void test(Mode mode)
+ {
+ qpid::messaging::Address address("#; " + getPolicy(mode));
+ create(address);
+
+ Sender s = session.createSender(address);
+ Receiver r = session.createReceiver(address);
+ switch (mode) {
+ case RECEIVER:
+ s.close();
+ BOOST_CHECK(exists(address));
+ r.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case SENDER:
+ r.close();
+ BOOST_CHECK(exists(address));
+ s.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case ALWAYS:
+ s.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case NEVER:
+ r.close();
+ BOOST_CHECK(exists(address));
+ s.close();
+ BOOST_CHECK(exists(address));
+ destroy(address);
+ }
+ }
+};
+
+struct QueueDeletePolicyFixture : DeletePolicyFixture
+{
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createQueue(address.getName());
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteQueue(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ return admin.checkQueueExists(address.getName());
+ }
+};
+
+struct ExchangeDeletePolicyFixture : DeletePolicyFixture
+{
+ const std::string exchangeType;
+ ExchangeDeletePolicyFixture(const std::string type = "topic") : exchangeType(type) {}
+
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createExchange(address.getName(), exchangeType);
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteExchange(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ std::string actualType;
+ return admin.checkExchangeExists(address.getName(), actualType) && actualType == exchangeType;
+ }
+};
+
+QPID_AUTO_TEST_CASE(testDeletePolicyQueue)
+{
+ QueueDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testDeletePolicyExchange)
+{
+ ExchangeDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testAssertPolicyQueue)
+{
+ MessagingFixture fix;
+ std::string a1 = "q; {create:always, assert:always, node:{type:queue, durable:false, x-declare:{arguments:{qpid.max-count:100}}}}";
+ Sender s1 = fix.session.createSender(a1);
+ s1.close();
+ Receiver r1 = fix.session.createReceiver(a1);
+ r1.close();
+
+ std::string a2 = "q; {assert:receiver, node:{durable:true, x-declare:{arguments:{qpid.max-count:100}}}}";
+ Sender s2 = fix.session.createSender(a2);
+ s2.close();
+ BOOST_CHECK_THROW(fix.session.createReceiver(a2), qpid::messaging::AssertionFailed);
+
+ std::string a3 = "q; {assert:sender, node:{x-declare:{arguments:{qpid.max-count:99}}}}";
+ BOOST_CHECK_THROW(fix.session.createSender(a3), qpid::messaging::AssertionFailed);
+ Receiver r3 = fix.session.createReceiver(a3);
+ r3.close();
+
+ fix.admin.deleteQueue("q");
+}
+
+QPID_AUTO_TEST_CASE(testGetSender)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createSender(fix.queue).getName();
+ Sender sender = fix.session.getSender(name);
+ BOOST_CHECK_EQUAL(name, sender.getName());
+ Message out(Uuid(true).str());
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(fix.session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getSender("UnknownSender"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetReceiver)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createReceiver(fix.queue).getName();
+ Receiver receiver = fix.session.getReceiver(name);
+ BOOST_CHECK_EQUAL(name, receiver.getName());
+ Message out(Uuid(true).str());
+ fix.session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getReceiver("UnknownReceiver"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetSessionFromConnection)
+{
+ QueueFixture fix;
+ fix.connection.createSession("my-session");
+ Session session = fix.connection.getSession("my-session");
+ Message out(Uuid(true).str());
+ session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.connection.getSession("UnknownSession"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetConnectionFromSession)
+{
+ QueueFixture fix;
+ Message out(Uuid(true).str());
+ Sender sender = fix.session.createSender(fix.queue);
+ sender.send(out);
+ Message in;
+ sender.getSession().getConnection().createSession("incoming");
+ BOOST_CHECK(fix.connection.getSession("incoming").createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testTx)
+{
+ QueueFixture fix;
+ Session ssn1 = fix.connection.createTransactionalSession();
+ Session ssn2 = fix.connection.createTransactionalSession();
+ Sender sender1 = ssn1.createSender(fix.queue);
+ Sender sender2 = ssn2.createSender(fix.queue);
+ Receiver receiver1 = ssn1.createReceiver(fix.queue);
+ Receiver receiver2 = ssn2.createReceiver(fix.queue);
+ Message in;
+
+ send(sender1, 5, 1, "A");
+ send(sender2, 5, 1, "B");
+ ssn2.commit();
+ receive(receiver1, 5, 1, "B");//(only those from sender2 should be received)
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn1.rollback();
+ receive(receiver2, 5, 1, "B");
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn2.rollback();
+ receive(receiver1, 5, 1, "B");
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn1.commit();
+ //check neither receiver gets any more messages:
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testRelease)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message m1 = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.release(m1);
+ Message m2 = receiver.fetch(Duration::SECOND * 1);
+ BOOST_CHECK_EQUAL(m1.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(m1.getContent(), m2.getContent());
+ fix.session.acknowledge(true);
+}
+
+QPID_AUTO_TEST_CASE(testOptionVerification)
+{
+ MessagingFixture fix;
+ fix.session.createReceiver("my-queue; {create: always, assert: always, delete: always, node: {type: queue, durable: false, x-declare: {arguments: {a: b}}, x-bindings: [{exchange: amq.fanout}]}, link: {name: abc, durable: false, reliability: exactly-once, x-subscribe: {arguments:{a:b}}, x-bindings:[{exchange: amq.fanout}]}, mode: browse}");
+ BOOST_CHECK_THROW(fix.session.createReceiver("my-queue; {invalid-option:blah}"), qpid::messaging::AddressError);
+}
+
+QPID_AUTO_TEST_CASE(testReceiveSpecialProperties)
+{
+ QueueFixture fix;
+
+ qpid::client::Message out;
+ out.getDeliveryProperties().setRoutingKey(fix.queue);
+ out.getMessageProperties().setAppId("my-app-id");
+ out.getMessageProperties().setMessageId(qpid::framing::Uuid(true));
+ out.getMessageProperties().setContentEncoding("my-content-encoding");
+ fix.admin.send(out);
+
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.routing-key"].asString(), out.getDeliveryProperties().getRoutingKey());
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.app-id"].asString(), out.getMessageProperties().getAppId());
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.content-encoding"].asString(), out.getMessageProperties().getContentEncoding());
+ BOOST_CHECK_EQUAL(in.getMessageId(), out.getMessageProperties().getMessageId().str());
+ fix.session.acknowledge(true);
+}
+
+QPID_AUTO_TEST_CASE(testSendSpecialProperties)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ std::string appId = "my-app-id";
+ std::string contentEncoding = "my-content-encoding";
+ out.getProperties()["x-amqp-0-10.app-id"] = appId;
+ out.getProperties()["x-amqp-0-10.content-encoding"] = contentEncoding;
+ out.setMessageId(qpid::framing::Uuid(true).str());
+ sender.send(out, true);
+
+ qpid::client::LocalQueue q;
+ qpid::client::SubscriptionManager subs(fix.admin.session);
+ qpid::client::Subscription s = subs.subscribe(q, fix.queue);
+ qpid::client::Message in = q.get();
+ s.cancel();
+ fix.admin.session.sync();
+
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getAppId(), appId);
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getContentEncoding(), contentEncoding);
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getMessageId().str(), out.getMessageId());
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveSubscriber)
+{
+ QueueFixture fix;
+ std::string address = (boost::format("%1%; { link: { x-subscribe : { exclusive:true } } }") % fix.queue).str();
+ Receiver receiver = fix.session.createReceiver(address);
+ ScopedSuppressLogging sl;
+ try {
+ fix.session.createReceiver(address);
+ fix.session.sync();
+ BOOST_FAIL("Expected exception.");
+ } catch (const MessagingException& /*e*/) {}
+}
+
+
+QPID_AUTO_TEST_CASE(testExclusiveQueueSubscriberAndBrowser)
+{
+ MessagingFixture fix;
+
+ std::string address = "exclusive-queue; { create: receiver, node : { x-declare : { auto-delete: true, exclusive: true } } }";
+ std::string browseAddress = "exclusive-queue; { mode: browse }";
+
+ Receiver receiver = fix.session.createReceiver(address);
+ fix.session.sync();
+
+ Connection c2 = fix.newConnection();
+ c2.open();
+ Session s2 = c2.createSession();
+
+ BOOST_CHECK_NO_THROW(Receiver browser = s2.createReceiver(browseAddress));
+ c2.close();
+}
+
+
+QPID_AUTO_TEST_CASE(testDeleteQueueWithUnackedMessages)
+{
+ MessagingFixture fix;
+ const uint capacity = 5;
+
+ Sender sender = fix.session.createSender("test.ex;{create:always,node:{type:topic}}");
+ Receiver receiver2 = fix.session.createReceiver("alternate.ex;{create:always,node:{type:topic}}");
+ Receiver receiver1 = fix.session.createReceiver("test.q;{create:always, delete:always,node:{type:queue, x-declare:{alternate-exchange:alternate.ex}},link:{x-bindings:[{exchange:test.ex,queue:test.q,key:#}]}}");
+
+ receiver1.setCapacity(capacity);
+ receiver2.setCapacity(capacity*2);
+
+ Message out("test-message");
+ for (uint i = 0; i < capacity*2; ++i) {
+ sender.send(out);
+ }
+
+ receiver1.close();
+
+ // Make sure all pending messages were sent to the alternate
+ // exchange when the queue was deleted.
+ Message in;
+ for (uint i = 0; i < capacity*2; ++i) {
+ in = receiver2.fetch(Duration::SECOND * 5);
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testAuthenticatedUsername)
+{
+ MessagingFixture fix;
+ Connection connection = fix.newConnection();
+ connection.setOption("sasl-mechanism", "PLAIN");
+ connection.setOption("username", "test-user");
+ connection.setOption("password", "ignored");
+ connection.open();
+ BOOST_CHECK_EQUAL(connection.getAuthenticatedUsername(), std::string("test-user"));
+}
+
+QPID_AUTO_TEST_CASE(testExceptionOnClosedConnection)
+{
+ MessagingFixture fix;
+ fix.connection.close();
+ BOOST_CHECK_THROW(fix.connection.createSession(), MessagingException);
+ Connection connection("blah");
+ BOOST_CHECK_THROW(connection.createSession(), MessagingException);
+}
+
+QPID_AUTO_TEST_CASE(testAcknowledge)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ const uint count(20);
+ for (uint i = 0; i < count; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Session other = fix.connection.createSession();
+ Receiver receiver = other.createReceiver(fix.queue);
+ std::vector<Message> messages;
+ for (uint i = 0; i < count; ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ messages.push_back(msg);
+ }
+ const uint batch(10); //acknowledge first 10 messages only
+ for (uint i = 0; i < batch; ++i) {
+ other.acknowledge(messages[i]);
+ }
+ messages.clear();
+ other.sync();
+ other.close();
+
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ for (uint i = 0; i < (count-batch); ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1+batch)).str());
+ if (i % 2) other.acknowledge(msg); //acknowledge every other message
+ }
+ other.sync();
+ other.close();
+
+ //check unacknowledged messages are still enqueued
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ for (uint i = 0; i < ((count-batch)/2); ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % ((i*2)+1+batch)).str());
+ }
+ other.acknowledge();//acknowledge all messages
+ other.sync();
+ other.close();
+
+ Message m;
+ //check queue is empty
+ BOOST_CHECK(!fix.session.createReceiver(fix.queue).fetch(m, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testQmfCreateAndDelete)
+{
+ MessagingFixture fix(Broker::Options(), true/*enable management*/);
+ MethodInvoker control(fix.session);
+ control.createQueue("my-queue");
+ control.createExchange("my-exchange", "topic");
+ control.bind("my-exchange", "my-queue", "subject1");
+
+ Sender sender = fix.session.createSender("my-exchange");
+ Receiver receiver = fix.session.createReceiver("my-queue");
+ Message out;
+ out.setSubject("subject1");
+ out.setContent("one");
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ control.unbind("my-exchange", "my-queue", "subject1");
+ control.bind("my-exchange", "my-queue", "subject2");
+
+ out.setContent("two");
+ sender.send(out);//should be dropped
+
+ out.setSubject("subject2");
+ out.setContent("three");
+ sender.send(out);//should not be dropped
+
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK(!receiver.fetch(in, Duration::IMMEDIATE));
+ sender.close();
+ receiver.close();
+
+ control.deleteExchange("my-exchange");
+ messaging::Session other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createSender("my-exchange"), qpid::messaging::NotFound);
+ }
+ control.deleteQueue("my-queue");
+ other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createReceiver("my-queue"), qpid::messaging::NotFound);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRejectAndCredit)
+{
+ //Ensure credit is restored on completing rejected messages
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+
+ const uint count(10);
+ receiver.setCapacity(count);
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Message in;
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ fix.session.reject(in);
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+1)).str());
+ break;
+ }
+ }
+ //send another batch of messages
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+count)).str()));
+ }
+
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+count)).str());
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+count)).str());
+ break;
+ }
+ }
+ fix.session.acknowledge();
+ receiver.close();
+ sender.close();
+}
+
+QPID_AUTO_TEST_CASE(testTtlForever)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("I want to live forever!");
+ out.setTtl(Duration::FOREVER);
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK(in.getTtl() == Duration::FOREVER);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessagingThreadTests.cpp b/qpid/cpp/src/tests/MessagingThreadTests.cpp
new file mode 100644
index 0000000000..48264735b1
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingThreadTests.cpp
@@ -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.
+ *
+ */
+#include "MessagingFixture.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace tests {
+QPID_AUTO_TEST_SUITE(MessagingThreadTests)
+
+using namespace messaging;
+using namespace boost::assign;
+using namespace std;
+
+struct ReceiveThread : public sys::Runnable {
+ Receiver receiver;
+ vector<string> received;
+ string error;
+
+ ReceiveThread(Receiver s) : receiver(s) {}
+ void run() {
+ try {
+ while(true) {
+ Message m = receiver.fetch(Duration::SECOND*5);
+ if (m.getContent() == "END") break;
+ received.push_back(m.getContent());
+ }
+ } catch (const NoMessageAvailable& e) {
+ // Indicates that fetch timed out OR receiver was closed by other thread.
+ if (!receiver.isClosed()) // timeout
+ error = e.what();
+ } catch (const std::exception& e) {
+ error = e.what();
+ }
+ }
+};
+
+struct NextReceiverThread : public sys::Runnable {
+ Session session;
+ vector<string> received;
+ string error;
+
+ NextReceiverThread(Session s) : session(s) {}
+ void run() {
+ try {
+ while(true) {
+ Message m = session.nextReceiver(Duration::SECOND*5).fetch();
+ if (m.getContent() == "END") break;
+ received.push_back(m.getContent());
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ }
+ }
+};
+
+
+QPID_AUTO_TEST_CASE(testConcurrentSendReceive) {
+ MessagingFixture fix;
+ Sender s = fix.session.createSender("concurrent;{create:always}");
+ Receiver r = fix.session.createReceiver("concurrent;{create:always,link:{reliability:unreliable}}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ const size_t COUNT=100;
+ for (size_t i = 0; i < COUNT; ++i) {
+ s.send(Message());
+ }
+ s.send(Message("END"));
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+ BOOST_CHECK_EQUAL(COUNT, rt.received.size());
+}
+
+QPID_AUTO_TEST_CASE(testCloseBusyReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("closeReceiver;{create:always}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ r.close();
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+
+ // Fetching on closed receiver should fail.
+ Message m;
+ BOOST_CHECK(!r.fetch(m, Duration(0)));
+ BOOST_CHECK_THROW(r.fetch(Duration(0)), NoMessageAvailable);
+}
+
+QPID_AUTO_TEST_CASE(testCloseSessionBusyReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("closeSession;{create:always}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ fix.session.close();
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+
+ // Fetching on closed receiver should fail.
+ Message m;
+ BOOST_CHECK(!r.fetch(m, Duration(0)));
+ BOOST_CHECK_THROW(r.fetch(Duration(0)), NoMessageAvailable);
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentSendNextReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("concurrent;{create:always,link:{reliability:unreliable}}");
+ const size_t COUNT=100;
+ r.setCapacity(COUNT);
+ NextReceiverThread rt(fix.session);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ Sender s = fix.session.createSender("concurrent;{create:always}");
+ for (size_t i = 0; i < COUNT; ++i) {
+ s.send(Message());
+ }
+ s.send(Message("END"));
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+ BOOST_CHECK_EQUAL(COUNT, rt.received.size());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/PartialFailure.cpp b/qpid/cpp/src/tests/PartialFailure.cpp
new file mode 100644
index 0000000000..63ee28017a
--- /dev/null
+++ b/qpid/cpp/src/tests/PartialFailure.cpp
@@ -0,0 +1,291 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**@file Tests for partial failure in a cluster.
+ * Partial failure means some nodes experience a failure while others do not.
+ * In this case the failed nodes must shut down.
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ClusterFixture.h"
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(PartialFailureTestSuite)
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid::client::arg;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
+
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=sys::TIME_SEC/4;
+
+static bool isLogOption(const std::string& s) { return boost::starts_with(s, "--log-enable"); }
+
+void updateArgs(ClusterFixture::Args& args, size_t index) {
+ ostringstream clusterLib, testStoreLib, storeName;
+ clusterLib << getLibPath("CLUSTER_LIB");
+ testStoreLib << getLibPath("TEST_STORE_LIB");
+ storeName << "s" << index;
+ args.push_back("--auth");
+ args.push_back("no");
+ args.push_back("--no-module-dir");
+ args.push_back("--load-module");
+ args.push_back(clusterLib.str());
+ args.push_back("--load-module");
+ args.push_back(testStoreLib.str());
+ args.push_back("--test-store-name");
+ args.push_back(storeName.str());
+ args.push_back("TMP_DATA_DIR");
+
+ // These tests generate errors deliberately, disable error logging unless a log env var is set.
+ if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")) {
+ remove_if(args.begin(), args.end(), isLogOption);
+ args.push_back("--log-enable=critical+:DISABLED"); // hacky way to disable logs.
+ }
+}
+
+Message pMessage(string data, string q) {
+ Message msg(data, q);
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ return msg;
+}
+
+void queueAndSub(Client& c) {
+ c.session.queueDeclare(c.name, durable=true);
+ c.subs.subscribe(c.lq, c.name);
+}
+
+// Handle near-simultaneous errors
+QPID_AUTO_TEST_CASE(testCoincidentErrors) {
+ ClusterFixture cluster(2, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+
+ c0.session.queueDeclare("q", durable=true);
+ {
+ ScopedSuppressLogging allQuiet;
+ async(c0.session).messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception]", "q"));
+ async(c1.session).messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception]", "q"));
+
+ int alive=0;
+ try { Client c00(cluster[0], "c00"); ++alive; c00.close(); } catch (...) {}
+ try { Client c11(cluster[1], "c11"); ++alive; c11.close(); } catch (...) {}
+
+ BOOST_CHECK_EQUAL(alive, 1);
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ }
+}
+
+// Verify normal cluster-wide errors.
+QPID_AUTO_TEST_CASE(testNormalErrors) {
+ // FIXME aconway 2009-04-10: Would like to put a scope just around
+ // the statements expected to fail (in BOOST_CHECK_yTHROW) but that
+ // sproadically lets out messages, possibly because they're in
+ // Connection thread.
+
+ ClusterFixture cluster(3, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+
+ {
+ ScopedSuppressLogging allQuiet;
+ queueAndSub(c0);
+ c0.session.messageTransfer(content=Message("x", "c0"));
+ BOOST_CHECK_EQUAL(c0.lq.get(TIMEOUT).getData(), "x");
+
+ // Session error.
+ BOOST_CHECK_THROW(c0.session.exchangeBind(), SessionException);
+ c1.session.messageTransfer(content=Message("stay", "c0")); // Will stay on queue, session c0 is dead.
+
+ // Connection error, kill c1 on all members.
+ queueAndSub(c1);
+ BOOST_CHECK_THROW(
+ c1.session.messageTransfer(
+ content=pMessage("TEST_STORE_DO: s0[exception] s1[exception] s2[exception] testNormalErrors", "c1")),
+ ConnectionException);
+ c2.session.messageTransfer(content=Message("stay", "c1")); // Will stay on queue, session/connection c1 is dead.
+
+ BOOST_CHECK_EQUAL(3u, knownBrokerPorts(c2.connection, 3).size());
+ BOOST_CHECK_EQUAL(c2.subs.get("c0", TIMEOUT).getData(), "stay");
+ BOOST_CHECK_EQUAL(c2.subs.get("c1", TIMEOUT).getData(), "stay");
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+
+// Test errors after a new member joins to verify frame-sequence-numbers are ok in update.
+QPID_AUTO_TEST_CASE(testErrorAfterJoin) {
+ ClusterFixture cluster(1, updateArgs, -1);
+ Client c0(cluster[0]);
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Kill the new guy
+ cluster.add();
+ Client c1(cluster[1]);
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testErrorAfterJoin", "q"));
+ BOOST_CHECK_THROW(c1.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
+
+ // Kill the old guy
+ cluster.add();
+ Client c2(cluster[2]);
+ c2.session.messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception] testErrorAfterJoin2", "q"));
+ BOOST_CHECK_THROW(c0.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
+
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c2.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+// Test that if one member fails and others do not, the failure leaves the cluster.
+QPID_AUTO_TEST_CASE(testSinglePartialFailure) {
+ ClusterFixture cluster(3, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+ // Cause partial failure on c1
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testSinglePartialFailure", "q"));
+ BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("b", "q"));
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 3u);
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
+
+ // Cause partial failure on c2
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s2[exception] testSinglePartialFailure2", "q"));
+ BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("c", "q"));
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 5u);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+// Test multiple partial falures: 2 fail 2 pass
+QPID_AUTO_TEST_CASE(testMultiPartialFailure) {
+ ClusterFixture cluster(4, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+ Client c3(cluster[3], "c3");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Cause partial failure on c1, c2
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] s2[exception] testMultiPartialFailure", "q"));
+ BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
+ BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("b", "q"));
+ c3.session.messageTransfer(content=pMessage("c", "q"));
+ BOOST_CHECK_EQUAL(c3.session.queueQuery("q").getMessageCount(), 4u);
+ // FIXME aconway 2009-06-30: This check fails sporadically with 2 != 3.
+ // It should pass reliably.
+ // BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ c3.close();
+ }
+}
+
+/** FIXME aconway 2009-04-10:
+ * The current approach to shutting down a process in test_store
+ * sometimes leads to assertion failures and errors in the shut-down
+ * process. Need a cleaner solution
+ */
+#if 0
+QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) {
+ ClusterFixture cluster(2, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Cause failure on member 0 and simultaneous crash on member 1.
+ BOOST_CHECK_THROW(
+ c0.session.messageTransfer(
+ content=pMessage("TEST_STORE_DO: s0[exception] s1[exit_process] testPartialFailureMemberLeaves", "q")),
+ ConnectionException);
+ cluster.wait(1);
+
+ Client c00(cluster[0], "c00"); // Old connection is dead.
+ BOOST_CHECK_EQUAL(c00.session.queueQuery("q").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c00.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ }
+}
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/PollableCondition.cpp b/qpid/cpp/src/tests/PollableCondition.cpp
new file mode 100644
index 0000000000..f9b3c25c93
--- /dev/null
+++ b/qpid/cpp/src/tests/PollableCondition.cpp
@@ -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.
+ *
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(PollableConditionTest)
+
+using namespace qpid::sys;
+
+const Duration SHORT = TIME_SEC/100;
+const Duration LONG = TIME_SEC/10;
+
+class Callback {
+ public:
+ enum Action { NONE, CLEAR };
+
+ Callback() : count(), action(NONE) {}
+
+ void call(PollableCondition& pc) {
+ Mutex::ScopedLock l(lock);
+ ++count;
+ switch(action) {
+ case NONE: break;
+ case CLEAR: pc.clear(); break;
+ }
+ action = NONE;
+ lock.notify();
+ }
+
+ bool isCalling() { Mutex::ScopedLock l(lock); return wait(LONG); }
+
+ bool isNotCalling() { Mutex::ScopedLock l(lock); return !wait(SHORT); }
+
+ bool nextCall(Action a=NONE) {
+ Mutex::ScopedLock l(lock);
+ action = a;
+ return wait(LONG);
+ }
+
+ private:
+ bool wait(Duration timeout) {
+ int n = count;
+ AbsTime deadline(now(), timeout);
+ while (n == count && lock.wait(deadline))
+ ;
+ return n != count;
+ }
+
+ Monitor lock;
+ int count;
+ Action action;
+};
+
+QPID_AUTO_TEST_CASE(testPollableCondition) {
+ boost::shared_ptr<Poller> poller(new Poller());
+ Callback callback;
+ PollableCondition pc(boost::bind(&Callback::call, &callback, _1), poller);
+
+ Thread runner = Thread(*poller);
+
+ BOOST_CHECK(callback.isNotCalling()); // condition is not set.
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ BOOST_CHECK(callback.isCalling()); // Still set.
+
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared.
+
+ poller->shutdown();
+ runner.join();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} //namespace qpid::tests
diff --git a/qpid/cpp/src/tests/PollerTest.cpp b/qpid/cpp/src/tests/PollerTest.cpp
new file mode 100644
index 0000000000..9fa5689c5f
--- /dev/null
+++ b/qpid/cpp/src/tests/PollerTest.cpp
@@ -0,0 +1,263 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * Use socketpair to test the poller
+ */
+
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+
+#include <string>
+#include <iostream>
+#include <memory>
+#include <exception>
+
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace std;
+using namespace qpid::sys;
+
+int writeALot(int fd, const string& s) {
+ int bytesWritten = 0;
+ do {
+ errno = 0;
+ int lastWrite = ::write(fd, s.c_str(), s.size());
+ if ( lastWrite >= 0) {
+ bytesWritten += lastWrite;
+ }
+ } while (errno != EAGAIN);
+ return bytesWritten;
+}
+
+int readALot(int fd) {
+ int bytesRead = 0;
+ char buf[1024];
+
+ do {
+ errno = 0;
+ int lastRead = ::read(fd, buf, sizeof(buf));
+ if ( lastRead >= 0) {
+ bytesRead += lastRead;
+ }
+ } while (errno != EAGAIN);
+ return bytesRead;
+}
+
+void makesocketpair(int (&sv)[2]) {
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ assert(rc >= 0);
+
+ // Set non-blocking
+ rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+}
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ try
+ {
+ int sv[2];
+ makesocketpair(sv);
+
+ // Make up a large string
+ string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
+ for (int i = 0; i < 6; i++)
+ testString += testString;
+
+ // Read as much as we can from socket 0
+ int bytesRead = readALot(sv[0]);
+ assert(bytesRead == 0);
+
+ // Write as much as we can to socket 0
+ int bytesWritten = writeALot(sv[0], testString);
+
+ // Read as much as we can from socket 1
+ bytesRead = readALot(sv[1]);
+ assert(bytesRead == bytesWritten);
+
+ auto_ptr<Poller> poller(new Poller);
+
+ PosixIOHandle f0(sv[0]);
+ PosixIOHandle f1(sv[1]);
+
+ PollerHandle h0(f0);
+ PollerHandle h1(f1);
+
+ poller->registerHandle(h0);
+ poller->monitorHandle(h0, Poller::INOUT);
+
+ // h0 should be writable
+ Poller::Event event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ // Write as much as we can to socket 0
+ bytesWritten = writeALot(sv[0], testString);
+
+ // Wait for 500ms - h0 no longer writable
+ event = poller->wait(500000000);
+ assert(event.handle == 0);
+
+ // Test we can read it all now
+ poller->registerHandle(h1);
+ poller->monitorHandle(h1, Poller::INOUT);
+ event = poller->wait();
+ assert(event.handle == &h1);
+ assert(event.type == Poller::READ_WRITABLE);
+
+ bytesRead = readALot(sv[1]);
+ assert(bytesRead == bytesWritten);
+
+ // Test poller interrupt
+ assert(poller->interrupt(h0) == true);
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::INTERRUPTED);
+
+ // Test multiple interrupts
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
+
+ // Make sure we can interrupt them again
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
+
+ // Make sure that they both come out
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0 || event.handle == &h1);
+ if (event.handle == &h0) {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h1);
+ } else {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0);
+ }
+
+ poller->unmonitorHandle(h1, Poller::INOUT);
+
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ // We didn't write anything so it should still be writable
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ poller->unmonitorHandle(h0, Poller::INOUT);
+
+ event = poller->wait(500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h1);
+ assert(poller->interrupt(h1) == false);
+
+ // close the other end to force a disconnect
+ ::close(sv[1]);
+
+ // Now make sure that we are readable followed by disconnected
+ // and after that we never return again
+ poller->monitorHandle(h0, Poller::INOUT);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::READABLE);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::DISCONNECTED);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ // Now we're disconnected monitoring should have no effect at all
+ poller->unmonitorHandle(h0, Poller::INOUT);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h0);
+ assert(poller->interrupt(h0) == false);
+
+ // Test shutdown
+ poller->shutdown();
+ event = poller->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ event = poller->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ ::close(sv[0]);
+
+ // Test for correct interaction of shutdown and interrupts - need to have new poller
+ // etc. for this
+ makesocketpair(sv);
+
+ auto_ptr<Poller> poller1(new Poller);
+
+ PosixIOHandle f2(sv[0]);
+ PosixIOHandle f3(sv[1]);
+
+ PollerHandle h2(f2);
+ PollerHandle h3(f3);
+
+ poller1->registerHandle(h2);
+ poller1->monitorHandle(h2, Poller::INOUT);
+ event = poller1->wait();
+ assert(event.handle == &h2);
+ assert(event.type == Poller::WRITABLE);
+
+ // Shutdown
+ poller1->shutdown();
+ event = poller1->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ assert(poller1->interrupt(h2) == true);
+ event = poller1->wait();
+ assert(event.handle == &h2);
+ assert(event.type == Poller::INTERRUPTED);
+ poller1->unmonitorHandle(h2, Poller::INOUT);
+
+ event = poller1->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ poller1->unregisterHandle(h2);
+ return 0;
+ } catch (exception& e) {
+ cout << "Caught exception " << e.what() << "\n";
+ }
+}
+
+
diff --git a/qpid/cpp/src/tests/ProxyTest.cpp b/qpid/cpp/src/tests/ProxyTest.cpp
new file mode 100644
index 0000000000..a926b28395
--- /dev/null
+++ b/qpid/cpp/src/tests/ProxyTest.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/ExecutionSyncBody.h"
+#include "qpid/framing/Proxy.h"
+
+#include "unit_test.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ProxyTestSuite)
+
+
+QPID_AUTO_TEST_CASE(testScopedSync)
+{
+ struct DummyHandler : FrameHandler
+ {
+ void handle(AMQFrame& f) {
+ AMQMethodBody* m = f.getMethod();
+ BOOST_CHECK(m);
+ BOOST_CHECK(m->isA<ExecutionSyncBody>());
+ BOOST_CHECK(m->isSync());
+ }
+ };
+ DummyHandler f;
+ Proxy p(f);
+ Proxy::ScopedSync s(p);
+ p.send(ExecutionSyncBody(p.getVersion()));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Qmf2.cpp b/qpid/cpp/src/tests/Qmf2.cpp
new file mode 100644
index 0000000000..66c774accd
--- /dev/null
+++ b/qpid/cpp/src/tests/Qmf2.cpp
@@ -0,0 +1,320 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/types/Variant.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/exceptions.h"
+
+#include "unit_test.h"
+
+using namespace qpid::types;
+using namespace qmf;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(Qmf2Suite)
+
+QPID_AUTO_TEST_CASE(testQuery)
+{
+ Query query(QUERY_OBJECT, "class_name", "package_name", "[and, [eq, name, [quote, smith]], [lt, age, [quote, 27]]]");
+ Query newQuery(new QueryImpl(QueryImplAccess::get(query).asMap()));
+
+ BOOST_CHECK_EQUAL(newQuery.getTarget(), QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(newQuery.getSchemaId().getName(), "class_name");
+ BOOST_CHECK_EQUAL(newQuery.getSchemaId().getPackageName(), "package_name");
+
+ Variant::List pred(newQuery.getPredicate());
+ BOOST_CHECK_EQUAL(pred.size(), size_t(3));
+
+ Variant::List::iterator iter(pred.begin());
+ BOOST_CHECK_EQUAL(iter->asString(), "and");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter = iter->asList().begin();
+ BOOST_CHECK_EQUAL(iter->asString(), "lt");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->asString(), "age");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter = iter->asList().begin();
+ BOOST_CHECK_EQUAL(iter->asString(), "quote");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->asUint32(), uint32_t(27));
+
+ Query query2(QUERY_OBJECT_ID);
+ Query newQuery2(new QueryImpl(QueryImplAccess::get(query2).asMap()));
+ BOOST_CHECK_EQUAL(newQuery2.getTarget(), QUERY_OBJECT_ID);
+
+ Query query3(QUERY_SCHEMA);
+ Query newQuery3(new QueryImpl(QueryImplAccess::get(query3).asMap()));
+ BOOST_CHECK_EQUAL(newQuery3.getTarget(), QUERY_SCHEMA);
+
+ Query query4(QUERY_SCHEMA_ID);
+ Query newQuery4(new QueryImpl(QueryImplAccess::get(query4).asMap()));
+ BOOST_CHECK_EQUAL(newQuery4.getTarget(), QUERY_SCHEMA_ID);
+
+ DataAddr addr("name", "agent_name", 34);
+ Query query5(addr);
+ Query newQuery5(new QueryImpl(QueryImplAccess::get(query5).asMap()));
+ BOOST_CHECK_EQUAL(newQuery5.getTarget(), QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getName(), "name");
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getAgentName(), "agent_name");
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getAgentEpoch(), uint32_t(34));
+}
+
+QPID_AUTO_TEST_CASE(testQueryPredicateErrors)
+{
+ Query query;
+ Variant::Map map;
+
+ BOOST_CHECK_THROW(Query(QUERY_OBJECT, "INVALID"), QmfException);
+ query = Query(QUERY_OBJECT, "[unknown, one, two]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[exists]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, [quote, 1, 2, 3]]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, [unexpected, 3]]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, {}]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, second, third]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[and, first, second, third]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+}
+
+QPID_AUTO_TEST_CASE(testQueryPredicate)
+{
+ Query query;
+ Variant::Map map;
+
+ map["forty"] = 40;
+ map["fifty"] = 50;
+ map["minus_ten"] = -10;
+ map["pos_float"] = 100.05;
+ map["neg_float"] = -1000.33;
+ map["name"] = "jones";
+ map["bool_t"] = true;
+ map["bool_f"] = false;
+
+ BOOST_CHECK_THROW(Query(QUERY_OBJECT, "INVALID"), QmfException);
+
+ query = Query(QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(query.matchesPredicate(Variant::Map()), true);
+
+ query = Query(QUERY_OBJECT, "[eq, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, forty, [quote, 41]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[le, forty, fifty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[and, [eq, forty, [quote, 40]], [eq, name, [quote, jones]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[and, [eq, forty, [quote, 40]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[or, [eq, forty, [quote, 40]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[or, [eq, forty, [quote, 41]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[not, [le, forty, [quote, 40]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[le, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[ge, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, forty, [quote, 45]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, [quote, 45], forty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[gt, forty, [quote, 45]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[gt, [quote, 45], forty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_t, [quote, True]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_t, [quote, False]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_f, [quote, True]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_f, [quote, False]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, minus_ten, [quote, -10]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, minus_ten, [quote, -20]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[lt, [quote, -20], minus_ten]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[exists, name]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[exists, nonexfield]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, pos_float, [quote, 100.05]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+}
+
+QPID_AUTO_TEST_CASE(testSchema)
+{
+ Schema in(SCHEMA_TYPE_DATA, "package", "class");
+ in.addProperty(SchemaProperty("prop1", SCHEMA_DATA_BOOL, "{desc:'Property One'}"));
+ in.addProperty(SchemaProperty("prop2", SCHEMA_DATA_INT, "{desc:'Property Two',unit:'Furlong'}"));
+ in.addProperty(SchemaProperty("prop3", SCHEMA_DATA_STRING, "{desc:'Property Three'}"));
+
+ SchemaMethod method1("method1", "{desc:'Method One'}");
+ method1.addArgument(SchemaProperty("arg1", SCHEMA_DATA_BOOL, "{desc:'Argument One',dir:IN}"));
+ method1.addArgument(SchemaProperty("arg2", SCHEMA_DATA_INT, "{desc:'Argument Two',dir:OUT}"));
+ method1.addArgument(SchemaProperty("arg3", SCHEMA_DATA_FLOAT, "{desc:'Argument Three',dir:INOUT}"));
+ in.addMethod(method1);
+
+ SchemaMethod method2("method2", "{desc:'Method Two'}");
+ method2.addArgument(SchemaProperty("arg21", SCHEMA_DATA_BOOL, "{desc:'Argument One',dir:IN}"));
+ method2.addArgument(SchemaProperty("arg22", SCHEMA_DATA_INT, "{desc:'Argument Two',dir:OUT}"));
+ method2.addArgument(SchemaProperty("arg23", SCHEMA_DATA_FLOAT, "{desc:'Argument Three',dir:INOUT}"));
+ in.addMethod(method2);
+
+ BOOST_CHECK(!in.isFinalized());
+ in.finalize();
+ BOOST_CHECK(in.isFinalized());
+
+ Variant::Map map(SchemaImplAccess::get(in).asMap());
+ Schema out(new SchemaImpl(map));
+
+ BOOST_CHECK(out.isFinalized());
+ BOOST_CHECK_EQUAL(out.getSchemaId().getType(), SCHEMA_TYPE_DATA);
+ BOOST_CHECK_EQUAL(out.getSchemaId().getPackageName(), "package");
+ BOOST_CHECK_EQUAL(out.getSchemaId().getName(), "class");
+ BOOST_CHECK_EQUAL(out.getSchemaId().getHash(), in.getSchemaId().getHash());
+
+ BOOST_CHECK_EQUAL(out.getPropertyCount(), uint32_t(3));
+ SchemaProperty prop;
+
+ prop = out.getProperty(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop1");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property One");
+
+ prop = out.getProperty(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop2");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property Two");
+ BOOST_CHECK_EQUAL(prop.getUnit(), "Furlong");
+ BOOST_CHECK(!prop.isIndex());
+
+ prop = out.getProperty(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop3");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_STRING);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property Three");
+
+ BOOST_CHECK_THROW(out.getProperty(3), QmfException);
+
+ BOOST_CHECK_EQUAL(out.getMethodCount(), uint32_t(2));
+ SchemaMethod method;
+
+ method = out.getMethod(0);
+ BOOST_CHECK_EQUAL(method.getName(), "method1");
+ BOOST_CHECK_EQUAL(method.getDesc(), "Method One");
+ BOOST_CHECK_EQUAL(method.getArgumentCount(), uint32_t(3));
+
+ prop = method.getArgument(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg1");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument One");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN);
+
+ prop = method.getArgument(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg2");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Two");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_OUT);
+
+ prop = method.getArgument(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg3");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_FLOAT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Three");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN_OUT);
+
+ BOOST_CHECK_THROW(method.getArgument(3), QmfException);
+
+ method = out.getMethod(1);
+ BOOST_CHECK_EQUAL(method.getName(), "method2");
+ BOOST_CHECK_EQUAL(method.getDesc(), "Method Two");
+ BOOST_CHECK_EQUAL(method.getArgumentCount(), uint32_t(3));
+
+ prop = method.getArgument(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg21");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument One");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN);
+
+ prop = method.getArgument(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg22");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Two");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_OUT);
+
+ prop = method.getArgument(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg23");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_FLOAT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Three");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN_OUT);
+
+ BOOST_CHECK_THROW(method.getArgument(3), QmfException);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueEvents.cpp b/qpid/cpp/src/tests/QueueEvents.cpp
new file mode 100644
index 0000000000..bd18fa45fb
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueEvents.cpp
@@ -0,0 +1,238 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "MessageUtils.h"
+#include "unit_test.h"
+#include "BrokerFixture.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/sys/Dispatcher.h"
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueEventsSuite)
+
+using namespace qpid::client;
+using namespace qpid::broker;
+using namespace qpid::sys;
+using qpid::framing::SequenceNumber;
+
+struct EventChecker
+{
+ typedef std::deque<QueueEvents::Event> Events;
+
+ Events events;
+ boost::shared_ptr<Poller> poller;
+
+ void handle(QueueEvents::Event e)
+ {
+ if (events.empty()) {
+ BOOST_FAIL("Unexpected event received");
+ } else {
+ BOOST_CHECK_EQUAL(events.front().type, e.type);
+ BOOST_CHECK_EQUAL(events.front().msg.queue, e.msg.queue);
+ BOOST_CHECK_EQUAL(events.front().msg.payload, e.msg.payload);
+ BOOST_CHECK_EQUAL(events.front().msg.position, e.msg.position);
+ events.pop_front();
+ }
+ if (events.empty() && poller) poller->shutdown();
+ }
+
+ void expect(QueueEvents::Event e)
+ {
+ events.push_back(e);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testBasicEventProcessing)
+{
+ boost::shared_ptr<Poller> poller(new Poller());
+ sys::Dispatcher dispatcher(poller);
+ Thread dispatchThread(dispatcher);
+ QueueEvents events(poller);
+ EventChecker listener;
+ listener.poller = poller;
+ events.registerListener("dummy", boost::bind(&EventChecker::handle, &listener, _1));
+ //signal occurence of some events:
+ Queue queue("queue1");
+ SequenceNumber id;
+ QueuedMessage event1(&queue, MessageUtils::createMessage(), id);
+ QueuedMessage event2(&queue, MessageUtils::createMessage(), ++id);
+
+ //define events expected by listener:
+ listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event1));
+ listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event2));
+ listener.expect(QueueEvents::Event(QueueEvents::DEQUEUE, event1));
+
+ events.enqueued(event1);
+ events.enqueued(event2);
+ events.dequeued(event1);
+
+ dispatchThread.join();
+ events.shutdown();
+ events.unregisterListener("dummy");
+}
+
+
+struct EventRecorder
+{
+ struct EventRecord
+ {
+ QueueEvents::EventType type;
+ std::string queue;
+ std::string content;
+ SequenceNumber position;
+ };
+
+ typedef std::deque<EventRecord> Events;
+
+ Events events;
+
+ void handle(QueueEvents::Event event)
+ {
+ EventRecord record;
+ record.type = event.type;
+ record.queue = event.msg.queue->getName();
+ event.msg.payload->getFrames().getContent(record.content);
+ record.position = event.msg.position;
+ events.push_back(record);
+ }
+
+ void check(QueueEvents::EventType type, const std::string& queue, const std::string& content, const SequenceNumber& position)
+ {
+ if (events.empty()) {
+ BOOST_FAIL("Missed event");
+ } else {
+ BOOST_CHECK_EQUAL(events.front().type, type);
+ BOOST_CHECK_EQUAL(events.front().queue, queue);
+ BOOST_CHECK_EQUAL(events.front().content, content);
+ BOOST_CHECK_EQUAL(events.front().position, position);
+ events.pop_front();
+ }
+ }
+ void checkEnqueue(const std::string& queue, const std::string& data, const SequenceNumber& position)
+ {
+ check(QueueEvents::ENQUEUE, queue, data, position);
+ }
+
+ void checkDequeue(const std::string& queue, const std::string& data, const SequenceNumber& position)
+ {
+ check(QueueEvents::DEQUEUE, queue, data, position);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing)
+{
+ ProxySessionFixture fixture;
+ //register dummy event listener to broker
+ EventRecorder listener;
+ fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
+
+ //declare queue with event options specified
+ QueueOptions options;
+ options.enableQueueEvents(false);
+ std::string q("queue-events-test");
+ fixture.session.queueDeclare(arg::queue=q, arg::arguments=options);
+ //send and consume some messages
+ LocalQueue incoming;
+ Subscription sub = fixture.subs.subscribe(incoming, q);
+ for (int i = 0; i < 5; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 3; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ for (int i = 5; i < 10; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 3; i < 10; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ fixture.connection.close();
+ fixture.broker->getQueueEvents().shutdown();
+
+ //check listener was notified of all events, and in correct order
+ SequenceNumber enqueueId(1);
+ SequenceNumber dequeueId(1);
+ for (int i = 0; i < 5; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 0; i < 3; i++) {
+ listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++);
+ }
+ for (int i = 5; i < 10; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 3; i < 10; i++) {
+ listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly)
+{
+ ProxySessionFixture fixture;
+ //register dummy event listener to broker
+ EventRecorder listener;
+ fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
+
+ //declare queue with event options specified
+ QueueOptions options;
+ options.enableQueueEvents(true);
+ std::string q("queue-events-test");
+ fixture.session.queueDeclare(arg::queue=q, arg::arguments=options);
+ //send and consume some messages
+ LocalQueue incoming;
+ Subscription sub = fixture.subs.subscribe(incoming, q);
+ for (int i = 0; i < 5; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 3; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ for (int i = 5; i < 10; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 3; i < 10; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ fixture.connection.close();
+ fixture.broker->getQueueEvents().shutdown();
+
+ //check listener was notified of all events, and in correct order
+ SequenceNumber enqueueId(1);
+ SequenceNumber dequeueId(1);
+ for (int i = 0; i < 5; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 5; i < 10; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
new file mode 100644
index 0000000000..8a6923fb09
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
@@ -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.
+ *
+ */
+#include <sstream>
+#include <deque>
+#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "MessageUtils.h"
+#include "BrokerFixture.h"
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueFlowLimitTestSuite)
+
+namespace {
+
+class TestFlow : public QueueFlowLimit
+{
+public:
+ TestFlow(uint32_t flowStopCount, uint32_t flowResumeCount,
+ uint64_t flowStopSize, uint64_t flowResumeSize) :
+ QueueFlowLimit(0, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize)
+ {}
+ virtual ~TestFlow() {}
+
+ static TestFlow *createTestFlow(const qpid::framing::FieldTable& settings)
+ {
+ FieldTable::ValuePtr v;
+
+ v = settings.get(flowStopCountKey);
+ uint32_t flowStopCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeCountKey);
+ uint32_t flowResumeCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowStopSizeKey);
+ uint64_t flowStopSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeSizeKey);
+ uint64_t flowResumeSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+
+ return new TestFlow(flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+
+ static QueueFlowLimit *getQueueFlowLimit(const qpid::framing::FieldTable& settings)
+ {
+ return QueueFlowLimit::createLimit(0, settings);
+ }
+};
+
+
+
+QueuedMessage createMessage(uint32_t size)
+{
+ static uint32_t seqNum;
+ QueuedMessage msg;
+ msg.payload = MessageUtils::createMessage();
+ msg.position = ++seqNum;
+ MessageUtils::addContent(msg.payload, std::string (size, 'x'));
+ return msg;
+}
+}
+
+QPID_AUTO_TEST_CASE(testFlowCount)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 7);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 7, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 5, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<QueuedMessage> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 6 on queue
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive()); // 7 on queue
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue, ON
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 9 on queue, no change to flow control
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 7 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 6 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 5 on queue, no change
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 4 on queue, OFF
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowSize)
+{
+ FieldTable args;
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 70);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 50);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 70, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 50, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<QueuedMessage> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 60 on queue
+ BOOST_CHECK_EQUAL(6u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(60u, flow->getFlowSize());
+
+ QueuedMessage msg_9 = createMessage(9);
+ flow->enqueued(msg_9);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 69 on queue
+ QueuedMessage tinyMsg_1 = createMessage(1);
+ flow->enqueued(tinyMsg_1);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 70 on queue
+
+ QueuedMessage tinyMsg_2 = createMessage(1);
+ flow->enqueued(tinyMsg_2);
+ BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue, ON
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 81 on queue
+ BOOST_CHECK_EQUAL(10u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(81u, flow->getFlowSize());
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 61 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 51 on queue
+
+ flow->dequeued(tinyMsg_1);
+ BOOST_CHECK(flow->isFlowControlActive()); // 50 on queue
+ flow->dequeued(tinyMsg_2);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 49 on queue, OFF
+
+ flow->dequeued(msg_9);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 40 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 30 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 20 on queue
+ BOOST_CHECK_EQUAL(2u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(20u, flow->getFlowSize());
+}
+
+QPID_AUTO_TEST_CASE(testFlowArgs)
+{
+ FieldTable args;
+ const uint64_t stop(0x2FFFFFFFFull);
+ const uint64_t resume(0x1FFFFFFFFull);
+ args.setInt(QueueFlowLimit::flowStopCountKey, 30);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 21);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, stop);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, resume);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 30, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 21, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL(stop, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL(resume, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowCombo)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 10);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 200);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 100);
+
+ std::deque<QueuedMessage> msgs_1;
+ std::deque<QueuedMessage> msgs_10;
+ std::deque<QueuedMessage> msgs_50;
+ std::deque<QueuedMessage> msgs_100;
+
+ QueuedMessage msg;
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+ BOOST_CHECK(!flow->isFlowControlActive()); // count:0 size:0
+
+ // verify flow control comes ON when only count passes its stop point.
+
+ for (size_t i = 0; i < 10; i++) {
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:10 size:100
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:11 size: 101 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 6; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:5 size: 41
+
+ flow->dequeued(msgs_1.front()); // count: 4 size: 40 ->OFF
+ msgs_1.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 4; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:0 size:0
+
+ // verify flow control comes ON when only size passes its stop point.
+
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back()); // count:1 size: 100
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:2 size: 150
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:3 size: 200
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:4 size: 201 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_100.front()); // count:3 size:101
+ msgs_100.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_1.front()); // count:2 size:100
+ msgs_1.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_50.front()); // count:1 size:50 ->OFF
+ msgs_50.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ // verify flow control remains ON until both thresholds drop below their
+ // resume point.
+
+ for (size_t i = 0; i < 8; i++) {
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:9 size:130
+
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back()); // count:10 size: 140
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:11 size: 141 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back()); // count:12 size: 241 (both thresholds crossed)
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ // at this point: 9@10 + 1@50 + 1@100 + 1@1 == 12@241
+
+ flow->dequeued(msgs_50.front()); // count:11 size:191
+ msgs_50.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 9; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:2 size:101
+ flow->dequeued(msgs_1.front()); // count:1 size:100
+ msgs_1.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // still active due to size
+
+ flow->dequeued(msgs_100.front()); // count:0 size:0 ->OFF
+ msgs_100.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDefaultArgs)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ FieldTable args;
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+ BOOST_CHECK_EQUAL((uint64_t) 2360001, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 2065000, flow->getFlowResumeSize());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowResumeCount());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideArgs)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideDefaults)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 97, // stop threshold
+ 73); // resume threshold
+ FieldTable args;
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 2861501, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 2153500, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDisable)
+{
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(!ptr);
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 0);
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(!ptr);
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueOptionsTest.cpp b/qpid/cpp/src/tests/QueueOptionsTest.cpp
new file mode 100644
index 0000000000..f2fbaba2c1
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueOptionsTest.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/framing/Array.h"
+#include "qpid/client/QueueOptions.h"
+
+#include "unit_test.h"
+
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite)
+
+QPID_AUTO_TEST_CASE(testSizePolicy)
+{
+ QueueOptions ft;
+
+ ft.setSizePolicy(REJECT,1,2);
+
+ BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(FLOW_TO_DISK,0,2);
+ BOOST_CHECK(QueueOptions::strFLOW_TO_DISK == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(RING,1,0);
+ BOOST_CHECK(QueueOptions::strRING == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.setSizePolicy(RING_STRICT,1,0);
+ BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.clearSizePolicy();
+ BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxCountKey));
+}
+
+QPID_AUTO_TEST_CASE(testFlags)
+{
+ QueueOptions ft;
+
+ ft.setPersistLastNode();
+ ft.setOrdering(LVQ);
+
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue));
+
+ ft.clearPersistLastNode();
+ ft.setOrdering(FIFO);
+
+ BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue));
+
+}
+
+QPID_AUTO_TEST_CASE(testSetOrdering)
+{
+ //ensure setOrdering(FIFO) works even if not preceded by a call to
+ //setOrdering(LVQ)
+ QueueOptions ft;
+ ft.setOrdering(FIFO);
+ BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue));
+
+}
+
+QPID_AUTO_TEST_CASE(testClearPersistLastNode)
+{
+ //ensure clear works even if not preceded by the setting on the
+ //option
+ QueueOptions ft;
+ ft.clearPersistLastNode();
+ BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode));
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp
new file mode 100644
index 0000000000..5455105078
--- /dev/null
+++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp
@@ -0,0 +1,406 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <sstream>
+#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "MessageUtils.h"
+#include "BrokerFixture.h"
+
+using namespace qpid::broker;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite)
+
+namespace {
+QueuedMessage createMessage(uint32_t size)
+{
+ QueuedMessage msg;
+ msg.payload = MessageUtils::createMessage();
+ MessageUtils::addContent(msg.payload, std::string (size, 'x'));
+ return msg;
+}
+}
+
+QPID_AUTO_TEST_CASE(testCount)
+{
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 0));
+ BOOST_CHECK_EQUAL((uint64_t) 0, policy->getMaxSize());
+ BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount());
+
+ QueuedMessage msg = createMessage(10);
+ for (size_t i = 0; i < 5; i++) {
+ policy->tryEnqueue(msg.payload);
+ }
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on enqueuing sixth message");
+ } catch (const ResourceLimitExceededException&) {}
+
+ policy->dequeued(msg);
+ policy->tryEnqueue(msg.payload);
+
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testSize)
+{
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 0, 50));
+ QueuedMessage msg = createMessage(10);
+
+ for (size_t i = 0; i < 5; i++) {
+ policy->tryEnqueue(msg.payload);
+ }
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+ policy->dequeued(msg);
+ policy->tryEnqueue(msg.payload);
+
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testBoth)
+{
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 50));
+ try {
+ QueuedMessage msg = createMessage(51);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on single message exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+ std::vector<QueuedMessage> messages;
+ messages.push_back(createMessage(15));
+ messages.push_back(createMessage(10));
+ messages.push_back(createMessage(11));
+ messages.push_back(createMessage(2));
+ messages.push_back(createMessage(7));
+ for (size_t i = 0; i < messages.size(); i++) {
+ policy->tryEnqueue(messages[i].payload);
+ }
+ //size = 45 at this point, count = 5
+ try {
+ QueuedMessage msg = createMessage(5);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+ try {
+ QueuedMessage msg = createMessage(10);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+
+ policy->dequeued(messages[0]);
+ try {
+ QueuedMessage msg = createMessage(20);
+ policy->tryEnqueue(msg.payload);
+ } catch (const ResourceLimitExceededException&) {
+ BOOST_FAIL("Policy failed incorrectly after dequeue. " << *policy);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSettings)
+{
+ //test reading and writing the policy from/to field table
+ std::auto_ptr<QueuePolicy> a(QueuePolicy::createQueuePolicy("test", 101, 303));
+ FieldTable settings;
+ a->update(settings);
+ std::auto_ptr<QueuePolicy> b(QueuePolicy::createQueuePolicy("test", settings));
+ BOOST_CHECK_EQUAL(a->getMaxCount(), b->getMaxCount());
+ BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize());
+}
+
+QPID_AUTO_TEST_CASE(testRingPolicyCount)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ client::Message msg;
+ for (int i = 5; i < 10; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+ for (int i = 10; i < 20; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 15; i < 20; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+}
+
+QPID_AUTO_TEST_CASE(testRingPolicySize)
+{
+ std::string hundredBytes = std::string(100, 'h');
+ std::string fourHundredBytes = std::string (400, 'f');
+ std::string thousandBytes = std::string(1000, 't');
+
+ // Ring queue, 500 bytes maxSize
+
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 0, 500, QueuePolicy::RING);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ // A. Send messages 0 .. 5, each 100 bytes
+
+ client::Message m(hundredBytes, q);
+
+ for (int i = 0; i < 6; i++) {
+ std::stringstream id;
+ id << i;
+ m.getMessageProperties().setCorrelationId(id.str());
+ f.session.messageTransfer(arg::content=m);
+ }
+
+ // should find 1 .. 5 on the queue, 0 is displaced by 5
+ client::Message msg;
+ for (int i = 1; i < 6; i++) {
+ std::stringstream id;
+ id << i;
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getMessageProperties().getCorrelationId(), id.str());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+ // B. Now make sure that one 400 byte message displaces four 100 byte messages
+
+ // Send messages 0 .. 5, each 100 bytes
+ for (int i = 0; i < 6; i++) {
+ client::Message m(hundredBytes, q);
+ std::stringstream id;
+ id << i;
+ m.getMessageProperties().setCorrelationId(id.str());
+ f.session.messageTransfer(arg::content=m);
+ }
+
+ // Now send one 400 byte message
+ client::Message m2(fourHundredBytes, q);
+ m2.getMessageProperties().setCorrelationId("6");
+ f.session.messageTransfer(arg::content=m2);
+
+ // expect to see 5, 6 on the queue
+ for (int i = 5; i < 7; i++) {
+ std::stringstream id;
+ id << i;
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getMessageProperties().getCorrelationId(), id.str());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+
+ // C. Try sending a 1000-byte message, should fail - exceeds maxSize of queue
+
+ client::Message m3(thousandBytes, q);
+ m3.getMessageProperties().setCorrelationId("6");
+ try {
+ ScopedSuppressLogging sl;
+ f.session.messageTransfer(arg::content=m3);
+ BOOST_FAIL("Ooops - successfully added a 1000 byte message to a 512 byte ring queue ...");
+ }
+ catch (...) {
+ }
+
+}
+
+
+QPID_AUTO_TEST_CASE(testStrictRingPolicy)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING_STRICT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testPolicyWithDtx)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-policy-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ f.session.dtxSelect();
+ Xid tx1(1, "test-dtx-mgr", "tx1");
+ f.session.dtxStart(arg::xid=tx1);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ f.session.dtxEnd(arg::xid=tx1);
+ f.session.dtxCommit(arg::xid=tx1, arg::onePhase=true);
+
+ Xid tx2(1, "test-dtx-mgr", "tx2");
+ f.session.dtxStart(arg::xid=tx2);
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ SequenceSet accepting=sub.getUnaccepted();
+ f.session.messageAccept(accepting);
+ f.session.dtxEnd(arg::xid=tx2);
+ f.session.dtxPrepare(arg::xid=tx2);
+ f.session.dtxRollback(arg::xid=tx2);
+ f.session.messageRelease(accepting);
+
+ Xid tx3(1, "test-dtx-mgr", "tx3");
+ f.session.dtxStart(arg::xid=tx3);
+ for (int i = 0; i < 5; i++) {
+ incoming.pop();
+ }
+ accepting=sub.getUnaccepted();
+ f.session.messageAccept(accepting);
+ f.session.dtxEnd(arg::xid=tx3);
+ f.session.dtxPrepare(arg::xid=tx3);
+
+ Session other = f.connection.newSession();
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ other.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+
+ f.session.dtxCommit(arg::xid=tx3);
+ //now retry and this time should succeed
+ other = f.connection.newSession();
+ other.messageTransfer(arg::content=client::Message("Message_6", q));
+}
+
+QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore)
+{
+ //Ensure that with no store loaded, we don't flow to disk but
+ //fallback to rejecting messages
+ QueueOptions args;
+ args.setSizePolicy(FLOW_TO_DISK, 0, 5);
+ // Disable flow control, or else we'll never hit the max limit
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+
+ ProxySessionFixture f;
+ std::string q("my-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("q");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ f.session.txSelect();
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ BOOST_CHECK_THROW(f.session.txCommit(), InternalErrorException);
+}
+
+QPID_AUTO_TEST_CASE(testCapacityConversion)
+{
+ FieldTable args;
+ args.setString("qpid.max_count", "5");
+ args.setString("qpid.flow_stop_count", "0");
+
+ ProxySessionFixture f;
+ std::string q("q");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueRegistryTest.cpp b/qpid/cpp/src/tests/QueueRegistryTest.cpp
new file mode 100644
index 0000000000..ae555539a4
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueRegistryTest.cpp
@@ -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.
+ */
+
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/Queue.h"
+#include "unit_test.h"
+#include <string>
+
+using namespace qpid::broker;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueRegistryTest)
+
+QPID_AUTO_TEST_CASE(testDeclare)
+{
+ std::string foo("foo");
+ std::string bar("bar");
+ QueueRegistry reg;
+ std::pair<Queue::shared_ptr, bool> qc;
+
+ qc = reg.declare(foo, false, 0, 0);
+ Queue::shared_ptr q = qc.first;
+ BOOST_CHECK(q);
+ BOOST_CHECK(qc.second); // New queue
+ BOOST_CHECK_EQUAL(foo, q->getName());
+
+ qc = reg.declare(foo, false, 0, 0);
+ BOOST_CHECK_EQUAL(q, qc.first);
+ BOOST_CHECK(!qc.second);
+
+ qc = reg.declare(bar, false, 0, 0);
+ q = qc.first;
+ BOOST_CHECK(q);
+ BOOST_CHECK_EQUAL(true, qc.second);
+ BOOST_CHECK_EQUAL(bar, q->getName());
+}
+
+QPID_AUTO_TEST_CASE(testDeclareTmp)
+{
+ QueueRegistry reg;
+ std::pair<Queue::shared_ptr, bool> qc;
+
+ qc = reg.declare(std::string(), false, 0, 0);
+ BOOST_CHECK(qc.second);
+ BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName());
+}
+
+QPID_AUTO_TEST_CASE(testFind)
+{
+ std::string foo("foo");
+ std::string bar("bar");
+ QueueRegistry reg;
+ std::pair<Queue::shared_ptr, bool> qc;
+
+ BOOST_CHECK(reg.find(foo) == 0);
+
+ reg.declare(foo, false, 0, 0);
+ reg.declare(bar, false, 0, 0);
+ Queue::shared_ptr q = reg.find(bar);
+ BOOST_CHECK(q);
+ BOOST_CHECK_EQUAL(bar, q->getName());
+}
+
+QPID_AUTO_TEST_CASE(testDestroy)
+{
+ std::string foo("foo");
+ QueueRegistry reg;
+ std::pair<Queue::shared_ptr, bool> qc;
+
+ qc = reg.declare(foo, false, 0, 0);
+ reg.destroy(foo);
+ // Queue is gone from the registry.
+ BOOST_CHECK(reg.find(foo) == 0);
+ // Queue is not actually destroyed till we drop our reference.
+ BOOST_CHECK_EQUAL(foo, qc.first->getName());
+ // We shoud be the only reference.
+ BOOST_CHECK_EQUAL(1L, qc.first.use_count());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp
new file mode 100644
index 0000000000..34e4592a15
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueTest.cpp
@@ -0,0 +1,1124 @@
+ /*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "MessageUtils.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/Exception.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+
+#include <iostream>
+#include "boost/format.hpp"
+
+using boost::intrusive_ptr;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+class TestConsumer : public virtual Consumer{
+public:
+ typedef boost::shared_ptr<TestConsumer> shared_ptr;
+
+ intrusive_ptr<Message> last;
+ bool received;
+ TestConsumer(bool acquire = true):Consumer(acquire), received(false) {};
+
+ virtual bool deliver(QueuedMessage& msg){
+ last = msg.payload;
+ received = true;
+ return true;
+ };
+ void notify() {}
+ OwnershipToken* getSession() { return 0; }
+};
+
+class FailOnDeliver : public Deliverable
+{
+ boost::intrusive_ptr<Message> msg;
+public:
+ FailOnDeliver() : msg(MessageUtils::createMessage()) {}
+ void deliverTo(const boost::shared_ptr<Queue>& queue)
+ {
+ throw Exception(QPID_MSG("Invalid delivery to " << queue->getName()));
+ }
+ Message& getMessage() { return *(msg.get()); }
+};
+
+intrusive_ptr<Message> create_message(std::string exchange, std::string routingKey) {
+ intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ return msg;
+}
+
+QPID_AUTO_TEST_SUITE(QueueTestSuite)
+
+QPID_AUTO_TEST_CASE(testAsyncMessage) {
+ Queue::shared_ptr queue(new Queue("my_test_queue", true));
+ intrusive_ptr<Message> received;
+
+ TestConsumer::shared_ptr c1(new TestConsumer());
+ queue->consume(c1);
+
+
+ //Test basic delivery:
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
+ queue->process(msg1);
+ sleep(2);
+
+ BOOST_CHECK(!c1->received);
+ msg1->enqueueComplete();
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg1.get(), received.get());
+}
+
+
+QPID_AUTO_TEST_CASE(testAsyncMessageCount){
+ Queue::shared_ptr queue(new Queue("my_test_queue", true));
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
+
+ queue->process(msg1);
+ sleep(2);
+ uint32_t compval=0;
+ BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount());
+ msg1->enqueueComplete();
+ compval=1;
+ BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount());
+ BOOST_CHECK_EQUAL(compval, queue->getMessageCount());
+}
+
+QPID_AUTO_TEST_CASE(testConsumers){
+ Queue::shared_ptr queue(new Queue("my_queue", true));
+
+ //Test adding consumers:
+ TestConsumer::shared_ptr c1(new TestConsumer());
+ TestConsumer::shared_ptr c2(new TestConsumer());
+ queue->consume(c1);
+ queue->consume(c2);
+
+ BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount());
+
+ //Test basic delivery:
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ queue->deliver(msg1);
+ BOOST_CHECK(queue->dispatch(c1));
+ BOOST_CHECK_EQUAL(msg1.get(), c1->last.get());
+
+ queue->deliver(msg2);
+ BOOST_CHECK(queue->dispatch(c2));
+ BOOST_CHECK_EQUAL(msg2.get(), c2->last.get());
+
+ c1->received = false;
+ queue->deliver(msg3);
+ BOOST_CHECK(queue->dispatch(c1));
+ BOOST_CHECK_EQUAL(msg3.get(), c1->last.get());
+
+ //Test cancellation:
+ queue->cancel(c1);
+ BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount());
+ queue->cancel(c2);
+ BOOST_CHECK_EQUAL(uint32_t(0), queue->getConsumerCount());
+}
+
+QPID_AUTO_TEST_CASE(testRegistry){
+ //Test use of queues in registry:
+ QueueRegistry registry;
+ registry.declare("queue1", true, true);
+ registry.declare("queue2", true, true);
+ registry.declare("queue3", true, true);
+
+ BOOST_CHECK(registry.find("queue1"));
+ BOOST_CHECK(registry.find("queue2"));
+ BOOST_CHECK(registry.find("queue3"));
+
+ registry.destroy("queue1");
+ registry.destroy("queue2");
+ registry.destroy("queue3");
+
+ BOOST_CHECK(!registry.find("queue1"));
+ BOOST_CHECK(!registry.find("queue2"));
+ BOOST_CHECK(!registry.find("queue3"));
+}
+
+QPID_AUTO_TEST_CASE(testDequeue){
+ Queue::shared_ptr queue(new Queue("my_queue", true));
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> received;
+
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+
+ BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg1.get(), received.get());
+ BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg2.get(), received.get());
+ BOOST_CHECK_EQUAL(uint32_t(1), queue->getMessageCount());
+
+ TestConsumer::shared_ptr consumer(new TestConsumer());
+ queue->consume(consumer);
+ queue->dispatch(consumer);
+ if (!consumer->received)
+ sleep(2);
+
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
+ BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount());
+
+ received = queue->get().payload;
+ BOOST_CHECK(!received);
+ BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount());
+
+}
+
+QPID_AUTO_TEST_CASE(testBound){
+ //test the recording of bindings, and use of those to allow a queue to be unbound
+ string key("my-key");
+ FieldTable args;
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+ ExchangeRegistry exchanges;
+ //establish bindings from exchange->queue and notify the queue as it is bound:
+ Exchange::shared_ptr exchange1 = exchanges.declare("my-exchange-1", "direct").first;
+ exchange1->bind(queue, key, &args);
+ queue->bound(exchange1->getName(), key, args);
+
+ Exchange::shared_ptr exchange2 = exchanges.declare("my-exchange-2", "fanout").first;
+ exchange2->bind(queue, key, &args);
+ queue->bound(exchange2->getName(), key, args);
+
+ Exchange::shared_ptr exchange3 = exchanges.declare("my-exchange-3", "topic").first;
+ exchange3->bind(queue, key, &args);
+ queue->bound(exchange3->getName(), key, args);
+
+ //delete one of the exchanges:
+ exchanges.destroy(exchange2->getName());
+ exchange2.reset();
+
+ //unbind the queue from all exchanges it knows it has been bound to:
+ queue->unbind(exchanges);
+
+ //ensure the remaining exchanges don't still have the queue bound to them:
+ FailOnDeliver deliverable;
+ exchange1->route(deliverable, key, &args);
+ exchange3->route(deliverable, key, &args);
+}
+
+QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){
+ client::QueueOptions args;
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+
+ //change mode
+ queue->setLastNodeFailure();
+
+ //enqueue 1 message
+ queue->deliver(msg3);
+
+ //check all have persistent ids.
+ BOOST_CHECK(msg1->isPersistent());
+ BOOST_CHECK(msg2->isPersistent());
+ BOOST_CHECK(msg3->isPersistent());
+
+}
+
+
+QPID_AUTO_TEST_CASE(testSeek){
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+
+ TestConsumer::shared_ptr consumer(new TestConsumer(false));
+ SequenceNumber seq(2);
+ consumer->position = seq;
+
+ QueuedMessage qm;
+ queue->dispatch(consumer);
+
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
+ queue->dispatch(consumer);
+ queue->dispatch(consumer); // make sure over-run is safe
+
+}
+
+QPID_AUTO_TEST_CASE(testSearch){
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+
+ SequenceNumber seq(2);
+ QueuedMessage qm = queue->find(seq);
+
+ BOOST_CHECK_EQUAL(seq.getValue(), qm.position.getValue());
+
+ queue->acquire(qm);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+ SequenceNumber seq1(3);
+ QueuedMessage qm1 = queue->find(seq1);
+ BOOST_CHECK_EQUAL(seq1.getValue(), qm1.position.getValue());
+
+}
+const std::string nullxid = "";
+
+class SimpleDummyCtxt : public TransactionContext {};
+
+class DummyCtxt : public TPCTransactionContext
+{
+ const std::string xid;
+ public:
+ DummyCtxt(const std::string& _xid) : xid(_xid) {}
+ static std::string getXid(TransactionContext& ctxt)
+ {
+ DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt));
+ return c ? c->xid : nullxid;
+ }
+};
+
+class TestMessageStoreOC : public MessageStore
+{
+ std::set<std::string> prepared;
+ uint64_t nextPersistenceId;
+ public:
+
+ uint enqCnt;
+ uint deqCnt;
+ bool error;
+
+ TestMessageStoreOC() : MessageStore(),nextPersistenceId(1),enqCnt(0),deqCnt(0),error(false) {}
+ ~TestMessageStoreOC(){}
+
+ virtual void dequeue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& /*msg*/,
+ const PersistableQueue& /*queue*/)
+ {
+ if (error) throw Exception("Dequeue error test");
+ deqCnt++;
+ }
+
+ virtual void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& /* queue */)
+ {
+ if (error) throw Exception("Enqueue error test");
+ enqCnt++;
+ msg->enqueueComplete();
+ }
+
+ void createError()
+ {
+ error=true;
+ }
+
+ bool init(const Options*) { return true; }
+ void truncateInit(const bool) {}
+ void create(PersistableQueue& queue, const framing::FieldTable&) { queue.setPersistenceId(nextPersistenceId++); }
+ void destroy(PersistableQueue&) {}
+ void create(const PersistableExchange& exchange, const framing::FieldTable&) { exchange.setPersistenceId(nextPersistenceId++); }
+ void destroy(const PersistableExchange&) {}
+ void bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {}
+ void unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {}
+ void create(const PersistableConfig& config) { config.setPersistenceId(nextPersistenceId++); }
+ void destroy(const PersistableConfig&) {}
+ void stage(const boost::intrusive_ptr<PersistableMessage>&) {}
+ void destroy(PersistableMessage&) {}
+ void appendContent(const boost::intrusive_ptr<const PersistableMessage>&, const std::string&) {}
+ void loadContent(const qpid::broker::PersistableQueue&, const boost::intrusive_ptr<const PersistableMessage>&,
+ std::string&, uint64_t, uint32_t) { throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); }
+ void flush(const qpid::broker::PersistableQueue&) {}
+ uint32_t outstandingQueueAIO(const PersistableQueue&) { return 0; }
+
+ std::auto_ptr<TransactionContext> begin() { return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt()); }
+ std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) { return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid)); }
+ void prepare(TPCTransactionContext& ctxt) { prepared.insert(DummyCtxt::getXid(ctxt)); }
+ void commit(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); }
+ void abort(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); }
+ void collectPreparedXids(std::set<std::string>& out) { out.insert(prepared.begin(), prepared.end()); }
+
+ void recover(RecoveryManager&) {}
+};
+
+
+QPID_AUTO_TEST_CASE(testLVQOrdering){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg4 = create_message("e", "D");
+ intrusive_ptr<Message> received;
+
+ //set deliever match for LVQ a,b,c,a
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+
+ //enqueue 4 message
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+ queue->deliver(msg4);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg4.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg2.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg3.get(), received.get());
+
+ intrusive_ptr<Message> msg5 = create_message("e", "A");
+ intrusive_ptr<Message> msg6 = create_message("e", "B");
+ intrusive_ptr<Message> msg7 = create_message("e", "C");
+ msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg7->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ queue->deliver(msg5);
+ queue->deliver(msg6);
+ queue->deliver(msg7);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg5.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg6.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg7.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQEmptyKey){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQAcquire){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+ // disable flow control, as this test violates the enqueue/dequeue sequence.
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg4 = create_message("e", "D");
+ intrusive_ptr<Message> msg5 = create_message("e", "F");
+ intrusive_ptr<Message> msg6 = create_message("e", "G");
+
+ //set deliever match for LVQ a,b,c,a
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+
+ //enqueue 4 message
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+ queue->deliver(msg4);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ framing::SequenceNumber sequence(1);
+ QueuedMessage qmsg(queue.get(), msg1, sequence);
+ QueuedMessage qmsg2(queue.get(), msg2, ++sequence);
+ framing::SequenceNumber sequence1(10);
+ QueuedMessage qmsg3(queue.get(), 0, sequence1);
+
+ BOOST_CHECK(!queue->acquire(qmsg));
+ BOOST_CHECK(queue->acquire(qmsg2));
+ // Acquire the massage again to test failure case.
+ BOOST_CHECK(!queue->acquire(qmsg2));
+ BOOST_CHECK(!queue->acquire(qmsg3));
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+
+ queue->deliver(msg5);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ // set mode to no browse and check
+ args.setOrdering(client::LVQ_NO_BROWSE);
+ queue->configure(args);
+ TestConsumer::shared_ptr c1(new TestConsumer(false));
+
+ queue->dispatch(c1);
+ queue->dispatch(c1);
+ queue->dispatch(c1);
+
+ queue->deliver(msg6);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ intrusive_ptr<Message> received;
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg4.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQMultiQueue){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true ));
+ Queue::shared_ptr queue2(new Queue("my-queue", true ));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+ queue2->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "A");
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+
+ queue1->deliver(msg1);
+ queue2->deliver(msg1);
+ queue1->deliver(msg2);
+
+ received = queue1->get().payload;
+ BOOST_CHECK_EQUAL(msg2.get(), received.get());
+
+ received = queue2->get().payload;
+ BOOST_CHECK_EQUAL(msg1.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQRecover){
+
+/* simulate this
+ 1. start 2 nodes
+ 2. create cluster durable lvq
+ 3. send a transient message to the queue
+ 4. kill one of the nodes (to trigger force persistent behaviour)...
+ 5. then restart it (to turn off force persistent behaviour)
+ 6. send another transient message with same lvq key as in 3
+ 7. kill the second node again (retrigger force persistent)
+ 8. stop and recover the first node
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->create(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "A");
+ // 2
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ // 3
+ queue1->deliver(msg1);
+ // 4
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ // 5
+ queue1->clearLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ // 6
+ queue1->deliver(msg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+ BOOST_CHECK_EQUAL(testStore.deqCnt, 1u);
+}
+
+void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0)
+{
+ for (uint i = 0; i < count; i++) {
+ intrusive_ptr<Message> m = create_message("exchange", "key");
+ if (i % 2) {
+ if (oddTtl) m->getProperties<DeliveryProperties>()->setTtl(oddTtl);
+ } else {
+ if (evenTtl) m->getProperties<DeliveryProperties>()->setTtl(evenTtl);
+ }
+ m->setTimestamp(new broker::ExpiryPolicy);
+ queue.deliver(m);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testPurgeExpired) {
+ Queue queue("my-queue");
+ addMessagesToQueue(10, queue);
+ BOOST_CHECK_EQUAL(queue.getMessageCount(), 10u);
+ ::usleep(300*1000);
+ queue.purgeExpired();
+ BOOST_CHECK_EQUAL(queue.getMessageCount(), 5u);
+}
+
+QPID_AUTO_TEST_CASE(testQueueCleaner) {
+ Timer timer;
+ QueueRegistry queues;
+ Queue::shared_ptr queue = queues.declare("my-queue").first;
+ addMessagesToQueue(10, *queue, 200, 400);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u);
+
+ QueueCleaner cleaner(queues, timer);
+ cleaner.start(100 * qpid::sys::TIME_MSEC);
+ ::usleep(300*1000);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 5u);
+ ::usleep(300*1000);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
+
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("queue1", true, &testStore ));
+ queue1->create(args);
+ Queue::shared_ptr queue2(new Queue("queue2", true, &testStore ));
+ queue2->create(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+
+ queue1->deliver(msg1);
+ queue2->deliver(msg1);
+
+ //change mode
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+
+ // check they don't get stored twice
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ queue1->deliver(msg2);
+ queue2->deliver(msg2);
+
+ queue1->clearLastNodeFailure();
+ queue2->clearLastNodeFailure();
+ // check only new messages get forced
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 4u);
+
+ // check no failure messages are stored
+ queue1->clearLastNodeFailure();
+ queue2->clearLastNodeFailure();
+
+ intrusive_ptr<Message> msg3 = create_message("e", "B");
+ queue1->deliver(msg3);
+ queue2->deliver(msg3);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 4u);
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 6u);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg4 = create_message("e", "C");
+ intrusive_ptr<Message> msg5 = create_message("e", "D");
+
+ framing::SequenceNumber sequence(1);
+ QueuedMessage qmsg1(queue1.get(), msg4, sequence);
+ QueuedMessage qmsg2(queue2.get(), msg5, ++sequence);
+
+ queue1->requeue(qmsg1);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 7u);
+
+ // check requeue 2
+ queue2->clearLastNodeFailure();
+ queue2->requeue(qmsg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 7u);
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 8u);
+
+ queue2->clearLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 8u);
+}
+
+QPID_AUTO_TEST_CASE(testLastNodeRecoverAndFail){
+/*
+simulate this:
+ 1. start two nodes
+ 2. create cluster durable queue and add some messages
+ 3. kill one node (trigger force-persistent behaviour)
+ 4. stop and recover remaining node
+ 5. add another node
+ 6. kill that new node again
+make sure that an attempt to re-enqueue a message does not happen which will
+result in the last man standing exiting with an error.
+
+we need to make sure that recover is safe, i.e. messages are
+not requeued to the store.
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->create(args);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg1 = create_message("e", "C");
+ intrusive_ptr<Message> msg2 = create_message("e", "D");
+
+ queue1->recover(msg1);
+
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+ queue1->clearLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+ queue1->deliver(msg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+
+}
+
+QPID_AUTO_TEST_CASE(testLastNodeJournalError){
+/*
+simulate store exception going into last node standing
+
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg1 = create_message("e", "C");
+
+ queue1->deliver(msg1);
+ testStore.createError();
+
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+}
+
+intrusive_ptr<Message> mkMsg(MessageStore& store, std::string content = "", bool durable = false)
+{
+ intrusive_ptr<Message> msg = MessageUtils::createMessage("", "", durable);
+ if (content.size()) MessageUtils::addContent(msg, content);
+ msg->setStore(&store);
+ return msg;
+}
+
+QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){
+
+ TestMessageStoreOC testStore;
+ client::QueueOptions args0; // No size policy
+ client::QueueOptions args1;
+ args1.setSizePolicy(FLOW_TO_DISK, 0, 1);
+ client::QueueOptions args2;
+ args2.setSizePolicy(FLOW_TO_DISK, 0, 2);
+
+ // --- Fanout exchange bound to single transient queue -------------------------------------------------------------
+
+ FanOutExchange sbtFanout1("sbtFanout1", false, args0); // single binding to transient queue
+ Queue::shared_ptr tq1(new Queue("tq1", true)); // transient w/ limit
+ tq1->configure(args1);
+ sbtFanout1.bind(tq1, "", 0);
+
+ intrusive_ptr<Message> msg01 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg01(msg01);
+ sbtFanout1.route(dmsg01, "", 0); // Brings queue 1 to capacity limit
+ msg01->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg01->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg02 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg02(msg02);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException);
+ }
+ msg02->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg02->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg03 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg03(msg03);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException);
+ }
+ msg03->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg03->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg04 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg04(msg04);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException);
+ }
+ msg04->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg04->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg05 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg05(msg05);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException);
+ }
+ msg05->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg05->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ // --- Fanout exchange bound to single durable queue ---------------------------------------------------------------
+
+ FanOutExchange sbdFanout2("sbdFanout2", false, args0); // single binding to durable queue
+ Queue::shared_ptr dq2(new Queue("dq2", true, &testStore)); // durable w/ limit
+ dq2->configure(args1);
+ sbdFanout2.bind(dq2, "", 0);
+
+ intrusive_ptr<Message> msg06 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg06(msg06);
+ sbdFanout2.route(dmsg06, "", 0); // Brings queue 2 to capacity limit
+ msg06->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg06->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg07 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg07(msg07);
+ sbdFanout2.route(dmsg07, "", 0);
+ msg07->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg07->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(2u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg08 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg08(msg08);
+ sbdFanout2.route(dmsg08, "", 0);
+ msg08->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg08->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(3u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg09 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg09(msg09);
+ sbdFanout2.route(dmsg09, "", 0);
+ msg09->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg09->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(4u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg10 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg10(msg10);
+ sbdFanout2.route(dmsg10, "", 0);
+ msg10->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg10->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(5u, dq2->getMessageCount());
+
+ // --- Fanout exchange bound to multiple durable queues ------------------------------------------------------------
+
+ FanOutExchange mbdFanout3("mbdFanout3", false, args0); // multiple bindings to durable queues
+ Queue::shared_ptr dq3(new Queue("dq3", true, &testStore)); // durable w/ limit 2
+ dq3->configure(args2);
+ mbdFanout3.bind(dq3, "", 0);
+ Queue::shared_ptr dq4(new Queue("dq4", true, &testStore)); // durable w/ limit 1
+ dq4->configure(args1);
+ mbdFanout3.bind(dq4, "", 0);
+ Queue::shared_ptr dq5(new Queue("dq5", true, &testStore)); // durable no limit
+ dq5->configure(args0);
+ mbdFanout3.bind(dq5, "", 0);
+
+ intrusive_ptr<Message> msg11 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg11(msg11);
+ mbdFanout3.route(dmsg11, "", 0); // Brings queues 3 and 4 to capacity limit
+ msg11->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg11->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg12 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg12(msg12);
+ mbdFanout3.route(dmsg12, "", 0);
+ msg12->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg12->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point!
+ BOOST_CHECK_EQUAL(2u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg13 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg13(msg13);
+ mbdFanout3.route(dmsg13, "", 0);
+ msg13->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg13->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(3u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(3u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(3u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg14 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg14(msg14);
+ mbdFanout3.route(dmsg14, "", 0);
+ msg14->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg14->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point!
+ BOOST_CHECK_EQUAL(4u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(4u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(4u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg15 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg15(msg15);
+ mbdFanout3.route(dmsg15, "", 0);
+ msg15->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg15->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(5u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(5u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(5u, dq5->getMessageCount());
+
+ // Bind a transient queue, this should block the release of any further messages.
+ // Note: this will result in a violation of the count policy of dq3 and dq4 - but this
+ // is expected until a better overall multi-queue design is implemented. Similarly
+ // for the other tests in this section.
+
+ Queue::shared_ptr tq6(new Queue("tq6", true)); // transient no limit
+ tq6->configure(args0);
+ mbdFanout3.bind(tq6, "", 0);
+
+ intrusive_ptr<Message> msg16 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg16(msg16);
+ mbdFanout3.route(dmsg16, "", 0);
+ msg16->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg16->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(6u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(6u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(6u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg17 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg17(msg17);
+ mbdFanout3.route(dmsg17, "", 0);
+ msg17->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg17->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(7u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(7u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(7u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg18 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg18(msg18);
+ mbdFanout3.route(dmsg18, "", 0);
+ msg18->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg18->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(8u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(8u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(8u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg19 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg19(msg19);
+ mbdFanout3.route(dmsg19, "", 0);
+ msg19->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg19->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(9u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(9u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(9u, dq5->getMessageCount());
+
+
+ // --- Fanout exchange bound to multiple durable and transient queues ----------------------------------------------
+
+ FanOutExchange mbmFanout4("mbmFanout4", false, args0); // multiple bindings to durable/transient queues
+ Queue::shared_ptr dq7(new Queue("dq7", true, &testStore)); // durable no limit
+ dq7->configure(args0);
+ mbmFanout4.bind(dq7, "", 0);
+ Queue::shared_ptr dq8(new Queue("dq8", true, &testStore)); // durable w/ limit
+ dq8->configure(args1);
+ mbmFanout4.bind(dq8, "", 0);
+ Queue::shared_ptr tq9(new Queue("tq9", true)); // transient no limit
+ tq9->configure(args0);
+ mbmFanout4.bind(tq9, "", 0);
+
+ intrusive_ptr<Message> msg20 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg20(msg20);
+ mbmFanout4.route(dmsg20, "", 0); // Brings queue 7 to capacity limit
+ msg20->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg20->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq7->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq8->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg21 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg21(msg21);
+ mbmFanout4.route(dmsg21, "", 0);
+ msg21->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg21->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(2u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(2u, dq8->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg22 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg22(msg22);
+ mbmFanout4.route(dmsg22, "", 0);
+ msg22->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg22->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(3u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(3u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(3u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg23 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg23(msg23);
+ mbmFanout4.route(dmsg23, "", 0);
+ msg23->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg23->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(4u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(4u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(4u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg24 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg24(msg24);
+ mbmFanout4.route(dmsg24, "", 0);
+ msg24->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg24->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(5u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(5u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(5u, tq9->getMessageCount());
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/README.txt b/qpid/cpp/src/tests/README.txt
new file mode 100644
index 0000000000..0f4edee493
--- /dev/null
+++ b/qpid/cpp/src/tests/README.txt
@@ -0,0 +1,54 @@
+= Running Qpid C++ tests =
+
+General philosophy is that "make check" run all tests by default, but
+developers can run tests selectively as explained below.
+
+== Valgrind ==
+
+By default we run tests under valgrind to detect memory errors if valgrind
+is present. ./configure --disable-valgrind will disable it.
+
+Default valgrind options are specified in .valgrindrc-default, which a
+checked-in file. The actual options used are in .valgrindrc which is a
+local file. Normally it is a copy of valgrindrc-default but you can
+modify at will.
+
+Supressed errors are listed in .valgrind.supp. If you want to change
+suppressions for local testing, just modify .valgrindrc to point to a
+different file. Do NOT add suppressions to .valgrindrc.supp unless
+they are known problems outside of Qpid that can't reasonably be
+worked around in Qpid.
+
+
+== Unit Tests ==
+Unit tests use the boost test framework, and are compiled to rogram
+ unit_test
+There are several options to control how test results are displayed,
+see
+ http://www.boost.org/doc/libs/1_35_0/libs/test/doc/components/utf/parameters/index.html
+
+NOTE: some unit tests are written as CppUnit plug-ins, we are moving away
+from CppUnit so new tests should use the boost framework.
+
+CppUnit tests are run by the script run-unit-tests.
+
+== System Tests ==
+
+System tests are self contained AMQP client executables or scripts.
+They are listed in the TESTS make variable, which can be over-ridden.
+
+The ./start_broker "test" launches the broker, ./stop_broker" stops it.
+Tests in between assume the broker is running.
+
+./python_tests: runs ../python/run_tests. This is the main set of
+system testss for the broker.
+
+Other C++ client test executables and scripts under client/test are
+system tests for the client.
+
+By setting TESTS in a make command you can run a different subset of tests
+against an already-running broker.
+
+
+
+
diff --git a/qpid/cpp/src/tests/RangeSet.cpp b/qpid/cpp/src/tests/RangeSet.cpp
new file mode 100644
index 0000000000..db3a964086
--- /dev/null
+++ b/qpid/cpp/src/tests/RangeSet.cpp
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/RangeSet.h"
+
+using namespace std;
+using namespace qpid;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RangeSetTestSuite)
+
+typedef qpid::Range<int> TestRange;
+typedef qpid::RangeSet<int> TestRangeSet;
+
+QPID_AUTO_TEST_CASE(testEmptyRange) {
+ TestRange r;
+ BOOST_CHECK(r.empty());
+ BOOST_CHECK(!r.contains(0));
+ // BOOST_CHECK(r.contiguous(0));
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddPoint) {
+ TestRangeSet r;
+ BOOST_CHECK(r.empty());
+ r += 3;
+ BOOST_CHECK_MESSAGE(r.contains(3), r);
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r);
+ BOOST_CHECK(!r.empty());
+ r += 5;
+ BOOST_CHECK_MESSAGE(r.contains(5), r);
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r);
+ BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r);
+ r += 4;
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r);
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddRange) {
+ TestRangeSet r;
+ r += TestRange(0,3);
+ BOOST_CHECK(r.contains(TestRange(0,3)));
+ r += TestRange(4,6);
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(4,6)), r);
+ r += 3;
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(0,6)), r);
+ BOOST_CHECK(r.front() == 0);
+ BOOST_CHECK(r.back() == 6);
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddSet) {
+ TestRangeSet r;
+ TestRangeSet s = TestRangeSet(0,3)+TestRange(5,10);
+ r += s;
+ BOOST_CHECK_EQUAL(r,s);
+ r += TestRangeSet(3,5) + TestRange(7,12) + 15;
+ BOOST_CHECK_EQUAL(r, TestRangeSet(0,12) + 15);
+
+ r.clear();
+ BOOST_CHECK(r.empty());
+ r += TestRange::makeClosed(6,10);
+ BOOST_CHECK_EQUAL(r, TestRangeSet(6,11));
+ r += TestRangeSet(2,6)+8;
+ BOOST_CHECK_EQUAL(r, TestRangeSet(2,11));
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetIterate) {
+ TestRangeSet r;
+ (((r += 1) += 10) += TestRange(4,7)) += 2;
+ BOOST_MESSAGE(r);
+ std::vector<int> actual;
+ std::copy(r.begin(), r.end(), std::back_inserter(actual));
+ std::vector<int> expect = boost::assign::list_of(1)(2)(4)(5)(6)(10);
+ BOOST_CHECK_EQUAL(expect, actual);
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetRemove) {
+ // points
+ BOOST_CHECK_EQUAL(TestRangeSet(0,5)-3, TestRangeSet(0,3)+TestRange(4,5));
+ BOOST_CHECK_EQUAL(TestRangeSet(1,5)-5, TestRangeSet(1,5));
+ BOOST_CHECK_EQUAL(TestRangeSet(1,5)-0, TestRangeSet(1,5));
+
+ TestRangeSet r(TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,25));
+
+ // TestRanges
+ BOOST_CHECK_EQUAL(r-TestRange(0,5), TestRangeSet(10,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(10,15), TestRangeSet(0,5)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(20,25), TestRangeSet(0,5)+TestRange(10,15));
+
+ BOOST_CHECK_EQUAL(r-TestRange(-5, 30), TestRangeSet());
+
+ BOOST_CHECK_EQUAL(r-TestRange(-5, 7), TestRangeSet(10,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(8,19), TestRangeSet(0,5)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15));
+ BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15));
+
+ BOOST_CHECK_EQUAL(r-TestRange(-5, 5), TestRangeSet(10,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(10,19), TestRangeSet(0,5)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(18,25), TestRangeSet(0,5)+TestRange(10,15));
+ BOOST_CHECK_EQUAL(r-TestRange(23,25), TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,23));
+
+ BOOST_CHECK_EQUAL(r-TestRange(-3, 3), TestRangeSet(3,5)+TestRange(10,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(3, 7), TestRangeSet(0,2)+TestRange(10,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(3, 12), TestRangeSet(0,3)+TestRange(12,15)+TestRange(20,25));
+ BOOST_CHECK_EQUAL(r-TestRange(3, 22), TestRangeSet(12,15)+TestRange(22,25));
+ BOOST_CHECK_EQUAL(r-TestRange(12, 22), TestRangeSet(0,5)+TestRange(10,11)+TestRange(22,25));
+
+ // Sets
+ BOOST_CHECK_EQUAL(r-(TestRangeSet(-1,6)+TestRange(11,14)+TestRange(23,25)),
+ TestRangeSet(10,11)+TestRange(14,15)+TestRange(20,23));
+}
+
+QPID_AUTO_TEST_CASE(testRangeContaining) {
+ TestRangeSet r;
+ (((r += 1) += TestRange(3,5)) += 7);
+ BOOST_CHECK_EQUAL(r.rangeContaining(0), TestRange(0,0));
+ BOOST_CHECK_EQUAL(r.rangeContaining(1), TestRange(1,2));
+ BOOST_CHECK_EQUAL(r.rangeContaining(2), TestRange(2,2));
+ BOOST_CHECK_EQUAL(r.rangeContaining(3), TestRange(3,5));
+ BOOST_CHECK_EQUAL(r.rangeContaining(4), TestRange(3,5));
+ BOOST_CHECK_EQUAL(r.rangeContaining(5), TestRange(5,5));
+ BOOST_CHECK_EQUAL(r.rangeContaining(6), TestRange(6,6));
+ BOOST_CHECK_EQUAL(r.rangeContaining(7), TestRange(7,8));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/RateFlowcontrolTest.cpp b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp
new file mode 100644
index 0000000000..80ad06af8c
--- /dev/null
+++ b/qpid/cpp/src/tests/RateFlowcontrolTest.cpp
@@ -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.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/broker/RateFlowcontrol.h"
+#include "qpid/sys/Time.h"
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RateFlowcontrolTestSuite)
+
+QPID_AUTO_TEST_CASE(RateFlowcontrolTest)
+{
+ // BOOST_CHECK(predicate);
+ // BOOST_CHECK_EQUAL(a, b);
+
+ RateFlowcontrol fc(100);
+ AbsTime n=AbsTime::now();
+
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U );
+
+ fc.sentCredit(n, 0);
+
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U );
+ fc.sentCredit(n, 50);
+
+ Duration d=250*TIME_MSEC;
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 25), 0U );
+ BOOST_CHECK_EQUAL( fc.availableCredit(n), 25U );
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 23), 48U );
+ BOOST_CHECK_EQUAL( fc.availableCredit(n), 48U );
+ fc.sentCredit(n, 48);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 50), 0U);
+ BOOST_CHECK(fc.flowStopped());
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 25U);
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 50U);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp
new file mode 100644
index 0000000000..e4c1da5696
--- /dev/null
+++ b/qpid/cpp/src/tests/RefCounted.cpp
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/RefCounted.h"
+#include <boost/intrusive_ptr.hpp>
+
+#include "unit_test.h"
+
+QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite)
+
+using boost::intrusive_ptr;
+using namespace std;
+using namespace qpid;
+
+namespace qpid {
+namespace tests {
+
+struct CountMe : public RefCounted {
+ static int instances;
+ CountMe() { ++instances; }
+ ~CountMe() { --instances; }
+};
+
+int CountMe::instances=0;
+
+QPID_AUTO_TEST_CASE(testRefCounted) {
+ BOOST_CHECK_EQUAL(0, CountMe::instances);
+ intrusive_ptr<CountMe> p(new CountMe());
+ BOOST_CHECK_EQUAL(1, CountMe::instances);
+ intrusive_ptr<CountMe> q(p);
+ BOOST_CHECK_EQUAL(1, CountMe::instances);
+ q=0;
+ BOOST_CHECK_EQUAL(1, CountMe::instances);
+ p=0;
+ BOOST_CHECK_EQUAL(0, CountMe::instances);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ReplicationTest.cpp b/qpid/cpp/src/tests/ReplicationTest.cpp
new file mode 100644
index 0000000000..7310a3fe20
--- /dev/null
+++ b/qpid/cpp/src/tests/ReplicationTest.cpp
@@ -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.
+ *
+ */
+#include "unit_test.h"
+#include "test_tools.h"
+#include "config.h"
+#include "BrokerFixture.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/replication/constants.h"
+#include "qpid/sys/Shlib.h"
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include <boost/assign.hpp>
+#include <boost/bind.hpp>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::replication::constants;
+using boost::assign::list_of;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ReplicationTestSuite)
+
+// FIXME aconway 2009-11-26: clean this up.
+// The CMake-based build passes in the module suffix; if it's not there, this
+// is a Linux/UNIX libtool-based build.
+#if defined (QPID_MODULE_PREFIX) && defined (QPID_MODULE_SUFFIX)
+static const char *default_shlib =
+ QPID_MODULE_PREFIX "replicating_listener" QPID_MODULE_POSTFIX QPID_MODULE_SUFFIX;
+#else
+static const char *default_shlib = ".libs/replicating_listener.so";
+#endif
+qpid::sys::Shlib plugin(getLibPath("REPLICATING_LISTENER_LIB", default_shlib));
+
+qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args)
+{
+ std::vector<const char*> argv(args.size());
+ transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
+
+ qpid::broker::Broker::Options opts;
+ qpid::Plugin::addOptions(opts);
+ opts.parse(argv.size(), &argv[0], "", true);
+ return opts;
+}
+
+QPID_AUTO_TEST_CASE(testReplicationExchange)
+{
+ qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<string>("qpidd")
+ ("--replication-exchange-name=qpid.replication")));
+ ProxySessionFixture f(brokerOpts);
+
+
+ std::string dataQ("queue-1");
+ std::string eventQ("event-queue-1");
+ std::string dataQ2("queue-2");
+ std::string eventQ2("event-queue-2");
+ FieldTable eventQopts;
+ eventQopts.setString("qpid.insert_sequence_numbers", REPLICATION_EVENT_SEQNO);
+
+ f.session.queueDeclare(arg::queue=eventQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts);
+ f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ, arg::bindingKey=dataQ);
+
+ f.session.queueDeclare(arg::queue=eventQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts);
+ f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ2, arg::bindingKey=dataQ2);
+
+ QueueOptions args;
+ args.enableQueueEvents(false);
+ f.session.queueDeclare(arg::queue=dataQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ f.session.queueDeclare(arg::queue=dataQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ));
+ f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ2));
+ }
+ Message msg;
+ LocalQueue incoming;
+ Subscription sub = f.subs.subscribe(incoming, dataQ);
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, dataQ));
+
+ sub.cancel();
+ sub = f.subs.subscribe(incoming, eventQ);
+ //check that we received enqueue events for first queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ //check that we received dequeue events for first queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), DEQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(DEQUEUED_MESSAGE_POSITION), (i+1));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+11));
+ }
+
+ sub.cancel();
+ sub = f.subs.subscribe(incoming, eventQ2);
+ //check that we received enqueue events for second queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ2);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/RetryList.cpp b/qpid/cpp/src/tests/RetryList.cpp
new file mode 100644
index 0000000000..50cd5edfe8
--- /dev/null
+++ b/qpid/cpp/src/tests/RetryList.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/broker/RetryList.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RetryListTestSuite)
+
+struct RetryListFixture
+{
+ RetryList list;
+ std::vector<Url> urls;
+ std::vector<Address> expected;
+
+ void addUrl(const std::string& s)
+ {
+ urls.push_back(Url(s));
+ }
+
+ void addExpectation(const std::string& host, uint16_t port)
+ {
+ expected.push_back(Address("tcp", host, port));
+ }
+
+ void check()
+ {
+ list.reset(urls);
+ for (int t = 0; t < 2; t++) {
+ Address next;
+ for (std::vector<Address>::const_iterator i = expected.begin(); i != expected.end(); ++i) {
+ BOOST_CHECK(list.next(next));
+ BOOST_CHECK_EQUAL(i->host, next.host);
+ BOOST_CHECK_EQUAL(i->port, next.port);
+ }
+ BOOST_CHECK(!list.next(next));
+ }
+ }
+};
+
+QPID_AUTO_TEST_CASE(testWithSingleAddress)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host:5673");
+ test.addExpectation("host", 5673);
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host1,host2:2222,tcp:host3:5673,host4:1");
+
+ test.addExpectation("host1", 5672);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:my-host");
+ test.addUrl("amqp:host1:6666,host2:2222,tcp:host3:5673,host4:1");
+ test.addUrl("amqp:host5,host6:2222,tcp:host7:5673");
+
+ test.addExpectation("my-host", 5672);
+ test.addExpectation("host1", 6666);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+ test.addExpectation("host5", 5672);
+ test.addExpectation("host6", 2222);
+ test.addExpectation("host7", 5673);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testEmptyList)
+{
+ RetryListFixture test;
+ test.check();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SequenceNumberTest.cpp b/qpid/cpp/src/tests/SequenceNumberTest.cpp
new file mode 100644
index 0000000000..f3c934e3ca
--- /dev/null
+++ b/qpid/cpp/src/tests/SequenceNumberTest.cpp
@@ -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.
+ *
+ */
+
+#include "unit_test.h"
+#include <iostream>
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceNumberSet.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap)
+{
+ BOOST_CHECK_EQUAL(gap, a - b);
+ BOOST_CHECK_EQUAL(-gap, b - a);
+
+ //increment until b wraps around
+ for (int i = 0; i < (gap + 2); i++, ++a, ++b) {
+ BOOST_CHECK_EQUAL(gap, a - b);
+ }
+ //keep incrementing until a also wraps around
+ for (int i = 0; i < (gap + 2); i++, ++a, ++b) {
+ BOOST_CHECK_EQUAL(gap, a - b);
+ }
+ //let b catch up and overtake
+ for (int i = 0; i < (gap*2); i++, ++b) {
+ BOOST_CHECK_EQUAL(gap - i, a - b);
+ BOOST_CHECK_EQUAL(i - gap, b - a);
+ }
+}
+
+void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap)
+{
+ //increment until b wraps around
+ for (int i = 0; i < (gap + 2); i++) {
+ BOOST_CHECK(++a < ++b);//test prefix
+ }
+ //keep incrementing until a also wraps around
+ for (int i = 0; i < (gap + 2); i++) {
+ BOOST_CHECK(a++ < b++);//test postfix
+ }
+ //let a 'catch up'
+ for (int i = 0; i < gap; i++) {
+ a++;
+ }
+ BOOST_CHECK(a == b);
+ BOOST_CHECK(++a > b);
+}
+
+
+QPID_AUTO_TEST_SUITE(SequenceNumberTestSuite)
+
+QPID_AUTO_TEST_CASE(testIncrementPostfix)
+{
+ SequenceNumber a;
+ SequenceNumber b;
+ BOOST_CHECK(!(a > b));
+ BOOST_CHECK(!(b < a));
+ BOOST_CHECK(a == b);
+
+ SequenceNumber c = a++;
+ BOOST_CHECK(a > b);
+ BOOST_CHECK(b < a);
+ BOOST_CHECK(a != b);
+ BOOST_CHECK(c < a);
+ BOOST_CHECK(a != c);
+
+ b++;
+ BOOST_CHECK(!(a > b));
+ BOOST_CHECK(!(b < a));
+ BOOST_CHECK(a == b);
+ BOOST_CHECK(c < b);
+ BOOST_CHECK(b != c);
+}
+
+QPID_AUTO_TEST_CASE(testIncrementPrefix)
+{
+ SequenceNumber a;
+ SequenceNumber b;
+ BOOST_CHECK(!(a > b));
+ BOOST_CHECK(!(b < a));
+ BOOST_CHECK(a == b);
+
+ SequenceNumber c = ++a;
+ BOOST_CHECK(a > b);
+ BOOST_CHECK(b < a);
+ BOOST_CHECK(a != b);
+ BOOST_CHECK(a == c);
+
+ ++b;
+ BOOST_CHECK(!(a > b));
+ BOOST_CHECK(!(b < a));
+ BOOST_CHECK(a == b);
+}
+
+QPID_AUTO_TEST_CASE(testWrapAround)
+{
+ const uint32_t max = 0xFFFFFFFF;
+ SequenceNumber a(max - 10);
+ SequenceNumber b(max - 5);
+ checkComparison(a, b, 5);
+
+ const uint32_t max_signed = 0x7FFFFFFF;
+ SequenceNumber c(max_signed - 10);
+ SequenceNumber d(max_signed - 5);
+ checkComparison(c, d, 5);
+}
+
+QPID_AUTO_TEST_CASE(testCondense)
+{
+ SequenceNumberSet set;
+ for (uint i = 0; i < 6; i++) {
+ set.push_back(SequenceNumber(i));
+ }
+ set.push_back(SequenceNumber(7));
+ for (uint i = 9; i < 13; i++) {
+ set.push_back(SequenceNumber(i));
+ }
+ set.push_back(SequenceNumber(13));
+ SequenceNumberSet actual = set.condense();
+
+ SequenceNumberSet expected;
+ expected.addRange(SequenceNumber(0), SequenceNumber(5));
+ expected.addRange(SequenceNumber(7), SequenceNumber(7));
+ expected.addRange(SequenceNumber(9), SequenceNumber(13));
+ BOOST_CHECK_EQUAL(expected, actual);
+}
+
+QPID_AUTO_TEST_CASE(testCondenseSingleRange)
+{
+ SequenceNumberSet set;
+ for (uint i = 0; i < 6; i++) {
+ set.push_back(SequenceNumber(i));
+ }
+ SequenceNumberSet actual = set.condense();
+
+ SequenceNumberSet expected;
+ expected.addRange(SequenceNumber(0), SequenceNumber(5));
+ BOOST_CHECK_EQUAL(expected, actual);
+}
+
+QPID_AUTO_TEST_CASE(testCondenseSingleItem)
+{
+ SequenceNumberSet set;
+ set.push_back(SequenceNumber(1));
+ SequenceNumberSet actual = set.condense();
+
+ SequenceNumberSet expected;
+ expected.addRange(SequenceNumber(1), SequenceNumber(1));
+ BOOST_CHECK_EQUAL(expected, actual);
+}
+
+QPID_AUTO_TEST_CASE(testDifference)
+{
+ SequenceNumber a;
+ SequenceNumber b;
+
+ for (int i = 0; i < 10; i++, ++a) {
+ BOOST_CHECK_EQUAL(i, a - b);
+ BOOST_CHECK_EQUAL(-i, b - a);
+ }
+
+ b = a;
+
+ for (int i = 0; i < 10; i++, ++b) {
+ BOOST_CHECK_EQUAL(-i, a - b);
+ BOOST_CHECK_EQUAL(i, b - a);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround1)
+{
+ const uint32_t max = 0xFFFFFFFF;
+ SequenceNumber a(max - 5);
+ SequenceNumber b(max - 10);
+ checkDifference(a, b, 5);
+}
+
+QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2)
+{
+ const uint32_t max_signed = 0x7FFFFFFF;
+ SequenceNumber c(max_signed - 5);
+ SequenceNumber d(max_signed - 10);
+ checkDifference(c, d, 5);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SequenceSet.cpp b/qpid/cpp/src/tests/SequenceSet.cpp
new file mode 100644
index 0000000000..bc0a8ea509
--- /dev/null
+++ b/qpid/cpp/src/tests/SequenceSet.cpp
@@ -0,0 +1,187 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/framing/SequenceSet.h"
+#include "unit_test.h"
+#include <list>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SequenceSetTestSuite)
+
+using namespace qpid::framing;
+
+struct RangeExpectations
+{
+ typedef std::pair<SequenceNumber, SequenceNumber> Range;
+ typedef std::list<Range> Ranges;
+
+ Ranges ranges;
+
+ RangeExpectations& expect(const SequenceNumber& start, const SequenceNumber& end) {
+ ranges.push_back(Range(start, end));
+ return *this;
+ }
+
+ void operator()(const SequenceNumber& start, const SequenceNumber& end) {
+ BOOST_CHECK(!ranges.empty());
+ if (!ranges.empty()) {
+ BOOST_CHECK_EQUAL(start, ranges.front().first);
+ BOOST_CHECK_EQUAL(end, ranges.front().second);
+ ranges.pop_front();
+ }
+ }
+
+ void check(SequenceSet& set) {
+ set.for_each(*this);
+ BOOST_CHECK(ranges.empty());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testAdd) {
+ SequenceSet s;
+ s.add(2);
+ s.add(8,8);
+ s.add(3,5);
+
+ for (uint32_t i = 0; i <= 1; i++)
+ BOOST_CHECK(!s.contains(i));
+
+ for (uint32_t i = 2; i <= 5; i++)
+ BOOST_CHECK(s.contains(i));
+
+ for (uint32_t i = 6; i <= 7; i++)
+ BOOST_CHECK(!s.contains(i));
+
+ BOOST_CHECK(s.contains(8));
+
+ for (uint32_t i = 9; i <= 10; i++)
+ BOOST_CHECK(!s.contains(i));
+
+ RangeExpectations().expect(2, 5).expect(8, 8).check(s);
+
+ SequenceSet t;
+ t.add(6, 10);
+ t.add(s);
+
+ for (uint32_t i = 0; i <= 1; i++)
+ BOOST_CHECK(!t.contains(i));
+
+ for (uint32_t i = 2; i <= 10; i++)
+ BOOST_CHECK_MESSAGE(t.contains(i), t << " contains " << i);
+
+ RangeExpectations().expect(2, 10).check(t);
+}
+
+QPID_AUTO_TEST_CASE(testAdd2) {
+ SequenceSet s;
+ s.add(7,6);
+ s.add(4,4);
+ s.add(3,10);
+ s.add(2);
+ RangeExpectations().expect(2, 10).check(s);
+}
+
+QPID_AUTO_TEST_CASE(testRemove) {
+ SequenceSet s;
+ SequenceSet t;
+ s.add(0, 10);
+ t.add(0, 10);
+
+ s.remove(7);
+ s.remove(3, 5);
+ s.remove(9, 10);
+
+ t.remove(s);
+
+ for (uint32_t i = 0; i <= 2; i++) {
+ BOOST_CHECK(s.contains(i));
+ BOOST_CHECK(!t.contains(i));
+ }
+
+ for (uint32_t i = 3; i <= 5; i++) {
+ BOOST_CHECK(!s.contains(i));
+ BOOST_CHECK(t.contains(i));
+ }
+
+ BOOST_CHECK(s.contains(6));
+ BOOST_CHECK(!t.contains(6));
+
+ BOOST_CHECK(!s.contains(7));
+ BOOST_CHECK(t.contains(7));
+
+ BOOST_CHECK(s.contains(8));
+ BOOST_CHECK(!t.contains(8));
+
+ for (uint32_t i = 9; i <= 10; i++) {
+ BOOST_CHECK(!s.contains(i));
+ BOOST_CHECK(t.contains(i));
+ }
+
+ RangeExpectations().expect(0, 2).expect(6, 6).expect(8, 8).check(s);
+ RangeExpectations().expect(3, 5).expect(7, 7).expect(9, 10).check(t);
+}
+
+
+QPID_AUTO_TEST_CASE(testOutOfOrderRemove) {
+
+ SequenceSet s(2, 20);
+
+ // test remove from middle:
+ s.remove(7);
+ RangeExpectations().expect(2, 6).expect(8, 20).check(s);
+ s.remove(14);
+ RangeExpectations().expect(2, 6).expect(8, 13).expect(15, 20).check(s);
+
+ // remove from front of subrange:
+ s.remove(8, 8);
+ RangeExpectations().expect(2, 6).expect(9, 13).expect(15, 20).check(s);
+
+ // remove from tail of subrange:
+ s.remove(6);
+ RangeExpectations().expect(2, 5).expect(9, 13).expect(15, 20).check(s);
+
+ // remove across subrange:
+ s.remove(13, 15);
+ RangeExpectations().expect(2, 5).expect(9, 12).expect(16, 20).check(s);
+
+ // remove within subrange:
+ s.remove(6, 8);
+ RangeExpectations().expect(2, 5).expect(9, 12).expect(16, 20).check(s);
+
+ // remove overlap subrange tail:
+ s.remove(11, 15);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(16, 20).check(s);
+
+ // remove overlap subrange head:
+ s.remove(14, 17);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(18, 20).check(s);
+
+ // remove overlap sequence tail:
+ s.remove(20, 22);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(18, 19).check(s);
+
+ // remove overlap sequence head:
+ s.remove(1, 3);
+ RangeExpectations().expect(4, 5).expect(9, 10).expect(18, 19).check(s);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp
new file mode 100644
index 0000000000..157cabfb63
--- /dev/null
+++ b/qpid/cpp/src/tests/SessionState.cpp
@@ -0,0 +1,304 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "unit_test.h"
+
+#include "qpid/SessionState.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/SessionFlushBody.h"
+
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <functional>
+#include <numeric>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SessionStateTestSuite)
+
+using namespace std;
+using namespace boost;
+using namespace qpid::framing;
+
+// ================================================================
+// Utility functions.
+
+// Apply f to [begin, end) and accumulate the result
+template <class Iter, class T, class F>
+T applyAccumulate(Iter begin, Iter end, T seed, const F& f) {
+ return std::accumulate(begin, end, seed, bind(std::plus<T>(), _1, bind(f, _2)));
+}
+
+// Create a frame with a one-char string.
+AMQFrame& frame(char s) {
+ static AMQFrame frame((AMQContentBody(string(&s, 1))));
+ return frame;
+}
+
+// Simple string representation of a frame.
+string str(const AMQFrame& f) {
+ if (f.getMethod()) return "C"; // Command or Control
+ const AMQContentBody* c = dynamic_cast<const AMQContentBody*>(f.getBody());
+ if (c) return c->getData(); // Return data for content frames.
+ return "H"; // Must be a header.
+}
+// Make a string from a range of frames.
+string str(const boost::iterator_range<vector<AMQFrame>::const_iterator>& frames) {
+ string (*strFrame)(const AMQFrame&) = str;
+ return applyAccumulate(frames.begin(), frames.end(), string(), ptr_fun(strFrame));
+}
+// Make a transfer command frame.
+AMQFrame transferFrame(bool hasContent) {
+ AMQFrame t((MessageTransferBody()));
+ t.setFirstFrame(true);
+ t.setLastFrame(true);
+ t.setFirstSegment(true);
+ t.setLastSegment(!hasContent);
+ return t;
+}
+// Make a content frame
+AMQFrame contentFrame(string content, bool isLast=true) {
+ AMQFrame f((AMQContentBody(content)));
+ f.setFirstFrame(true);
+ f.setLastFrame(true);
+ f.setFirstSegment(false);
+ f.setLastSegment(isLast);
+ return f;
+}
+AMQFrame contentFrameChar(char content, bool isLast=true) {
+ return contentFrame(string(1, content), isLast);
+}
+
+// Send frame & return size of frame.
+size_t send(qpid::SessionState& s, const AMQFrame& f) { s.senderRecord(f); return f.encodedSize(); }
+// Send transfer command with no content.
+size_t transfer0(qpid::SessionState& s) { return send(s, transferFrame(false)); }
+// Send transfer frame with single content frame.
+size_t transfer1(qpid::SessionState& s, string content) {
+ return send(s,transferFrame(true)) + send(s,contentFrame(content));
+}
+size_t transfer1Char(qpid::SessionState& s, char content) {
+ return transfer1(s, string(1,content));
+}
+
+// Send transfer frame with multiple single-byte content frames.
+size_t transferN(qpid::SessionState& s, string content) {
+ size_t size=send(s, transferFrame(!content.empty()));
+ if (!content.empty()) {
+ char last = content[content.size()-1];
+ content.resize(content.size()-1);
+ size += applyAccumulate(content.begin(), content.end(), 0,
+ bind(&send, ref(s),
+ bind(contentFrameChar, _1, false)));
+ size += send(s, contentFrameChar(last, true));
+ }
+ return size;
+}
+
+// Send multiple transfers with single-byte content.
+size_t transfers(qpid::SessionState& s, string content) {
+ return applyAccumulate(content.begin(), content.end(), 0,
+ bind(transfer1Char, ref(s), _1));
+}
+
+size_t contentFrameSize(size_t n=1) { return AMQFrame(( AMQContentBody())).encodedSize() + n; }
+size_t transferFrameSize() { return AMQFrame((MessageTransferBody())).encodedSize(); }
+
+// ==== qpid::SessionState test classes
+
+using qpid::SessionId;
+using qpid::SessionPoint;
+
+
+QPID_AUTO_TEST_CASE(testSendGetReplyList) {
+ qpid::SessionState s;
+ s.setTimeout(1);
+ s.senderGetCommandPoint();
+ transfer1(s, "abc");
+ transfers(s, "def");
+ transferN(s, "xyz");
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))),"CabcCdCeCfCxyz");
+ // Ignore controls.
+ s.senderRecord(AMQFrame(new SessionFlushBody()));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz");
+}
+
+QPID_AUTO_TEST_CASE(testNeedFlush) {
+ qpid::SessionState::Configuration c;
+ // sync after 2 1-byte transfers or equivalent bytes.
+ c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize());
+ qpid::SessionState s(SessionId(), c);
+ s.setTimeout(1);
+ s.senderGetCommandPoint();
+ transfers(s, "a");
+ BOOST_CHECK(!s.senderNeedFlush());
+ transfers(s, "b");
+ BOOST_CHECK(s.senderNeedFlush());
+ s.senderRecordFlush();
+ BOOST_CHECK(!s.senderNeedFlush());
+ transfers(s, "c");
+ BOOST_CHECK(!s.senderNeedFlush());
+ transfers(s, "d");
+ BOOST_CHECK(s.senderNeedFlush());
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint())), "CaCbCcCd");
+}
+
+QPID_AUTO_TEST_CASE(testPeerConfirmed) {
+ qpid::SessionState::Configuration c;
+ // sync after 2 1-byte transfers or equivalent bytes.
+ c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize());
+ qpid::SessionState s(SessionId(), c);
+ s.setTimeout(1);
+ s.senderGetCommandPoint();
+ transfers(s, "ab");
+ BOOST_CHECK(s.senderNeedFlush());
+ transfers(s, "cd");
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCcCd");
+ s.senderConfirmed(SessionPoint(3));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "Cd");
+ BOOST_CHECK(!s.senderNeedFlush());
+
+ // Multi-frame transfer.
+ transfer1(s, "efg");
+ transfers(s, "xy");
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "CdCefgCxCy");
+ BOOST_CHECK(s.senderNeedFlush());
+
+ s.senderConfirmed(SessionPoint(4));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CefgCxCy");
+ BOOST_CHECK(s.senderNeedFlush());
+
+ s.senderConfirmed(SessionPoint(5));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(5,0))), "CxCy");
+ BOOST_CHECK(s.senderNeedFlush());
+
+ s.senderConfirmed(SessionPoint(6));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(6,0))), "Cy");
+ BOOST_CHECK(!s.senderNeedFlush());
+}
+
+QPID_AUTO_TEST_CASE(testPeerCompleted) {
+ qpid::SessionState s;
+ s.setTimeout(1);
+ s.senderGetCommandPoint();
+ // Completion implies confirmation
+ transfers(s, "abc");
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc");
+ SequenceSet set(SequenceSet() + 0 + 1);
+ s.senderCompleted(set);
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))), "Cc");
+
+ transfers(s, "def");
+ // We dont do out-of-order confirmation, so this will only confirm up to 3:
+ set = SequenceSet(SequenceSet() + 2 + 3 + 5);
+ s.senderCompleted(set);
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf");
+}
+
+QPID_AUTO_TEST_CASE(testReceive) {
+ // Advance expected/received correctly
+ qpid::SessionState s;
+ s.receiverSetCommandPoint(SessionPoint());
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(0));
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(0));
+
+ BOOST_CHECK(s.receiverRecord(transferFrame(false)));
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(1));
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(1));
+
+ BOOST_CHECK(s.receiverRecord(transferFrame(true)));
+ SessionPoint point = SessionPoint(1, transferFrameSize());
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), point);
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), point);
+ BOOST_CHECK(s.receiverRecord(contentFrame("", false)));
+ point.offset += contentFrameSize(0);
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), point);
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), point);
+ BOOST_CHECK(s.receiverRecord(contentFrame("", true)));
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2));
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2));
+
+ // Idempotence barrier, rewind expected & receive some duplicates.
+ s.receiverSetCommandPoint(SessionPoint(1));
+ BOOST_CHECK(!s.receiverRecord(transferFrame(false)));
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2));
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2));
+ BOOST_CHECK(s.receiverRecord(transferFrame(false)));
+ BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(3));
+ BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(3));
+}
+
+QPID_AUTO_TEST_CASE(testCompleted) {
+ // completed & unknownCompleted
+ qpid::SessionState s;
+ s.receiverSetCommandPoint(SessionPoint());
+ s.receiverRecord(transferFrame(false));
+ s.receiverRecord(transferFrame(false));
+ s.receiverRecord(transferFrame(false));
+ s.receiverCompleted(1);
+ BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+1));
+ s.receiverCompleted(0);
+ BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(),
+ SequenceSet(SequenceSet() + qpid::Range<SequenceNumber>(0,2)));
+ s.receiverKnownCompleted(SequenceSet(SequenceSet()+1));
+ BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+2));
+ // TODO aconway 2008-04-30: missing tests for known-completed.
+}
+
+QPID_AUTO_TEST_CASE(testNeedKnownCompleted) {
+ size_t flushInterval= 2*(transferFrameSize()+contentFrameSize())+1;
+ qpid::SessionState::Configuration c(flushInterval);
+ qpid::SessionState s(qpid::SessionId(), c);
+ s.senderGetCommandPoint();
+ transfers(s, "a");
+ SequenceSet set(SequenceSet() + 0);
+ s.senderCompleted(set);
+ BOOST_CHECK(!s.senderNeedKnownCompleted());
+
+ transfers(s, "b");
+ set += 1;
+ s.senderCompleted(set);
+ BOOST_CHECK(!s.senderNeedKnownCompleted());
+
+ transfers(s, "c");
+ set += 2;
+ s.senderCompleted(set);
+ BOOST_CHECK(s.senderNeedKnownCompleted());
+ s.senderRecordKnownCompleted();
+ BOOST_CHECK(!s.senderNeedKnownCompleted());
+
+ transfers(s, "de");
+ set += 3;
+ set += 4;
+ s.senderCompleted(set);
+ BOOST_CHECK(!s.senderNeedKnownCompleted());
+
+ transfers(s, "f");
+ set += 2;
+ s.senderCompleted(set);
+ BOOST_CHECK(s.senderNeedKnownCompleted());
+ s.senderRecordKnownCompleted();
+ BOOST_CHECK(!s.senderNeedKnownCompleted());
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Shlib.cpp b/qpid/cpp/src/tests/Shlib.cpp
new file mode 100644
index 0000000000..d8ad4c14d8
--- /dev/null
+++ b/qpid/cpp/src/tests/Shlib.cpp
@@ -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.
+ *
+ */
+
+#include "test_tools.h"
+#include "config.h"
+#include "qpid/sys/Shlib.h"
+#include "qpid/Exception.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ShlibTestSuite)
+
+using namespace qpid::sys;
+typedef void (*CallMe)(int*);
+
+
+QPID_AUTO_TEST_CASE(testShlib) {
+ // The CMake-based build passes in the shared lib suffix; if it's not
+ // there, this is a Linux/UNIX libtool-based build.
+#if defined (QPID_SHLIB_PREFIX) && defined (QPID_SHLIB_SUFFIX)
+ Shlib sh("./" QPID_SHLIB_PREFIX "shlibtest" QPID_SHLIB_POSTFIX QPID_SHLIB_SUFFIX);
+#else
+ Shlib sh(".libs/libshlibtest.so");
+#endif
+ // Double cast to avoid ISO warning.
+ CallMe callMe=sh.getSymbol<CallMe>("callMe");
+ BOOST_REQUIRE(callMe != 0);
+ int unloaded=0;
+ callMe(&unloaded);
+ sh.unload();
+ BOOST_CHECK_EQUAL(42, unloaded);
+ try {
+ sh.getSymbol("callMe");
+ BOOST_FAIL("Expected exception");
+ }
+ catch (const qpid::Exception&) {}
+}
+
+QPID_AUTO_TEST_CASE(testAutoShlib) {
+ int unloaded = 0;
+ {
+#if defined (QPID_SHLIB_PREFIX) && defined (QPID_SHLIB_SUFFIX)
+ AutoShlib sh("./" QPID_SHLIB_PREFIX "shlibtest" QPID_SHLIB_POSTFIX QPID_SHLIB_SUFFIX);
+#else
+ AutoShlib sh(".libs/libshlibtest.so");
+#endif
+ CallMe callMe=sh.getSymbol<CallMe>("callMe");
+ BOOST_REQUIRE(callMe != 0);
+ callMe(&unloaded);
+ }
+ BOOST_CHECK_EQUAL(42, unloaded);
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SocketProxy.h b/qpid/cpp/src/tests/SocketProxy.h
new file mode 100644
index 0000000000..d195f11aa9
--- /dev/null
+++ b/qpid/cpp/src/tests/SocketProxy.h
@@ -0,0 +1,183 @@
+#ifndef SOCKETPROXY_H
+#define SOCKETPROXY_H
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IOHandle.h"
+#ifdef _WIN32
+# include "qpid/sys/windows/IoHandlePrivate.h"
+ typedef SOCKET FdType;
+#else
+# include "qpid/sys/posix/PrivatePosix.h"
+ typedef int FdType;
+#endif
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace tests {
+
+/**
+ * A simple socket proxy that forwards to another socket.
+ * Used between client & local broker to simulate network failures.
+ */
+class SocketProxy : private qpid::sys::Runnable
+{
+ // Need a Socket we can get the fd from
+ class LowSocket : public qpid::sys::Socket {
+ public:
+#ifdef _WIN32
+ FdType getFd() { return toSocketHandle(*this); }
+#else
+ FdType getFd() { return toFd(impl); }
+#endif
+ };
+
+ public:
+ /** Connect to connectPort on host, start a forwarding thread.
+ * Listen for connection on getPort().
+ */
+ SocketProxy(int connectPort, const std::string host="localhost")
+ : closed(false), joined(true),
+ port(listener.listen()), dropClient(), dropServer()
+ {
+ client.connect(host, boost::lexical_cast<std::string>(connectPort));
+ joined = false;
+ thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this));
+ }
+
+ ~SocketProxy() { close(); if (!joined) thread.join(); }
+
+ /** Simulate a network disconnect. */
+ void close() {
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ if (closed) { return; }
+ closed=true;
+ }
+ if (thread && thread != qpid::sys::Thread::current()) {
+ thread.join();
+ joined = true;
+ }
+ client.close();
+ }
+
+ /** Simulate lost packets, drop data from client */
+ void dropClientData(bool drop=true) { dropClient=drop; }
+
+ /** Simulate lost packets, drop data from server */
+ void dropServerData(bool drop=true) { dropServer=drop; }
+
+ bool isClosed() const {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return closed;
+ }
+
+ uint16_t getPort() const { return port; }
+
+ private:
+ static void throwErrno(const std::string& msg) {
+ throw qpid::Exception(msg+":"+qpid::sys::strError(errno));
+ }
+ static void throwIf(bool condition, const std::string& msg) {
+ if (condition) throw qpid::Exception(msg);
+ }
+
+ void run() {
+ std::auto_ptr<LowSocket> server;
+ try {
+ fd_set socks;
+ FdType maxFd = listener.getFd();
+ struct timeval tmo;
+ for (;;) {
+ FD_ZERO(&socks);
+ FD_SET(maxFd, &socks);
+ tmo.tv_sec = 0;
+ tmo.tv_usec = 500 * 1000;
+ if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ throwIf(closed, "SocketProxy: Closed by close()");
+ continue;
+ }
+ throwIf(!FD_ISSET(maxFd, &socks), "SocketProxy: Accept failed");
+ break; // Accept ready... go to next step
+ }
+ server.reset(reinterpret_cast<LowSocket *>(listener.accept()));
+ maxFd = server->getFd();
+ if (client.getFd() > maxFd)
+ maxFd = client.getFd();
+ char buffer[1024];
+ for (;;) {
+ FD_ZERO(&socks);
+ tmo.tv_sec = 0;
+ tmo.tv_usec = 500 * 1000;
+ FD_SET(client.getFd(), &socks);
+ FD_SET(server->getFd(), &socks);
+ if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ throwIf(closed, "SocketProxy: Closed by close()");
+ continue;
+ }
+ // Something is set; relay data as needed until something closes
+ if (FD_ISSET(server->getFd(), &socks)) {
+ int n = server->read(buffer, sizeof(buffer));
+ throwIf(n <= 0, "SocketProxy: server disconnected");
+ if (!dropServer) client.write(buffer, n);
+ }
+ if (FD_ISSET(client.getFd(), &socks)) {
+ int n = client.read(buffer, sizeof(buffer));
+ throwIf(n <= 0, "SocketProxy: client disconnected");
+ if (!dropServer) server->write(buffer, n);
+ }
+ if (!FD_ISSET(client.getFd(), &socks) &&
+ !FD_ISSET(server->getFd(), &socks))
+ throwIf(true, "SocketProxy: No handle ready");
+ }
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(debug, "SocketProxy::run exception: " << e.what());
+ }
+ try {
+ if (server.get()) server->close();
+ close();
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what());
+ }
+ }
+
+ mutable qpid::sys::Mutex lock;
+ mutable bool closed;
+ bool joined;
+ LowSocket client, listener;
+ uint16_t port;
+ qpid::sys::Thread thread;
+ bool dropClient, dropServer;
+};
+
+}} // namespace qpid::tests
+
+#endif
diff --git a/qpid/cpp/src/tests/Statistics.cpp b/qpid/cpp/src/tests/Statistics.cpp
new file mode 100644
index 0000000000..19531762b1
--- /dev/null
+++ b/qpid/cpp/src/tests/Statistics.cpp
@@ -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.
+ *
+ */
+#include "Statistics.h"
+#include <qpid/messaging/Message.h>
+#include <ostream>
+#include <iomanip>
+
+namespace qpid {
+namespace tests {
+
+using namespace std;
+
+Statistic::~Statistic() {}
+
+Throughput::Throughput() : messages(0), started(false) {}
+
+void Throughput::message(const messaging::Message&) {
+ ++messages;
+ if (!started) {
+ start = sys::now();
+ started = true;
+ }
+}
+
+void Throughput::header(ostream& o) const {
+ o << "tp(m/s)";
+}
+
+void Throughput::report(ostream& o) const {
+ double elapsed(int64_t(sys::Duration(start, sys::now()))/double(sys::TIME_SEC));
+ o << fixed << setprecision(0) << messages/elapsed;
+}
+
+ThroughputAndLatency::ThroughputAndLatency() :
+ total(0),
+ min(numeric_limits<double>::max()),
+ max(numeric_limits<double>::min()),
+ samples(0)
+{}
+
+const std::string TS = "ts";
+
+void ThroughputAndLatency::message(const messaging::Message& m) {
+ Throughput::message(m);
+ types::Variant::Map::const_iterator i = m.getProperties().find(TS);
+ if (i != m.getProperties().end()) {
+ ++samples;
+ int64_t start(i->second.asInt64());
+ int64_t end(sys::Duration(sys::EPOCH, sys::now()));
+ double latency = double(end - start)/sys::TIME_MSEC;
+ if (latency > 0) {
+ total += latency;
+ if (latency < min) min = latency;
+ if (latency > max) max = latency;
+ }
+ }
+}
+
+void ThroughputAndLatency::header(ostream& o) const {
+ Throughput::header(o);
+ o << '\t' << "l-min" << '\t' << "l-max" << '\t' << "l-avg";
+}
+
+void ThroughputAndLatency::report(ostream& o) const {
+ Throughput::report(o);
+ if (samples) {
+ o << fixed << setprecision(2)
+ << '\t' << min << '\t' << max << '\t' << total/samples;
+ }
+}
+
+ReporterBase::ReporterBase(ostream& o, int batch, bool wantHeader)
+ : batchSize(batch), batchCount(0), headerPrinted(!wantHeader), out(o)
+{}
+
+ReporterBase::~ReporterBase() {}
+
+/** Count message in the statistics */
+void ReporterBase::message(const messaging::Message& m) {
+ if (!overall.get()) overall = create();
+ overall->message(m);
+ if (batchSize) {
+ if (!batch.get()) batch = create();
+ batch->message(m);
+ if (++batchCount == batchSize) {
+ header();
+ batch->report(out);
+ out << endl;
+ batch = create();
+ batchCount = 0;
+ }
+ }
+}
+
+/** Print overall report. */
+void ReporterBase::report() {
+ if (!overall.get()) overall = create();
+ header();
+ overall->report(out);
+ out << endl;
+}
+
+void ReporterBase::header() {
+ if (!headerPrinted) {
+ if (!overall.get()) overall = create();
+ overall->header(out);
+ out << endl;
+ headerPrinted = true;
+ }
+}
+
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Statistics.h b/qpid/cpp/src/tests/Statistics.h
new file mode 100644
index 0000000000..091046a17f
--- /dev/null
+++ b/qpid/cpp/src/tests/Statistics.h
@@ -0,0 +1,111 @@
+#ifndef TESTS_STATISTICS_H
+#define TESTS_STATISTICS_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include <qpid/sys/Time.h>
+#include <limits>
+#include <iosfwd>
+#include <memory>
+
+namespace qpid {
+
+namespace messaging {
+class Message;
+}
+
+namespace tests {
+
+class Statistic {
+ public:
+ virtual ~Statistic();
+ virtual void message(const messaging::Message&) = 0;
+ virtual void report(std::ostream&) const = 0;
+ virtual void header(std::ostream&) const = 0;
+};
+
+class Throughput : public Statistic {
+ public:
+ Throughput();
+ virtual void message(const messaging::Message&);
+ virtual void report(std::ostream&) const;
+ virtual void header(std::ostream&) const;
+
+ protected:
+ int messages;
+
+ private:
+ bool started;
+ sys::AbsTime start;
+};
+
+class ThroughputAndLatency : public Throughput {
+ public:
+ ThroughputAndLatency();
+ virtual void message(const messaging::Message&);
+ virtual void report(std::ostream&) const;
+ virtual void header(std::ostream&) const;
+
+ private:
+ double total, min, max; // Milliseconds
+ int samples;
+};
+
+/** Report batch and overall statistics */
+class ReporterBase {
+ public:
+ virtual ~ReporterBase();
+
+ /** Count message in the statistics */
+ void message(const messaging::Message& m);
+
+ /** Print overall report. */
+ void report();
+
+ protected:
+ ReporterBase(std::ostream& o, int batchSize, bool wantHeader);
+ virtual std::auto_ptr<Statistic> create() = 0;
+
+ private:
+ void header();
+ void report(const Statistic& s);
+ std::auto_ptr<Statistic> overall;
+ std::auto_ptr<Statistic> batch;
+ int batchSize, batchCount;
+ bool stopped, headerPrinted;
+ std::ostream& out;
+};
+
+template <class Stats> class Reporter : public ReporterBase {
+ public:
+ Reporter(std::ostream& o, int batchSize, bool wantHeader)
+ : ReporterBase(o, batchSize, wantHeader) {}
+
+ virtual std::auto_ptr<Statistic> create() {
+ return std::auto_ptr<Statistic>(new Stats);
+ }
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_STATISTICS_H*/
diff --git a/qpid/cpp/src/tests/StoreStatus.cpp b/qpid/cpp/src/tests/StoreStatus.cpp
new file mode 100644
index 0000000000..43d4cfd920
--- /dev/null
+++ b/qpid/cpp/src/tests/StoreStatus.cpp
@@ -0,0 +1,117 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/cluster/StoreStatus.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/assign.hpp>
+#include <boost/filesystem/operations.hpp>
+
+using namespace std;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::framing::cluster;
+using namespace boost::assign;
+using namespace boost::filesystem;
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(StoreStatusTestSuite)
+
+const char* TEST_DIR = "StoreStatus.tmp";
+
+struct TestDir {
+ TestDir() {
+ remove_all(TEST_DIR);
+ create_directory(TEST_DIR);
+ }
+ ~TestDir() {
+ remove_all(TEST_DIR);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testLoadEmpty) {
+ TestDir td;
+ StoreStatus ss(TEST_DIR);
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_NO_STORE);
+ BOOST_CHECK(!ss.getClusterId());
+ BOOST_CHECK(!ss.getShutdownId());
+ ss.load();
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_EMPTY_STORE);
+ BOOST_CHECK(!ss.getShutdownId());
+}
+
+QPID_AUTO_TEST_CASE(testSaveLoadDirty) {
+ TestDir td;
+ Uuid clusterId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.setClusterId(clusterId);
+ ss.dirty();
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_DIRTY_STORE);
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK(!ss2.getShutdownId());
+}
+
+QPID_AUTO_TEST_CASE(testSaveLoadClean) {
+ TestDir td;
+ Uuid clusterId = Uuid(true);
+ Uuid shutdownId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.setClusterId(clusterId);
+ ss.clean(shutdownId);
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_CLEAN_STORE);
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_CLEAN_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK_EQUAL(ss2.getShutdownId(), shutdownId);
+}
+
+QPID_AUTO_TEST_CASE(testMarkDirty) {
+ // Save clean then mark to dirty.
+ TestDir td;
+ Uuid clusterId = Uuid(true);
+ Uuid shutdownId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.setClusterId(clusterId);
+ ss.dirty();
+ ss.clean(shutdownId);
+ ss.dirty();
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK(!ss2.getShutdownId());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+ }} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/StringUtils.cpp b/qpid/cpp/src/tests/StringUtils.cpp
new file mode 100644
index 0000000000..6a19119288
--- /dev/null
+++ b/qpid/cpp/src/tests/StringUtils.cpp
@@ -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.
+ *
+ */
+#include <iostream>
+#include "qpid/StringUtils.h"
+
+#include "unit_test.h"
+
+QPID_AUTO_TEST_SUITE(StringUtilsTestSuite)
+
+using namespace qpid;
+using std::string;
+
+QPID_AUTO_TEST_CASE(testSplit_general)
+{
+ std::vector<std::string> results = split("a bbbb, car,d123,,,e", ", ");
+ BOOST_CHECK_EQUAL(5u, results.size());
+ BOOST_CHECK_EQUAL(string("a"), results[0]);
+ BOOST_CHECK_EQUAL(string("bbbb"), results[1]);
+ BOOST_CHECK_EQUAL(string("car"), results[2]);
+ BOOST_CHECK_EQUAL(string("d123"), results[3]);
+ BOOST_CHECK_EQUAL(string("e"), results[4]);
+}
+
+QPID_AUTO_TEST_CASE(testSplit_noDelims)
+{
+ std::vector<std::string> results = split("abc", ", ");
+ BOOST_CHECK_EQUAL(1u, results.size());
+ BOOST_CHECK_EQUAL(string("abc"), results[0]);
+}
+
+QPID_AUTO_TEST_CASE(testSplit_delimAtEnd)
+{
+ std::vector<std::string> results = split("abc def,,", ", ");
+ BOOST_CHECK_EQUAL(2u, results.size());
+ BOOST_CHECK_EQUAL(string("abc"), results[0]);
+ BOOST_CHECK_EQUAL(string("def"), results[1]);
+}
+
+QPID_AUTO_TEST_CASE(testSplit_delimAtStart)
+{
+ std::vector<std::string> results = split(",,abc def", ", ");
+ BOOST_CHECK_EQUAL(2u, results.size());
+ BOOST_CHECK_EQUAL(string("abc"), results[0]);
+ BOOST_CHECK_EQUAL(string("def"), results[1]);
+}
+
+QPID_AUTO_TEST_CASE(testSplit_onlyDelims)
+{
+ std::vector<std::string> results = split(",, , ", ", ");
+ BOOST_CHECK_EQUAL(0u, results.size());
+}
+
+QPID_AUTO_TEST_CASE(testSplit_empty)
+{
+ std::vector<std::string> results = split("", ", ");
+ BOOST_CHECK_EQUAL(0u, results.size());
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/TestMessageStore.h b/qpid/cpp/src/tests/TestMessageStore.h
new file mode 100644
index 0000000000..20e0b755b2
--- /dev/null
+++ b/qpid/cpp/src/tests/TestMessageStore.h
@@ -0,0 +1,63 @@
+#ifndef _tests_TestMessageStore_h
+#define _tests_TestMessageStore_h
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/NullMessageStore.h"
+#include <vector>
+
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair;
+
+class TestMessageStore : public NullMessageStore
+{
+ public:
+ std::vector<boost::intrusive_ptr<PersistableMessage> > dequeued;
+ std::vector<msg_queue_pair> enqueued;
+
+ void dequeue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& /*queue*/)
+ {
+ dequeued.push_back(msg);
+ }
+
+ void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+ {
+ msg->enqueueComplete();
+ enqueued.push_back(msg_queue_pair(queue.getName(), msg));
+ }
+
+ TestMessageStore() : NullMessageStore() {}
+ ~TestMessageStore(){}
+};
+
+}} // namespace qpid::tests
+
+#endif
diff --git a/qpid/cpp/src/tests/TestOptions.h b/qpid/cpp/src/tests/TestOptions.h
new file mode 100644
index 0000000000..f8da0f59cf
--- /dev/null
+++ b/qpid/cpp/src/tests/TestOptions.h
@@ -0,0 +1,79 @@
+#ifndef _TestOptions_
+#define _TestOptions_
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Options.h"
+#include "qpid/log/Options.h"
+#include "qpid/Url.h"
+#include "qpid/log/Logger.h"
+#include "qpid/client/Connection.h"
+#include "ConnectionOptions.h"
+
+#include <iostream>
+#include <exception>
+
+namespace qpid {
+
+struct TestOptions : public qpid::Options
+{
+ TestOptions(const std::string& helpText_=std::string(),
+ const std::string& argv0=std::string())
+ : Options("Test Options"), help(false), log(argv0), helpText(helpText_)
+ {
+ addOptions()
+ ("help", optValue(help), "print this usage statement");
+ add(con);
+ add(log);
+ }
+
+ /** As well as parsing, throw help message if requested. */
+ void parse(int argc, char** argv) {
+ try {
+ qpid::Options::parse(argc, argv);
+ } catch (const std::exception& e) {
+ std::ostringstream msg;
+ msg << *this << std::endl << std::endl << e.what() << std::endl;
+ throw qpid::Options::Exception(msg.str());
+ }
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::ostringstream msg;
+ msg << *this << std::endl << std::endl << helpText << std::endl;
+ throw qpid::Options::Exception(msg.str());
+ }
+ }
+
+ /** Open a connection using option values */
+ void open(qpid::client::Connection& connection) {
+ connection.open(con);
+ }
+
+
+ bool help;
+ ConnectionOptions con;
+ qpid::log::Options log;
+ std::string helpText;
+};
+
+}
+
+#endif
diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp
new file mode 100644
index 0000000000..6a0a196f4e
--- /dev/null
+++ b/qpid/cpp/src/tests/TimerTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Monitor.h"
+#include "unit_test.h"
+#include <math.h>
+#include <iostream>
+#include <memory>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+using namespace qpid::sys;
+using boost::intrusive_ptr;
+using boost::dynamic_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+class Counter
+{
+ Mutex lock;
+ uint counter;
+ public:
+ Counter() : counter(0) {}
+ uint next()
+ {
+ Mutex::ScopedLock l(lock);
+ return ++counter;
+ }
+};
+
+class TestTask : public TimerTask
+{
+ const AbsTime start;
+ const Duration expected;
+ AbsTime end;
+ bool fired;
+ uint position;
+ Monitor monitor;
+ Counter& counter;
+
+ public:
+ TestTask(Duration timeout, Counter& _counter)
+ : TimerTask(timeout, "Test"), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {}
+
+ void fire()
+ {
+ Monitor::ScopedLock l(monitor);
+ fired = true;
+ position = counter.next();
+ end = now();
+ monitor.notify();
+ }
+
+ void check(uint expected_position, uint64_t tolerance = 500 * TIME_MSEC)
+ {
+ Monitor::ScopedLock l(monitor);
+ BOOST_CHECK(fired);
+ BOOST_CHECK_EQUAL(expected_position, position);
+ Duration actual(start, end);
+#ifdef _MSC_VER
+ uint64_t difference = _abs64(expected - actual);
+#elif defined(_WIN32)
+ uint64_t difference = labs(expected - actual);
+#else
+ uint64_t difference = abs(expected - actual);
+#endif
+ std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference));
+ BOOST_CHECK_MESSAGE(difference < tolerance, msg);
+ }
+
+ void wait(Duration d)
+ {
+ Monitor::ScopedLock l(monitor);
+ monitor.wait(AbsTime(now(), d));
+ }
+};
+
+class DummyRunner : public Runnable
+{
+ public:
+ void run() {}
+};
+
+QPID_AUTO_TEST_SUITE(TimerTestSuite)
+
+QPID_AUTO_TEST_CASE(testGeneral)
+{
+ Counter counter;
+ Timer timer;
+ intrusive_ptr<TestTask> task1(new TestTask(Duration(3 * TIME_SEC), counter));
+ intrusive_ptr<TestTask> task2(new TestTask(Duration(1 * TIME_SEC), counter));
+ intrusive_ptr<TestTask> task3(new TestTask(Duration(4 * TIME_SEC), counter));
+ intrusive_ptr<TestTask> task4(new TestTask(Duration(2 * TIME_SEC), counter));
+
+ timer.add(task1);
+ timer.add(task2);
+ timer.add(task3);
+ timer.add(task4);
+
+ dynamic_pointer_cast<TestTask>(task3)->wait(Duration(6 * TIME_SEC));
+
+ dynamic_pointer_cast<TestTask>(task1)->check(3);
+ dynamic_pointer_cast<TestTask>(task2)->check(1);
+ dynamic_pointer_cast<TestTask>(task3)->check(4);
+ dynamic_pointer_cast<TestTask>(task4)->check(2);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp
new file mode 100644
index 0000000000..ff8931f9c9
--- /dev/null
+++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp
@@ -0,0 +1,406 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "qpid/broker/TopicExchange.h"
+#include "unit_test.h"
+#include "test_tools.h"
+
+using namespace qpid::broker;
+using namespace std;
+
+
+namespace qpid {
+namespace broker {
+
+// Class for exercising the pattern match code in the TopicExchange
+class TopicExchange::TopicExchangeTester {
+
+public:
+ typedef std::vector<std::string> BindingVec;
+
+private:
+ // binding node iterator that collects all routes that are bound
+ class TestFinder : public TopicExchange::BindingNode::TreeIterator {
+ public:
+ TestFinder(BindingVec& m) : bv(m) {};
+ ~TestFinder() {};
+ bool visit(BindingNode& node) {
+ if (!node.bindings.bindingVector.empty())
+ bv.push_back(node.routePattern);
+ return true;
+ }
+
+ BindingVec& bv;
+ };
+
+public:
+ TopicExchangeTester() {};
+ ~TopicExchangeTester() {};
+ bool addBindingKey(const std::string& bKey) {
+ string routingPattern = normalize(bKey);
+ BindingKey *bk = bindingTree.addBindingKey(routingPattern);
+ if (bk) {
+ // push a dummy binding to mark this node as "non-leaf"
+ bk->bindingVector.push_back(Binding::shared_ptr());
+ return true;
+ }
+ return false;
+ }
+
+ bool removeBindingKey(const std::string& bKey){
+ string routingPattern = normalize(bKey);
+ BindingKey *bk = bindingTree.getBindingKey(routingPattern);
+ if (bk) {
+ bk->bindingVector.pop_back();
+ if (bk->bindingVector.empty()) {
+ // no more bindings - remove this node
+ bindingTree.removeBindingKey(routingPattern);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void findMatches(const std::string& rKey, BindingVec& matches) {
+ TestFinder testFinder(matches);
+ bindingTree.iterateMatch( rKey, testFinder );
+ }
+
+ void getAll(BindingVec& bindings) {
+ TestFinder testFinder(bindings);
+ bindingTree.iterateAll( testFinder );
+ }
+
+private:
+ TopicExchange::BindingNode bindingTree;
+};
+} // namespace broker
+
+
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite)
+
+#define CHECK_NORMALIZED(expect, pattern) BOOST_CHECK_EQUAL(expect, TopicExchange::normalize(pattern));
+
+namespace {
+ // return the count of bindings that match 'pattern'
+ int match(TopicExchange::TopicExchangeTester &tt,
+ const std::string& pattern)
+ {
+ TopicExchange::TopicExchangeTester::BindingVec bv;
+ tt.findMatches(pattern, bv);
+ return int(bv.size());
+ }
+
+ // return true if expected contains exactly all bindings that match
+ // against pattern.
+ bool compare(TopicExchange::TopicExchangeTester& tt,
+ const std::string& pattern,
+ const TopicExchange::TopicExchangeTester::BindingVec& expected)
+ {
+ TopicExchange::TopicExchangeTester::BindingVec bv;
+ tt.findMatches(pattern, bv);
+ if (expected.size() != bv.size()) {
+ // std::cout << "match failed 1 f=[" << bv << "]" << std::endl;
+ // std::cout << "match failed 1 e=[" << expected << "]" << std::endl;
+ return false;
+ }
+ TopicExchange::TopicExchangeTester::BindingVec::const_iterator i;
+ for (i = expected.begin(); i != expected.end(); i++) {
+ TopicExchange::TopicExchangeTester::BindingVec::iterator j;
+ for (j = bv.begin(); j != bv.end(); j++) {
+ // std::cout << "matched [" << *j << "]" << std::endl;
+ if (*i == *j) break;
+ }
+ if (j == bv.end()) {
+ // std::cout << "match failed 2 [" << bv << "]" << std::endl;
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testNormalize)
+{
+ CHECK_NORMALIZED("", "");
+ CHECK_NORMALIZED("a.b.c", "a.b.c");
+ CHECK_NORMALIZED("a.*.c", "a.*.c");
+ CHECK_NORMALIZED("#", "#");
+ CHECK_NORMALIZED("#", "#.#.#.#");
+ CHECK_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*");
+ CHECK_NORMALIZED("*.*.*.#", "*.#.#.*.*.#");
+}
+
+QPID_AUTO_TEST_CASE(testPlain)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("ab.cd.e");
+
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "ab.cd.e"));
+ BOOST_CHECK_EQUAL(0, match(tt, "abx.cd.e"));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd"));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd..e."));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd.e."));
+ BOOST_CHECK_EQUAL(0, match(tt, ".ab.cd.e"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, ""));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = ".";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "."));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+
+QPID_AUTO_TEST_CASE(testStar)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("a.*.b");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.xx.b"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.b"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "*.x";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "y.x"));
+ BOOST_CHECK_EQUAL(1, match(tt, ".x"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "x.x.*";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.x.y"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.x."));
+ BOOST_CHECK_EQUAL(0, match(tt, "x.x"));
+ BOOST_CHECK_EQUAL(0, match(tt, "q.x.y"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+QPID_AUTO_TEST_CASE(testHash)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("a.#.b");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a..x.y.zz.b"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.b."));
+ BOOST_CHECK_EQUAL(0, match(tt, "q.x.b"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "#.a";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.a"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#.b.#.c";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.c"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.b.y.c"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.x.b.y.y.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+QPID_AUTO_TEST_CASE(testMixed)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("*.x.#.y");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.y"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.p.qq.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.a.x.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "aa.x.b.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#.b.*";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.x"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.x.x.b.x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "*.*.*.#";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.z"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.z.a.b.c"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+
+QPID_AUTO_TEST_CASE(testMultiple)
+{
+ TopicExchange::TopicExchangeTester tt;
+ const std::string bindings[] =
+ { "a", "b",
+ "a.b", "b.c",
+ "a.b.c.d", "b.c.d.e",
+ "a.*", "a.#", "a.*.#",
+ "#.b", "*.b", "*.#.b",
+ "a.*.b", "a.#.b", "a.*.#.b",
+ "*.b.*", "#.b.#",
+ };
+ const size_t nBindings = sizeof(bindings)/sizeof(bindings[0]);
+
+ // setup bindings
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ BOOST_CHECK(tt.addBindingKey(bindings[idx]));
+ }
+
+ {
+ // read all bindings, and verify all are present
+ TopicExchange::TopicExchangeTester::BindingVec b;
+ tt.getAll(b);
+ BOOST_CHECK_EQUAL(b.size(), nBindings);
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ bool found = false;
+ for (TopicExchange::TopicExchangeTester::BindingVec::iterator i = b.begin();
+ i != b.end(); i++) {
+ if (*i == bindings[idx]) {
+ found = true;
+ break;
+ }
+ }
+ BOOST_CHECK(found);
+ }
+ }
+
+ { // test match on pattern "a"
+ const std::string matches[] = { "a", "a.#" };
+ const size_t nMatches = 2;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a", expected));
+ }
+
+ { // test match on pattern "a.z"
+ const std::string matches[] = { "a.*", "a.#", "a.*.#" };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.z", expected));
+ }
+
+ { // test match on pattern "a.b"
+ const std::string matches[] = {
+ "a.b", "a.*", "a.#", "a.*.#",
+ "#.b", "#.b.#", "*.#.b", "*.b",
+ "a.#.b"
+ };
+ const size_t nMatches = 9;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b", expected));
+ }
+
+ { // test match on pattern "a.c.c.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b", "a.#.b",
+ "a.#", "a.*.#.b", "a.*.#"
+ };
+ const size_t nMatches = 7;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.c.c.b", expected));
+ }
+
+ { // test match on pattern "a.b.c"
+
+ const std::string matches[] = {
+ "#.b.#", "*.b.*", "a.#", "a.*.#"
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b.c", expected));
+ }
+
+ { // test match on pattern "b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "b"
+ };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "b", expected));
+ }
+
+ { // test match on pattern "x.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b", "*.b"
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.b", expected));
+ }
+
+ { // test match on pattern "x.y.z.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b"
+ };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.y.z.b", expected));
+ }
+
+ { // test match on pattern "x.y.z.b.a.b.c"
+
+ const std::string matches[] = {
+ "#.b.#", "#.b.#"
+ };
+ const size_t nMatches = 2;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.y.z.b.a.b.c", expected));
+ }
+
+ { // test match on pattern "a.b.c.d"
+
+ const std::string matches[] = {
+ "#.b.#", "a.#", "a.*.#", "a.b.c.d",
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b.c.d", expected));
+ }
+
+ // cleanup bindings
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ BOOST_CHECK(tt.removeBindingKey(bindings[idx]));
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TxBufferTest.cpp b/qpid/cpp/src/tests/TxBufferTest.cpp
new file mode 100644
index 0000000000..4807026ab7
--- /dev/null
+++ b/qpid/cpp/src/tests/TxBufferTest.cpp
@@ -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.
+ *
+ */
+#include "qpid/broker/TxBuffer.h"
+#include "unit_test.h"
+#include <iostream>
+#include <vector>
+#include "TxMocks.h"
+
+using namespace qpid::broker;
+using boost::static_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(TxBufferTestSuite)
+
+QPID_AUTO_TEST_CASE(testCommitLocal)
+{
+ MockTransactionalStore store;
+ store.expectBegin().expectCommit();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectCommit();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectPrepare().expectPrepare().expectCommit().expectCommit();//opB enlisted twice to test relative order
+ MockTxOp::shared_ptr opC(new MockTxOp());
+ opC->expectPrepare().expectCommit();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));//opB enlisted twice
+ buffer.enlist(static_pointer_cast<TxOp>(opC));
+
+ BOOST_CHECK(buffer.commitLocal(&store));
+ store.check();
+ BOOST_CHECK(store.isCommitted());
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testFailOnCommitLocal)
+{
+ MockTransactionalStore store;
+ store.expectBegin().expectAbort();
+
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp(true));
+ opB->expectPrepare().expectRollback();
+ MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail
+ opC->expectRollback();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+ buffer.enlist(static_pointer_cast<TxOp>(opC));
+
+ BOOST_CHECK(!buffer.commitLocal(&store));
+ BOOST_CHECK(store.isAborted());
+ store.check();
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testPrepare)
+{
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectPrepare();
+ MockTxOp::shared_ptr opC(new MockTxOp());
+ opC->expectPrepare();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+ buffer.enlist(static_pointer_cast<TxOp>(opC));
+
+ BOOST_CHECK(buffer.prepare(0));
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testFailOnPrepare)
+{
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectPrepare();
+ MockTxOp::shared_ptr opB(new MockTxOp(true));
+ opB->expectPrepare();
+ MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+ buffer.enlist(static_pointer_cast<TxOp>(opC));
+
+ BOOST_CHECK(!buffer.prepare(0));
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testRollback)
+{
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp(true));
+ opB->expectRollback();
+ MockTxOp::shared_ptr opC(new MockTxOp());
+ opC->expectRollback();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+ buffer.enlist(static_pointer_cast<TxOp>(opC));
+
+ buffer.rollback();
+ opA->check();
+ opB->check();
+ opC->check();
+}
+
+QPID_AUTO_TEST_CASE(testBufferIsClearedAfterRollback)
+{
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectRollback();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectRollback();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+
+ buffer.rollback();
+ buffer.commit();//second call should not reach ops
+ opA->check();
+ opB->check();
+}
+
+QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit)
+{
+ MockTxOp::shared_ptr opA(new MockTxOp());
+ opA->expectCommit();
+ MockTxOp::shared_ptr opB(new MockTxOp());
+ opB->expectCommit();
+
+ TxBuffer buffer;
+ buffer.enlist(static_pointer_cast<TxOp>(opA));
+ buffer.enlist(static_pointer_cast<TxOp>(opB));
+
+ buffer.commit();
+ buffer.rollback();//second call should not reach ops
+ opA->check();
+ opB->check();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TxMocks.h b/qpid/cpp/src/tests/TxMocks.h
new file mode 100644
index 0000000000..72cb50cd21
--- /dev/null
+++ b/qpid/cpp/src/tests/TxMocks.h
@@ -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.
+ *
+ */
+#ifndef _tests_TxMocks_h
+#define _tests_TxMocks_h
+
+
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include "qpid/broker/TransactionalStore.h"
+#include "qpid/broker/TxOp.h"
+#include <iostream>
+#include <vector>
+
+using namespace qpid::broker;
+using boost::static_pointer_cast;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){
+ unsigned int i = 0;
+ while(i < expected.size() && i < actual.size()){
+ BOOST_CHECK_EQUAL(expected[i], actual[i]);
+ i++;
+ }
+ if (i < expected.size()) {
+ throw qpid::Exception(QPID_MSG("Missing " << expected[i]));
+ } else if (i < actual.size()) {
+ throw qpid::Exception(QPID_MSG("Extra " << actual[i]));
+ }
+ BOOST_CHECK_EQUAL(expected.size(), actual.size());
+}
+
+class TxOpConstants{
+protected:
+ const string PREPARE;
+ const string COMMIT;
+ const string ROLLBACK;
+
+ TxOpConstants() : PREPARE("PREPARE"), COMMIT("COMMIT"), ROLLBACK("ROLLBACK") {}
+};
+
+class MockTxOp : public TxOp, public TxOpConstants{
+ std::vector<string> expected;
+ std::vector<string> actual;
+ bool failOnPrepare;
+ string debugName;
+public:
+ typedef boost::shared_ptr<MockTxOp> shared_ptr;
+
+ MockTxOp() : failOnPrepare(false) {}
+ MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {}
+
+ void setDebugName(string name){
+ debugName = name;
+ }
+
+ void printExpected(){
+ std::cout << std::endl << "MockTxOp[" << debugName << "] expects: ";
+ for (std::vector<string>::iterator i = expected.begin(); i < expected.end(); i++) {
+ if(i != expected.begin()) std::cout << ", ";
+ std::cout << *i;
+ }
+ std::cout << std::endl;
+ }
+
+ void printActual(){
+ std::cout << std::endl << "MockTxOp[" << debugName << "] actual: ";
+ for (std::vector<string>::iterator i = actual.begin(); i < actual.end(); i++) {
+ if(i != actual.begin()) std::cout << ", ";
+ std::cout << *i;
+ }
+ std::cout << std::endl;
+ }
+
+ bool prepare(TransactionContext*) throw(){
+ actual.push_back(PREPARE);
+ return !failOnPrepare;
+ }
+ void commit() throw(){
+ actual.push_back(COMMIT);
+ }
+ void rollback() throw(){
+ if(!debugName.empty()) std::cout << std::endl << "MockTxOp[" << debugName << "]::rollback()" << std::endl;
+ actual.push_back(ROLLBACK);
+ }
+ MockTxOp& expectPrepare(){
+ expected.push_back(PREPARE);
+ return *this;
+ }
+ MockTxOp& expectCommit(){
+ expected.push_back(COMMIT);
+ return *this;
+ }
+ MockTxOp& expectRollback(){
+ expected.push_back(ROLLBACK);
+ return *this;
+ }
+ void check(){
+ assertEqualVector(expected, actual);
+ }
+
+ void accept(TxOpConstVisitor&) const {}
+
+ ~MockTxOp(){}
+};
+
+class MockTransactionalStore : public TransactionalStore{
+ const string BEGIN;
+ const string BEGIN2PC;
+ const string PREPARE;
+ const string COMMIT;
+ const string ABORT;
+ std::vector<string> expected;
+ std::vector<string> actual;
+
+ enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4};
+ int state;
+
+ class TestTransactionContext : public TPCTransactionContext{
+ MockTransactionalStore* store;
+ public:
+ TestTransactionContext(MockTransactionalStore* _store) : store(_store) {}
+ void prepare(){
+ if(!store->isOpen()) throw "txn already completed";
+ store->state = PREPARED;
+ }
+
+ void commit(){
+ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed";
+ store->state = COMMITTED;
+ }
+
+ void abort(){
+ if(!store->isOpen() && !store->isPrepared()) throw "txn already completed";
+ store->state = ABORTED;
+ }
+ ~TestTransactionContext(){}
+ };
+
+public:
+ MockTransactionalStore() :
+ BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){}
+
+ void collectPreparedXids(std::set<std::string>&)
+ {
+ throw "Operation not supported";
+ }
+
+ std::auto_ptr<TPCTransactionContext> begin(const std::string&){
+ actual.push_back(BEGIN2PC);
+ std::auto_ptr<TPCTransactionContext> txn(new TestTransactionContext(this));
+ return txn;
+ }
+ std::auto_ptr<TransactionContext> begin(){
+ actual.push_back(BEGIN);
+ std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this));
+ return txn;
+ }
+ void prepare(TPCTransactionContext& ctxt){
+ actual.push_back(PREPARE);
+ dynamic_cast<TestTransactionContext&>(ctxt).prepare();
+ }
+ void commit(TransactionContext& ctxt){
+ actual.push_back(COMMIT);
+ dynamic_cast<TestTransactionContext&>(ctxt).commit();
+ }
+ void abort(TransactionContext& ctxt){
+ actual.push_back(ABORT);
+ dynamic_cast<TestTransactionContext&>(ctxt).abort();
+ }
+ MockTransactionalStore& expectBegin(){
+ expected.push_back(BEGIN);
+ return *this;
+ }
+ MockTransactionalStore& expectBegin2PC(){
+ expected.push_back(BEGIN2PC);
+ return *this;
+ }
+ MockTransactionalStore& expectPrepare(){
+ expected.push_back(PREPARE);
+ return *this;
+ }
+ MockTransactionalStore& expectCommit(){
+ expected.push_back(COMMIT);
+ return *this;
+ }
+ MockTransactionalStore& expectAbort(){
+ expected.push_back(ABORT);
+ return *this;
+ }
+ void check(){
+ assertEqualVector(expected, actual);
+ }
+
+ bool isPrepared(){
+ return state == PREPARED;
+ }
+
+ bool isCommitted(){
+ return state == COMMITTED;
+ }
+
+ bool isAborted(){
+ return state == ABORTED;
+ }
+
+ bool isOpen() const{
+ return state == OPEN;
+ }
+ ~MockTransactionalStore(){}
+};
+
+}} // namespace qpid::tests
+
+#endif
diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp
new file mode 100644
index 0000000000..210abf0a5b
--- /dev/null
+++ b/qpid/cpp/src/tests/TxPublishTest.cpp
@@ -0,0 +1,99 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/RecoveryManager.h"
+#include "qpid/broker/TxPublish.h"
+#include "unit_test.h"
+#include <iostream>
+#include <list>
+#include <vector>
+#include "MessageUtils.h"
+#include "TestMessageStore.h"
+
+using std::list;
+using std::pair;
+using std::vector;
+using boost::intrusive_ptr;
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+struct TxPublishTest
+{
+
+ TestMessageStore store;
+ Queue::shared_ptr queue1;
+ Queue::shared_ptr queue2;
+ intrusive_ptr<Message> msg;
+ TxPublish op;
+
+ TxPublishTest() :
+ queue1(new Queue("queue1", false, &store, 0)),
+ queue2(new Queue("queue2", false, &store, 0)),
+ msg(MessageUtils::createMessage("exchange", "routing_key", false, "id")),
+ op(msg)
+ {
+ msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT);
+ op.deliverTo(queue1);
+ op.deliverTo(queue2);
+ }
+};
+
+
+QPID_AUTO_TEST_SUITE(TxPublishTestSuite)
+
+QPID_AUTO_TEST_CASE(testPrepare)
+{
+ TxPublishTest t;
+
+ intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(t.msg);
+ //ensure messages are enqueued in store
+ t.op.prepare(0);
+ BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size());
+ BOOST_CHECK_EQUAL(string("queue1"), t.store.enqueued[0].first);
+ BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second);
+ BOOST_CHECK_EQUAL(string("queue2"), t.store.enqueued[1].first);
+ BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[1].second);
+ BOOST_CHECK_EQUAL( true, ( boost::static_pointer_cast<PersistableMessage>(t.msg))->isIngressComplete());
+}
+
+QPID_AUTO_TEST_CASE(testCommit)
+{
+ TxPublishTest t;
+
+ //ensure messages are delivered to queue
+ t.op.prepare(0);
+ t.op.commit();
+ BOOST_CHECK_EQUAL((uint32_t) 1, t.queue1->getMessageCount());
+ intrusive_ptr<Message> msg_dequeue = t.queue1->get().payload;
+
+ BOOST_CHECK_EQUAL( true, (boost::static_pointer_cast<PersistableMessage>(msg_dequeue))->isIngressComplete());
+ BOOST_CHECK_EQUAL(t.msg, msg_dequeue);
+
+ BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount());
+ BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Url.cpp b/qpid/cpp/src/tests/Url.cpp
new file mode 100644
index 0000000000..234a62ee91
--- /dev/null
+++ b/qpid/cpp/src/tests/Url.cpp
@@ -0,0 +1,90 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/Url.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(UrlTestSuite)
+
+#define URL_CHECK_STR(STR) BOOST_CHECK_EQUAL(Url(STR).str(), STR)
+#define URL_CHECK_INVALID(STR) BOOST_CHECK_THROW(Url(STR), Url::Invalid)
+
+QPID_AUTO_TEST_CASE(TestParseTcp) {
+ URL_CHECK_STR("amqp:tcp:host:42");
+ URL_CHECK_STR("amqp:tcp:host-._~%ff%23:42"); // unreserved chars and pct encoded hex.
+ // Check defaults
+ BOOST_CHECK_EQUAL(Url("amqp:host:42").str(), "amqp:tcp:host:42");
+ BOOST_CHECK_EQUAL(Url("amqp:tcp:host").str(), "amqp:tcp:host:5672");
+ BOOST_CHECK_EQUAL(Url("host").str(), "amqp:tcp:host:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseInvalid) {
+ //host is required:
+ URL_CHECK_INVALID("amqp:tcp:");
+ URL_CHECK_INVALID("amqp:");
+ URL_CHECK_INVALID("amqp::42");
+ URL_CHECK_INVALID("");
+
+ // Port must be numeric
+ URL_CHECK_INVALID("host:badPort");
+}
+
+QPID_AUTO_TEST_CASE(TestParseXyz) {
+ Url::addProtocol("xyz");
+ URL_CHECK_STR("amqp:xyz:host:123");
+ BOOST_CHECK_EQUAL(Url("xyz:host").str(), "amqp:xyz:host:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseMultiAddress) {
+ Url::addProtocol("xyz");
+ URL_CHECK_STR("amqp:tcp:host:0,xyz:foo:123,tcp:foo:0,xyz:bar:1");
+ URL_CHECK_STR("amqp:xyz:foo:222,tcp:foo:0");
+ URL_CHECK_INVALID("amqp:tcp:h:0,");
+ URL_CHECK_INVALID(",amqp:tcp:h");
+}
+
+QPID_AUTO_TEST_CASE(TestParseUserPass) {
+ URL_CHECK_STR("amqp:user/pass@tcp:host:123");
+ URL_CHECK_STR("amqp:user@tcp:host:123");
+ BOOST_CHECK_EQUAL(Url("user/pass@host").str(), "amqp:user/pass@tcp:host:5672");
+ BOOST_CHECK_EQUAL(Url("user@host").str(), "amqp:user@tcp:host:5672");
+
+ Url u("user/pass@host");
+ BOOST_CHECK_EQUAL(u.getUser(), "user");
+ BOOST_CHECK_EQUAL(u.getPass(), "pass");
+ Url v("foo@host");
+ BOOST_CHECK_EQUAL(v.getUser(), "foo");
+ BOOST_CHECK_EQUAL(v.getPass(), "");
+ u = v;
+ BOOST_CHECK_EQUAL(u.getUser(), "foo");
+ BOOST_CHECK_EQUAL(u.getPass(), "");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp
new file mode 100644
index 0000000000..0195455ca3
--- /dev/null
+++ b/qpid/cpp/src/tests/Uuid.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/sys/alloca.h"
+
+#include "unit_test.h"
+
+#include <set>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(UuidTestSuite)
+
+using namespace std;
+using namespace qpid::framing;
+
+struct UniqueSet : public std::set<Uuid> {
+ void operator()(const Uuid& uuid) {
+ BOOST_REQUIRE(find(uuid) == end());
+ insert(uuid);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testUuidCtor) {
+ // Uniqueness
+ boost::array<Uuid,1000> uuids;
+ for_each(uuids.begin(), uuids.end(), mem_fun_ref(&Uuid::generate));
+ UniqueSet unique;
+ for_each(uuids.begin(), uuids.end(), unique);
+}
+
+boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}};
+const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb");
+const string zeroStr("00000000-0000-0000-0000-000000000000");
+
+QPID_AUTO_TEST_CASE(testUuidIstream) {
+ Uuid uuid;
+ istringstream in(sampleStr);
+ in >> uuid;
+ BOOST_CHECK(!in.fail());
+ BOOST_CHECK(uuid == sample);
+
+ istringstream is(zeroStr);
+ Uuid zero;
+ is >> zero;
+ BOOST_CHECK(!in.fail());
+ BOOST_CHECK_EQUAL(zero, Uuid());
+}
+
+QPID_AUTO_TEST_CASE(testUuidOstream) {
+ Uuid uuid(sample.c_array());
+ ostringstream out;
+ out << uuid;
+ BOOST_CHECK(out.good());
+ BOOST_CHECK_EQUAL(out.str(), sampleStr);
+
+ ostringstream os;
+ os << Uuid();
+ BOOST_CHECK(out.good());
+ BOOST_CHECK_EQUAL(os.str(), zeroStr);
+}
+
+QPID_AUTO_TEST_CASE(testUuidIOstream) {
+ Uuid a(true), b(true);
+ ostringstream os;
+ os << a << endl << b;
+ Uuid aa, bb;
+ istringstream is(os.str());
+ is >> aa >> ws >> bb;
+ BOOST_CHECK(os.good());
+ BOOST_CHECK_EQUAL(a, aa);
+ BOOST_CHECK_EQUAL(b, bb);
+}
+
+QPID_AUTO_TEST_CASE(testUuidEncodeDecode) {
+ char* buff = static_cast<char*>(::alloca(Uuid::size()));
+ Buffer wbuf(buff, Uuid::size());
+ Uuid uuid(sample.c_array());
+ uuid.encode(wbuf);
+
+ Buffer rbuf(buff, Uuid::size());
+ Uuid decoded;
+ decoded.decode(rbuf);
+ BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()),
+ string(decoded.begin(), decoded.end()));
+}
+
+QPID_AUTO_TEST_CASE(testTypesUuid)
+{
+ //tests for the Uuid class in the types namespace (introduced
+ //to avoid pulling in dependencies from framing)
+ types::Uuid a;
+ types::Uuid b(true);
+ types::Uuid c(true);
+ types::Uuid d(b);
+ types::Uuid e;
+ e = c;
+
+ BOOST_CHECK(!a);
+ BOOST_CHECK(b);
+
+ BOOST_CHECK(a != b);
+ BOOST_CHECK(b != c);
+
+ BOOST_CHECK_EQUAL(b, d);
+ BOOST_CHECK_EQUAL(c, e);
+
+ ostringstream out;
+ out << b;
+ istringstream in(out.str());
+ in >> a;
+ BOOST_CHECK(!in.fail());
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp
new file mode 100644
index 0000000000..40f1c0cf75
--- /dev/null
+++ b/qpid/cpp/src/tests/Variant.cpp
@@ -0,0 +1,775 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/types/Variant.h"
+#include "qpid/amqp_0_10/Codecs.h"
+
+#include "unit_test.h"
+
+using namespace qpid::types;
+using namespace qpid::amqp_0_10;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(VariantSuite)
+
+QPID_AUTO_TEST_CASE(testConversions)
+{
+ Variant value;
+
+ //string to float/double
+ value = "1.5";
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+
+ //float to string or double
+ value = 1.5f;
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //double to string (conversion to float not valid)
+ value = 1.5;
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //uint8 to larger unsigned ints and string
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("7"), value.asString());
+
+ value = (uint16_t) 8;
+ BOOST_CHECK_EQUAL(std::string("8"), value.asString());
+ value = (uint32_t) 9;
+ BOOST_CHECK_EQUAL(std::string("9"), value.asString());
+
+ //uint32 to larger unsigned ints and string
+ value = (uint32_t) 9999999;
+ BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("9999999"), value.asString());
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+
+ value = "true";
+ BOOST_CHECK(value.asBool());
+ value = "false";
+ BOOST_CHECK(!value.asBool());
+ value = "1";
+ BOOST_CHECK(value.asBool());
+ value = "0";
+ BOOST_CHECK(!value.asBool());
+ value = "other";
+ BOOST_CHECK_THROW(value.asBool(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testConversionsFromString)
+{
+ Variant value;
+ value = "5";
+ BOOST_CHECK_EQUAL(5, value.asInt16());
+ BOOST_CHECK_EQUAL(5u, value.asUint16());
+
+ value = "-5";
+ BOOST_CHECK_EQUAL(-5, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+
+ value = "18446744073709551615";
+ BOOST_CHECK_EQUAL(18446744073709551615ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "9223372036854775808";
+ BOOST_CHECK_EQUAL(9223372036854775808ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "-9223372036854775809";
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "2147483648";
+ BOOST_CHECK_EQUAL(2147483648ul, value.asUint32());
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "-2147483649";
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "32768";
+ BOOST_CHECK_EQUAL(32768u, value.asUint16());
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-32769";
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-2.5";
+ BOOST_CHECK_EQUAL(-2.5, value.asFloat());
+
+ value = "-0.875432e10";
+ BOOST_CHECK_EQUAL(-0.875432e10, value.asDouble());
+
+ value = "-0";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-000";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-0010";
+ BOOST_CHECK_EQUAL(-10, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testSizeConversionsUint)
+{
+ Variant value;
+
+ //uint8 (in 7 bits) to other uints, ints
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 7, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 7, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 7, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 7, value.asInt64());
+
+ //uint8 (in 8 bits) to other uints, ints
+ value = (uint8_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+
+
+ //uint16 (in 7 bits) to other uints, ints
+ value = (uint16_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint16 (more than 7 bits) to other uints, ints
+ value = (uint16_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint16 (more than 8 bits) to other uints, ints
+ value = (uint16_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint16 (more than 15 bits) to other uints, ints
+ value = (uint16_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+
+
+ //uint32 (in 7 bits) to other uints, ints
+ value = (uint32_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint32 (more than 7 bits) to other uints, ints
+ value = (uint32_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint32 (more than 8 bits) to other uints, ints
+ value = (uint32_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint32 (more than 15 bits) to other uints, ints
+ value = (uint32_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+ //uint32 (more than 16 bits) to other uints, ints
+ value = (uint32_t) 66000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 66000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 66000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 66000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 66000, value.asInt64());
+
+
+
+ //uint64 (in 7 bits) to other uints, ints
+ value = (uint64_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint64 (more than 7 bits) to other uints, ints
+ value = (uint64_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint64 (more than 8 bits) to other uints, ints
+ value = (uint64_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint64 (more than 15 bits) to other uints, ints
+ value = (uint64_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+ //uint64 (more than 16 bits) to other uints, ints
+ value = (uint64_t) 66000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 66000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 66000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 66000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 66000, value.asInt64());
+
+ //uint64 (more than 31 bits) to other uints, ints
+ value = (uint64_t) 3000000000ul;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 3000000000ul, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 3000000000ul, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 3000000000ul, value.asInt64());
+
+ //uint64 (more than 32 bits) to other uints, ints
+ value = (uint64_t) 7000000000ull;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 7000000000ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 7000000000ull, value.asInt64());
+
+ //uint64 (more than 63 bits) to other uints, ints
+ value = (uint64_t) 0x8000000000000000ull;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 0x8000000000000000ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testSizeConversionsInt)
+{
+ Variant value;
+
+ //int8 (positive in 7 bits)
+ value = (int8_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int8 (negative)
+ value = (int8_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+
+
+ //int16 (positive in 7 bits)
+ value = (int16_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int16 (positive in 8 bits)
+ value = (int16_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int16 (positive in more than 8 bits)
+ value = (int16_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int16 (negative in 7 bits)
+ value = (int16_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int16 (negative in more than 7 bits)
+ value = (int16_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+
+
+ //int32 (positive in 7 bits)
+ value = (int32_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int32 (positive in 8 bits)
+ value = (int32_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int32 (positive in more than 8 bits)
+ value = (int32_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int32 (positive in more than 15 bits)
+ value = (int32_t) 40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 40000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 40000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 40000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 40000, value.asInt64());
+
+ //int32 (negative in 7 bits)
+ value = (int32_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int32 (negative in more than 7 bits)
+ value = (int32_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+ //int32 (negative in more than 15 bits)
+ value = (int32_t) -40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) -40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -40000, value.asInt64());
+
+
+
+ //int64 (positive in 7 bits)
+ value = (int64_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int64 (positive in 8 bits)
+ value = (int64_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int64 (positive in more than 8 bits)
+ value = (int64_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int64 (positive in more than 15 bits)
+ value = (int64_t) 40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 40000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 40000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 40000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 40000, value.asInt64());
+
+ //int64 (positive in more than 31 bits)
+ value = (int64_t) 3000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 3000000000ll, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 3000000000ll, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 3000000000ll, value.asInt64());
+
+ //int64 (positive in more than 32 bits)
+ value = (int64_t) 5000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 5000000000ll, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 5000000000ll, value.asInt64());
+
+ //int64 (negative in 7 bits)
+ value = (int64_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int64 (negative in more than 7 bits)
+ value = (int64_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+ //int64 (negative in more than 15 bits)
+ value = (int64_t) -40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) -40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -40000, value.asInt64());
+
+ //int64 (negative in more than 31 bits)
+ value = (int64_t) -3000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) -3000000000ll, value.asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testAssignment)
+{
+ Variant value("abc");
+ Variant other = value;
+ BOOST_CHECK_EQUAL(VAR_STRING, value.getType());
+ BOOST_CHECK_EQUAL(other.getType(), value.getType());
+ BOOST_CHECK_EQUAL(other.asString(), value.asString());
+
+ const uint32_t i(1000);
+ value = i;
+ BOOST_CHECK_EQUAL(VAR_UINT32, value.getType());
+ BOOST_CHECK_EQUAL(VAR_STRING, other.getType());
+}
+
+QPID_AUTO_TEST_CASE(testList)
+{
+ const std::string s("abc");
+ const float f(9.876f);
+ const int16_t x(1000);
+
+ Variant value = Variant::List();
+ value.asList().push_back(Variant(s));
+ value.asList().push_back(Variant(f));
+ value.asList().push_back(Variant(x));
+ BOOST_CHECK_EQUAL(3u, value.asList().size());
+ Variant::List::const_iterator i = value.asList().begin();
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_STRING, i->getType());
+ BOOST_CHECK_EQUAL(s, i->asString());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_FLOAT, i->getType());
+ BOOST_CHECK_EQUAL(f, i->asFloat());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_INT16, i->getType());
+ BOOST_CHECK_EQUAL(x, i->asInt16());
+ i++;
+
+ BOOST_CHECK(i == value.asList().end());
+}
+
+QPID_AUTO_TEST_CASE(testMap)
+{
+ const std::string red("red");
+ const float pi(3.14f);
+ const int16_t x(1000);
+ const Uuid u(true);
+
+ Variant value = Variant::Map();
+ value.asMap()["colour"] = red;
+ value.asMap()["pi"] = pi;
+ value.asMap()["my-key"] = x;
+ value.asMap()["id"] = u;
+ BOOST_CHECK_EQUAL(4u, value.asMap().size());
+
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["colour"].getType());
+ BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString());
+
+ BOOST_CHECK_EQUAL(VAR_FLOAT, value.asMap()["pi"].getType());
+ BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat());
+
+ BOOST_CHECK_EQUAL(VAR_INT16, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16());
+
+ BOOST_CHECK_EQUAL(VAR_UUID, value.asMap()["id"].getType());
+ BOOST_CHECK_EQUAL(u, value.asMap()["id"].asUuid());
+
+ value.asMap()["my-key"] = "now it's a string";
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString());
+}
+
+QPID_AUTO_TEST_CASE(testIsEqualTo)
+{
+ BOOST_CHECK_EQUAL(Variant("abc"), Variant("abc"));
+ BOOST_CHECK_EQUAL(Variant(1234), Variant(1234));
+
+ Variant a = Variant::Map();
+ a.asMap()["colour"] = "red";
+ a.asMap()["pi"] = 3.14f;
+ a.asMap()["my-key"] = 1234;
+ Variant b = Variant::Map();
+ b.asMap()["colour"] = "red";
+ b.asMap()["pi"] = 3.14f;
+ b.asMap()["my-key"] = 1234;
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+QPID_AUTO_TEST_CASE(testEncoding)
+{
+ Variant a("abc");
+ a.setEncoding("utf8");
+ Variant b = a;
+ Variant map = Variant::Map();
+ map.asMap()["a"] = a;
+ map.asMap()["b"] = b;
+ BOOST_CHECK_EQUAL(a.getEncoding(), std::string("utf8"));
+ BOOST_CHECK_EQUAL(a.getEncoding(), b.getEncoding());
+ BOOST_CHECK_EQUAL(a.getEncoding(), map.asMap()["a"].getEncoding());
+ BOOST_CHECK_EQUAL(b.getEncoding(), map.asMap()["b"].getEncoding());
+ BOOST_CHECK_EQUAL(map.asMap()["a"].getEncoding(), map.asMap()["b"].getEncoding());
+}
+
+QPID_AUTO_TEST_CASE(testBufferEncoding)
+{
+ Variant a("abc");
+ a.setEncoding("utf8");
+ std::string buffer;
+
+ Variant::Map inMap, outMap;
+ inMap["a"] = a;
+
+ MapCodec::encode(inMap, buffer);
+ MapCodec::decode(buffer, outMap);
+ BOOST_CHECK_EQUAL(inMap, outMap);
+
+ inMap["b"] = Variant(std::string(65535, 'X'));
+ inMap["b"].setEncoding("utf16");
+ MapCodec::encode(inMap, buffer);
+ MapCodec::decode(buffer, outMap);
+ BOOST_CHECK_EQUAL(inMap, outMap);
+
+ inMap["fail"] = Variant(std::string(65536, 'X'));
+ inMap["fail"].setEncoding("utf16");
+ BOOST_CHECK_THROW(MapCodec::encode(inMap, buffer), std::exception);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/qpid/cpp/src/tests/XmlClientSessionTest.cpp
new file mode 100644
index 0000000000..b3b7f12b53
--- /dev/null
+++ b/qpid/cpp/src/tests/XmlClientSessionTest.cpp
@@ -0,0 +1,297 @@
+/*
+ *
+ * Licensed to the Apachef Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/sys/Shlib.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/client/Message.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/LocalQueue.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(XmlClientSessionTest)
+
+using namespace qpid::client;
+
+using namespace qpid::client::arg;
+using namespace qpid::framing;
+using namespace qpid;
+using qpid::sys::Shlib;
+using qpid::sys::Monitor;
+using std::string;
+using std::cout;
+using std::endl;
+
+
+Shlib shlib(getLibPath("XML_LIB"));
+
+class SubscribedLocalQueue : public LocalQueue {
+ private:
+ SubscriptionManager& subscriptions;
+ public:
+ SubscribedLocalQueue(SubscriptionManager& subs) : subscriptions(subs) {}
+ Message get () { return pop(); }
+ Message get (sys::Duration timeout) { return pop(timeout); }
+ virtual ~SubscribedLocalQueue() {}
+};
+
+
+struct SimpleListener : public MessageListener
+{
+ Monitor lock;
+ std::vector<Message> messages;
+
+ void received(Message& msg)
+ {
+ Monitor::ScopedLock l(lock);
+ messages.push_back(msg);
+ lock.notifyAll();
+ }
+
+ void waitFor(const uint n)
+ {
+ Monitor::ScopedLock l(lock);
+ while (messages.size() < n) {
+ lock.wait();
+ }
+ }
+};
+
+struct ClientSessionFixture : public ProxySessionFixture
+{
+ void declareSubscribe(const string& q="odd_blue",
+ const string& dest="xml")
+ {
+ session.queueDeclare(queue=q);
+ session.messageSubscribe(queue=q, destination=dest, acquireMode=1);
+ session.messageFlow(destination=dest, unit=0, value=0xFFFFFFFF);//messages
+ session.messageFlow(destination=dest, unit=1, value=0xFFFFFFFF);//bytes
+ }
+};
+
+// ########### START HERE ####################################
+
+QPID_AUTO_TEST_CASE(testXmlBinding) {
+ ClientSessionFixture f;
+
+ SubscriptionManager subscriptions(f.session);
+ SubscribedLocalQueue localQueue(subscriptions);
+
+ f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml");
+ f.session.queueDeclare(qpid::client::arg::queue="odd_blue");
+ subscriptions.subscribe(localQueue, "odd_blue");
+
+ FieldTable binding;
+ binding.setString("xquery", "declare variable $color external;"
+ "(./message/id mod 2 = 1) and ($color = 'blue')");
+ f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding);
+
+ Message message;
+ message.getDeliveryProperties().setRoutingKey("query_name");
+
+ message.getHeaders().setString("color", "blue");
+ string m = "<message><id>1</id></message>";
+ message.setData(m);
+
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+
+ Message m2 = localQueue.get(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+/**
+ * Ensure that multiple queues can be bound using the same routing key
+ */
+QPID_AUTO_TEST_CASE(testXMLBindMultipleQueues) {
+ ClientSessionFixture f;
+
+
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true);
+ f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true);
+
+ FieldTable blue;
+ blue.setString("xquery", "./colour = 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue);
+ FieldTable red;
+ red.setString("xquery", "./colour = 'red'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red);
+
+ Message sent1("<colour>blue</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent1, arg::destination="xml");
+
+ Message sent2("<colour>red</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent2, arg::destination="xml");
+
+ Message received;
+ BOOST_CHECK(f.subs.get(received, "blue"));
+ BOOST_CHECK_EQUAL(sent1.getData(), received.getData());
+ BOOST_CHECK(f.subs.get(received, "red"));
+ BOOST_CHECK_EQUAL(sent2.getData(), received.getData());
+}
+
+//### Test: Bad XML does not kill the server - and does not even
+// raise an exception, the content is not required to be XML.
+
+QPID_AUTO_TEST_CASE(testXMLSendBadXML) {
+ ClientSessionFixture f;
+
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\
+ ;
+ f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true);
+
+ FieldTable blue;
+ blue.setString("xquery", "./colour = 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\
+olour", arg::arguments=blue);
+ FieldTable red;
+ red.setString("xquery", "./colour = 'red'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-co\
+lour", arg::arguments=red);
+
+ Message sent1("<>colour>blue</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent1, arg::destination="xml");
+
+ BOOST_CHECK_EQUAL(1, 1);
+}
+
+
+//### Test: Bad XQuery does not kill the server, but does raise an exception
+
+QPID_AUTO_TEST_CASE(testXMLBadXQuery) {
+ ClientSessionFixture f;
+
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\
+ ;
+
+ try {
+ ScopedSuppressLogging sl; // Supress logging of error messages for expected error.
+ FieldTable blue;
+ blue.setString("xquery", "./colour $=! 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\
+olour", arg::arguments=blue);
+ }
+ catch (const InternalErrorException& e) {
+ return;
+ }
+ BOOST_ERROR("A bad XQuery must raise an exception when used in an XML Binding.");
+
+}
+
+
+//### Test: double, string, and integer field values can all be bound to queries
+
+QPID_AUTO_TEST_CASE(testXmlBindingUntyped) {
+ ClientSessionFixture f;
+
+ SubscriptionManager subscriptions(f.session);
+ SubscribedLocalQueue localQueue(subscriptions);
+
+ f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml");
+ f.session.queueDeclare(qpid::client::arg::queue="odd_blue");
+ subscriptions.subscribe(localQueue, "odd_blue");
+
+ FieldTable binding;
+ binding.setString("xquery",
+ "declare variable $s external;"
+ "declare variable $i external;"
+ "declare variable $d external;"
+ "$s = 'string' and $i = 1 and $d < 1");
+ f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding);
+
+ Message message;
+ message.getDeliveryProperties().setRoutingKey("query_name");
+
+ message.getHeaders().setString("s", "string");
+ message.getHeaders().setInt("i", 1);
+ message.getHeaders().setDouble("d", 0.5);
+ string m = "<message>Hi, Mom!</message>";
+ message.setData(m);
+
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+
+ Message m2 = localQueue.get(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+
+//### Test: double, string, and integer field values can all be bound to queries
+
+QPID_AUTO_TEST_CASE(testXmlBindingTyped) {
+ ClientSessionFixture f;
+
+ SubscriptionManager subscriptions(f.session);
+ SubscribedLocalQueue localQueue(subscriptions);
+
+ f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml");
+ f.session.queueDeclare(qpid::client::arg::queue="odd_blue");
+ subscriptions.subscribe(localQueue, "odd_blue");
+
+ FieldTable binding;
+ binding.setString("xquery",
+ "declare variable $s as xs:string external;"
+ "declare variable $i as xs:integer external;"
+ "declare variable $d external;" // XQilla bug when declaring xs:float, xs:double types? Fine if untyped, acts as float.
+ "$s = 'string' and $i = 1 and $d < 1");
+ f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding);
+
+ Message message;
+ message.getDeliveryProperties().setRoutingKey("query_name");
+
+ message.getHeaders().setString("s", "string");
+ message.getHeaders().setInt("i", 1);
+ message.getHeaders().setDouble("d", 0.5);
+ string m = "<message>Hi, Mom!</message>";
+ message.setData(m);
+
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+
+ Message m2 = localQueue.get(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+
+//### Test: Each session can provide its own definition for a query name
+
+
+
+//### Test: Bindings persist, surviving broker restart
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py
new file mode 100755
index 0000000000..5e9a150d8f
--- /dev/null
+++ b/qpid/cpp/src/tests/acl.py
@@ -0,0 +1,1077 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import qpid
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import uuid4
+from qpid.testlib import TestBase010
+from qmf.console import Session
+from qpid.datatypes import Message
+import qpid.messaging
+
+class ACLFile:
+ def __init__(self, policy='data_dir/policy.acl'):
+ self.f = open(policy,'w')
+
+ def write(self,line):
+ self.f.write(line)
+
+ def close(self):
+ self.f.close()
+
+class ACLTests(TestBase010):
+
+ def get_session(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection.session(str(uuid4()))
+
+ def reload_acl(self):
+ acl = self.qmf.getObjects(_class="acl")[0]
+ return acl.reloadACLFile()
+
+ def get_acl_file(self):
+ return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
+
+ def setUp(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ TestBase010.setUp(self)
+ self.startQmf()
+ self.reload_acl()
+
+ def tearDown(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ self.reload_acl()
+ TestBase010.tearDown(self)
+
+ #=====================================
+ # ACL general tests
+ #=====================================
+
+ def test_deny_mode(self):
+ """
+ Test the deny all mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="deny_queue")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+ try:
+ session.exchange_bind(exchange="amq.direct", queue="deny_queue", binding_key="routing_key")
+ self.fail("ACL should deny queue bind request");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+
+ def test_allow_mode(self):
+ """
+ Test the allow all mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="allow_queue")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+ try:
+ session.exchange_bind(exchange="amq.direct", queue="allow_queue", binding_key="routing_key")
+ self.fail("ACL should deny queue bind request");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+
+
+ #=====================================
+ # ACL file format tests
+ #=====================================
+
+ def test_empty_groups(self):
+ """
+ Test empty groups
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl group\n')
+ aclf.write('acl group admins bob@QPID joe@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Insufficient tokens for acl definition",0,len(result.text)) == -1):
+ self.fail("ACL Reader should reject the acl file due to empty group name")
+
+ def test_illegal_acl_formats(self):
+ """
+ Test illegal acl formats
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl group admins bob@QPID joe@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Unknown ACL permission",0,len(result.text)) == -1):
+ self.fail(result)
+
+ def test_illegal_extension_lines(self):
+ """
+ Test illegal extension lines
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('group admins bob@QPID \n')
+ aclf.write(' \ \n')
+ aclf.write('joe@QPID \n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("contains an illegal extension",0,len(result.text)) == -1):
+ self.fail(result)
+
+ if (result.text.find("Non-continuation line must start with \"group\" or \"acl\"",0,len(result.text)) == -1):
+ self.fail(result)
+
+ def test_llegal_extension_lines(self):
+ """
+ Test proper extention lines
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe@EXAMPLE.com \\ \n') # should be allowed
+ aclf.write(' jack@EXAMPLE.com \\ \n') # should be allowed
+ aclf.write('jill@TEST.COM \\ \n') # should be allowed
+ aclf.write('host/123.example.com@TEST.COM\n') # should be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("ACL format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ def test_user_realm(self):
+ """
+ Test a user defined without a realm
+ Ex. group admin rajith
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group admin bob\n') # shouldn't be allowed
+ aclf.write('acl deny admin bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Username 'bob' must contain a realm",0,len(result.text)) == -1):
+ self.fail(result)
+
+ def test_allowed_chars_for_username(self):
+ """
+ Test a user defined without a realm
+ Ex. group admin rajith
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe@EXAMPLE.com\n') # should be allowed
+ aclf.write('group test2 jack_123-jill@EXAMPLE.com\n') # should be allowed
+ aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("ACL format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Username \"joe$H@EXAMPLE.com\" contains illegal characters",0,len(result.text)) == -1):
+ self.fail(result)
+
+ #=====================================
+ # ACL validation tests
+ #=====================================
+
+ def test_illegal_queue_policy(self):
+ """
+ Test illegal queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "ding is not a valid value for 'policytype', possible values are one of" \
+ " { 'ring' 'ring_strict' 'flow_to_disk' 'reject' }";
+ if (result.text != expected):
+ self.fail(result)
+
+ def test_illegal_queue_size(self):
+ """
+ Test illegal queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'maxqueuesize', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.text != expected):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'maxqueuesize', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.text != expected):
+ self.fail(result)
+
+
+ def test_illegal_queue_count(self):
+ """
+ Test illegal queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'maxqueuecount', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.text != expected):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'maxqueuecount', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.text != expected):
+ self.fail(result)
+
+
+ #=====================================
+ # ACL queue tests
+ #=====================================
+
+ def test_queue_allow_mode(self):
+ """
+ Test cases for queue acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl deny bob@QPID access queue name=q3\n')
+ aclf.write('acl deny bob@QPID purge queue name=q3\n')
+ aclf.write('acl deny bob@QPID delete queue name=q4\n')
+ aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ self.fail("ACL should deny queue create request with name=q1 durable=true passive=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=true qpid.policy_type=ring");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring_strict"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2 exclusive=true qpid.policy_type=ring_strict");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2, qpid.max_size=500 and qpid.max_count=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 100
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2, qpid.max_size=100 and qpid.max_count=200 ");
+ try:
+ session.queue_declare(queue="q3", exclusive=True)
+ session.queue_declare(queue="q4", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q3 and q4 with any parameter");
+
+ try:
+ session.queue_query(queue="q3")
+ self.fail("ACL should deny queue query request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q3")
+ self.fail("ACL should deny queue purge request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q4")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q4");
+
+ try:
+ session.queue_delete(queue="q4")
+ self.fail("ACL should deny queue delete request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q3");
+
+
+ def test_queue_deny_mode(self):
+ """
+ Test cases for queue acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl allow bob@QPID access queue name=q3\n')
+ aclf.write('acl allow bob@QPID purge queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q4\n')
+ aclf.write('acl allow bob@QPID delete queue name=q4\n')
+ aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q1 durable=true passive=true");
+
+ try:
+ session.queue_declare(queue="q1", durable=False, passive=False)
+ self.fail("ACL should deny queue create request with name=q1 durable=true passive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q2", exclusive=False)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2 maxqueuesize=500 maxqueuecount=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 100
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2 maxqueuesize=500 maxqueuecount=200");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q2 with exclusive=true policytype=ring");
+
+ try:
+ session.queue_declare(queue="q3")
+ session.queue_declare(queue="q4")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q3 and q4");
+
+ try:
+ session.queue_query(queue="q4")
+ self.fail("ACL should deny queue query request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q4")
+ self.fail("ACL should deny queue purge request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q3");
+
+ try:
+ session.queue_query(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue query request for q3");
+
+ try:
+ session.queue_delete(queue="q3")
+ self.fail("ACL should deny queue delete request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q4")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q4");
+
+ #=====================================
+ # ACL exchange tests
+ #=====================================
+
+ def test_exchange_acl_allow_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue="baz")
+
+ """
+ Test cases for exchange acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
+ aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.exchange_declare(exchange='myEx', type='direct')
+
+ try:
+ session.exchange_declare(exchange='testEx', durable=True, passive=True)
+ self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False)
+ except qpid.session.SessionException, e:
+ print e
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true");
+
+ try:
+ session.exchange_declare(exchange='ex1', type='direct')
+ self.fail("ACL should deny exchange create request with name=ex1 type=direct");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myXml', type='direct')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myXml with any parameter");
+
+ try:
+ session.exchange_query(name='myEx')
+ self.fail("ACL should deny exchange query request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ self.fail("ACL should deny exchange bound request for myEx with queuename=q1 and routing_key='rk1.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='amq.topic'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ self.fail("ACL should deny exchange delete request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange='myXml')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myXml");
+
+
+ def test_exchange_acl_deny_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='bar')
+
+ """
+ Test cases for exchange acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n')
+ aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl allow bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=True, passive=False)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ self.fail("ACL should deny exchange create request with name=myEx durable=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ self.fail("ACL should deny exchange query request for amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ self.fail("ACL should deny exchange bound request for amq.topic with queuename=q1 and routing_key='rk2.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='myEx')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='myEx'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'");
+
+ try:
+ session.exchange_delete(exchange='myXml')
+ self.fail("ACL should deny exchange delete request for myXml");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myEx");
+
+ def test_create_and_delete_exchange_via_qmf(self):
+ """
+ Test acl is enforced when creating/deleting via QMF
+ methods. Note that in order to be able to send the QMF methods
+ and receive the responses a significant amount of permissions
+ need to be enabled (TODO: can the set below be narrowed down
+ at all?)
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl allow admin@QPID delete exchange\n')
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+ bob.create_exchange("my-exchange") #should pass
+ #cleanup by deleting exchange
+ try:
+ bob.delete_exchange("my-exchange") #should fail
+ self.fail("ACL should deny exchange delete request for my-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ admin = BrokerAdmin(self.config.broker, "admin", "admin")
+ admin.delete_exchange("my-exchange") #should pass
+
+ anonymous = BrokerAdmin(self.config.broker)
+ try:
+ anonymous.create_exchange("another-exchange") #should fail
+ self.fail("ACL should deny exchange create request for another-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+
+
+ #=====================================
+ # ACL consume tests
+ #=====================================
+
+ def test_consume_allow_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID consume queue name=q1\n')
+ aclf.write('acl deny bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q2', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q2'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq1')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q3");
+
+
+ def test_consume_deny_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID consume queue name=q1\n')
+ aclf.write('acl allow bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl allow anonymous all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ session.message_subscribe(queue='q2', destination='myq2')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q1 and q2");
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq3')
+ self.fail("ACL should deny subscription for queue='q3'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+
+ #=====================================
+ # ACL publish tests
+ #=====================================
+
+ def test_publish_acl_allow_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n')
+ aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange myEx with routing key rk1");
+
+
+ props = session.delivery_properties(routing_key="rk2")
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk2");
+
+
+ def test_publish_acl_deny_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
+ aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n')
+ aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n')
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl allow anonymous all all \n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk2")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk2");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.topic with any routing key");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2");
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=myEx routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1");
+
+class BrokerAdmin:
+ def __init__(self, broker, username=None, password=None):
+ self.connection = qpid.messaging.Connection(broker)
+ if username:
+ self.connection.username = username
+ self.connection.password = password
+ self.connection.sasl_mechanisms = "PLAIN"
+ self.connection.open()
+ self.session = self.connection.session()
+ self.sender = self.session.sender("qmf.default.direct/broker")
+ self.reply_to = "responses-#; {create:always}"
+ self.receiver = self.session.receiver(self.reply_to)
+
+ def invoke(self, method, arguments):
+ content = {
+ "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"},
+ "_method_name": method,
+ "_arguments": arguments
+ }
+ request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+ request.properties["x-amqp-0-10.app-id"] = "qmf2"
+ request.properties["qmf.opcode"] = "_method_request"
+ self.sender.send(request)
+ response = self.receiver.fetch()
+ self.session.acknowledge()
+ if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if response.properties['qmf.opcode'] == '_method_response':
+ return response.content['_arguments']
+ elif response.properties['qmf.opcode'] == '_exception':
+ raise Exception(response.content['_values'])
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+ def create_exchange(self, name, exchange_type=None, options={}):
+ properties = options
+ if exchange_type: properties["exchange_type"] = exchange_type
+ self.invoke("create", {"type": "exchange", "name":name, "properties":properties})
+
+ def create_queue(self, name, properties={}):
+ self.invoke("create", {"type": "queue", "name":name, "properties":properties})
+
+ def delete_exchange(self, name):
+ self.invoke("delete", {"type": "exchange", "name":name})
+
+ def delete_queue(self, name):
+ self.invoke("delete", {"type": "queue", "name":name})
diff --git a/qpid/cpp/src/tests/ais_check b/qpid/cpp/src/tests/ais_check
new file mode 100755
index 0000000000..92eaa9dd39
--- /dev/null
+++ b/qpid/cpp/src/tests/ais_check
@@ -0,0 +1,34 @@
+#!/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.
+#
+
+srcdir=`dirname $0`
+
+# Check AIS requirements and run tests if found.
+ps -u root | grep 'aisexec\|corosync' >/dev/null || {
+ echo WARNING: Skipping cluster tests, the aisexec or corosync daemon is not running.
+ exit 0; # A warning, not a failure.
+}
+
+# Execute command with the ais group set if user is a member.
+with_ais_group() {
+ if id -nG | grep '\<ais\>' >/dev/null; then sg ais -c "$*"
+ else "$@"
+ fi
+}
diff --git a/qpid/cpp/src/tests/ais_test.cpp b/qpid/cpp/src/tests/ais_test.cpp
new file mode 100644
index 0000000000..00c61242e4
--- /dev/null
+++ b/qpid/cpp/src/tests/ais_test.cpp
@@ -0,0 +1,23 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+// Defines test_main function to link with actual unit test code.
+#define BOOST_AUTO_TEST_MAIN // Boost 1.33
+#define BOOST_TEST_MAIN
+#include "unit_test.h"
+
diff --git a/qpid/cpp/src/tests/allhosts b/qpid/cpp/src/tests/allhosts
new file mode 100755
index 0000000000..e43571aed4
--- /dev/null
+++ b/qpid/cpp/src/tests/allhosts
@@ -0,0 +1,77 @@
+#!/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.
+#
+
+usage() {
+ echo "Usage: $0 [options] command.
+Run a command on each host in \$HOSTS.
+Options:
+ -l USER passed to ssh - run as USER.
+ -t passed to ssh - create a terminal.
+ -b run in background, wait for commands to complete.
+ -d run in background, don't wait for commands to complete.
+ -s SECONDS sleep between starting commands.
+ -q don't print banner lines for each host.
+ -o SUFFIX log output of each command to <host>.SUFFIX
+"
+ exit 1
+}
+
+while getopts "tl:bs:dqo:" opt; do
+ case $opt in
+ l) SSHOPTS="-l$OPTARG $SSHOPTS" ;;
+ t) SSHOPTS="-t $SSHOPTS" ;;
+ b) BACKGROUND=1 ;;
+ d) BACKGROUND=1; DISOWN=1 ;;
+ s) SLEEP="sleep $OPTARG" ;;
+ q) NOBANNER=1 ;;
+ o) SUFFIX=$OPTARG ;;
+ *) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+test "$*" || usage;
+
+OK_FILE=`mktemp` # Will be deleted if anything goes wrong.
+trap "rm -f $OK_FILE" EXIT
+
+do_ssh() {
+ h=$1; shift
+ if test $SUFFIX ; then ssh $SSHOPTS $h "$@" &> $h.$SUFFIX
+ else ssh $SSHOPTS $h "$@"; fi || rm -rf $OK_FILE;
+}
+
+for h in $HOSTS ; do
+ test "$NOBANNER" || echo "== ssh $SSHOPTS $h $@ =="
+ if [ "$BACKGROUND" = 1 ]; then
+ do_ssh $h "$@" &
+ CHILDREN="$! $CHILDREN"
+ else
+ do_ssh $h "$@"
+ fi
+ $SLEEP
+done
+
+if [ "$DISOWN" = 1 ]; then
+ for c in $CHILDREN; do disown $c; done
+else
+ wait
+fi
+
+test -f $OK_FILE
diff --git a/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/qpid/cpp/src/tests/amqp_0_10/Map.cpp
new file mode 100644
index 0000000000..ffb235829e
--- /dev/null
+++ b/qpid/cpp/src/tests/amqp_0_10/Map.cpp
@@ -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.
+ *
+ */
+#include "amqp_0_10/unit_test.h"
+#include "qpid/amqp_0_10/Map.h"
+#include "qpid/amqp_0_10/Array.h"
+#include "qpid/amqp_0_10/Struct32.h"
+#include "qpid/amqp_0_10/UnknownType.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include <iostream>
+
+using namespace qpid::amqp_0_10;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(MapTestSuite)
+
+ QPID_AUTO_TEST_CASE(testGetSet) {
+ MapValue v;
+ v = Str8("foo");
+ BOOST_CHECK(v.get<Str8>());
+ BOOST_CHECK(!v.get<uint8_t>());
+ BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo");
+
+ v = uint8_t(42);
+ BOOST_CHECK(!v.get<Str8>());
+ BOOST_CHECK(v.get<uint8_t>());
+ BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42);
+
+ v = uint16_t(12);
+ BOOST_CHECK(v.get<uint16_t>());
+ BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12);
+}
+
+template <class R> struct TestVisitor : public MapValue::Visitor<R> {
+ template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); }
+ R operator()(const R& r) const { return r; }
+};
+
+QPID_AUTO_TEST_CASE(testVisit) {
+ MapValue v;
+ v = Str8("foo");
+ BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo");
+ v = Uint16(42);
+ BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42);
+ try {
+ v.apply_visitor(TestVisitor<bool>());
+ BOOST_FAIL("Expecting exception");
+ }
+ catch(const MapValue::BadTypeException&) {}
+}
+
+
+QPID_AUTO_TEST_CASE(testEncodeMapValue) {
+ MapValue mv;
+ std::string data;
+ mv = Str8("hello");
+ Codec::encode(back_inserter(data))(mv);
+ BOOST_CHECK_EQUAL(data.size(), Codec::size(mv));
+ MapValue mv2;
+ Codec::decode(data.begin())(mv2);
+ BOOST_CHECK_EQUAL(mv2.getCode(), 0x85);
+ BOOST_REQUIRE(mv2.get<Str8>());
+ BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello");
+}
+
+QPID_AUTO_TEST_CASE(testEncode) {
+ Map map;
+ std::string data;
+ map["A"] = true;
+ map["b"] = Str8("hello");
+ Codec::encode(back_inserter(data))(map);
+ BOOST_CHECK_EQUAL(Codec::size(map), data.size());
+ Map map2;
+ Codec::decode(data.begin())(map2);
+ BOOST_CHECK_EQUAL(map.size(), 2u);
+ BOOST_CHECK(map["A"].get<bool>());
+ BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello");
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
new file mode 100644
index 0000000000..f54ee0da22
--- /dev/null
+++ b/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
@@ -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.
+ *
+ */
+
+#include "amqp_0_10/unit_test.h"
+#include "qpid/amqp_0_10/ProxyTemplate.h"
+#include <boost/any.hpp>
+
+QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite)
+
+using namespace qpid::amqp_0_10;
+
+struct ToAny {
+ template <class T>
+ boost::any operator()(const T& t) { return boost::any(t); }
+};
+
+struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {};
+
+QPID_AUTO_TEST_CASE(testAnyProxy) {
+ AnyProxy p;
+ boost::any a=p.connectionTune(1,2,3,4);
+ BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name());
+ connection::Tune* tune=boost::any_cast<connection::Tune>(&a);
+ BOOST_REQUIRE(tune);
+ BOOST_CHECK_EQUAL(tune->channelMax, 1u);
+ BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u);
+ BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u);
+ BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u);
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/apply.cpp b/qpid/cpp/src/tests/amqp_0_10/apply.cpp
new file mode 100644
index 0000000000..0aa4421791
--- /dev/null
+++ b/qpid/cpp/src/tests/amqp_0_10/apply.cpp
@@ -0,0 +1,99 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "amqp_0_10/unit_test.h"
+#include "qpid/amqp_0_10/specification.h"
+#include "qpid/amqp_0_10/ApplyControl.h"
+
+QPID_AUTO_TEST_SUITE(VisitorTestSuite)
+
+using namespace qpid::amqp_0_10;
+
+struct GetCode : public ApplyFunctor<uint8_t> {
+ template <class T> uint8_t operator()(const T&) const { return T::CODE; }
+};
+
+struct SetChannelMax : ApplyFunctor<void> {
+ template <class T> void operator()(T&) const { BOOST_FAIL(""); }
+ void operator()(connection::Tune& t) const { t.channelMax=42; }
+};
+
+struct TestFunctor {
+ typedef bool result_type;
+ bool operator()(const connection::Tune& tune) {
+ BOOST_CHECK_EQUAL(tune.channelMax, 1u);
+ BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
+ return true;
+ }
+ template <class T>
+ bool operator()(const T&) { return false; }
+};
+
+QPID_AUTO_TEST_CASE(testApply) {
+ connection::Tune tune(1,2,3,4);
+ Control* p = &tune;
+
+ // boost oddity - without the cast we get undefined symbol errors.
+ BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE);
+
+ TestFunctor tf;
+ BOOST_CHECK(apply(tf, *p));
+
+ connection::Start start;
+ p = &start;
+ BOOST_CHECK(!apply(tf, *p));
+
+ apply(SetChannelMax(), tune);
+ BOOST_CHECK_EQUAL(tune.channelMax, 42);
+}
+
+struct VoidTestFunctor {
+ typedef void result_type;
+
+ int code;
+ VoidTestFunctor() : code() {}
+
+ void operator()(const connection::Tune& tune) {
+ BOOST_CHECK_EQUAL(tune.channelMax, 1u);
+ BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
+ code=connection::Tune::CODE;
+ }
+ template <class T>
+ void operator()(const T&) { code=0xFF; }
+};
+
+QPID_AUTO_TEST_CASE(testApplyVoid) {
+ connection::Tune tune(1,2,3,4);
+ Control* p = &tune;
+ VoidTestFunctor tf;
+ apply(tf, *p);
+ BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code);
+
+ connection::Start start;
+ p = &start;
+ apply(tf, *p);
+ BOOST_CHECK_EQUAL(0xFF, tf.code);
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/handlers.cpp b/qpid/cpp/src/tests/amqp_0_10/handlers.cpp
new file mode 100644
index 0000000000..91bb304a17
--- /dev/null
+++ b/qpid/cpp/src/tests/amqp_0_10/handlers.cpp
@@ -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.
+ *
+ */
+
+#include "amqp_0_10/unit_test.h"
+#include "qpid/Exception.h"
+#include "qpid/amqp_0_10/Unit.h"
+#include "qpid/amqp_0_10/ControlHolder.h"
+#include "qpid/amqp_0_10/CommandHolder.h"
+#include "qpid/amqp_0_10/handlers.h"
+#include "qpid/amqp_0_10/specification.h"
+
+QPID_AUTO_TEST_SUITE(handler_tests)
+
+using namespace qpid::amqp_0_10;
+using namespace std;
+
+string called; // Set by called handler function
+
+// Note on handlers:
+//
+// Control and Command handlers are separate, both behave the same way,
+// so substitute "control or command" for command in the following.
+//
+// Command handlers derive from CommandHandler and implement functions
+// for all the commands they handle. Handling an unimplemented command
+// will raise NotImplementedException.
+//
+// Using virtual inheritance from CommandHandler allows multiple
+// handlers to be aggregated into one with multiple inheritance,
+// See test code for example.
+//
+// E.g. the existing broker model would have two control handlers:
+// - ConnectionHandler: ControlHandler for connection controls.
+// - SessionHandler: ControlHandler for session controls.
+// It would have class-command handlers for each AMQP class:
+// - QueueHandler, MessageHandler etc.. handle each class.
+// And an aggregate handler in place of BrokerAdapter
+// - BrokerCommandHandler: public QueueHandler, MessageHandler ...
+//
+// In other applications (e.g. cluster) any combination of commands
+// can be handled by a given handler. It _might_ simplify the code
+// to collaps ConnectionHandler and SessionHandler into a single
+// ControlHandler (or it might not.)
+
+struct TestExecutionHandler : public virtual CommandHandler {
+ void executionSync() { called = "executionSync"; }
+ // ... etc. for all execution commands
+};
+
+struct TestMessageHandler : public virtual CommandHandler {
+ void messageCancel(const Str8&) { called="messageCancel"; }
+ // ... etc.
+};
+
+// Aggregate handler for all recognised commands.
+struct TestCommandHandler :
+ public TestExecutionHandler,
+ public TestMessageHandler
+ // ... etc. handlers for all command classes.
+{}; // Nothing to do.
+
+
+// Sample unit handler, written as a static_visitor.
+// Note it could equally be written with if/else statements
+// in handle.
+//
+struct TestUnitHandler : public boost::static_visitor<void> {
+ TestCommandHandler handler;
+ void handle(const Unit& u) { u.applyVisitor(*this); }
+
+ void operator()(const Body&) { called="Body"; }
+ void operator()(const Header&) { called="Header"; }
+ void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); }
+ void operator()(const CommandHolder& c) { c.invoke(handler); }
+};
+
+QPID_AUTO_TEST_CASE(testHandlers) {
+ TestUnitHandler handler;
+ Unit u;
+
+ u = Body();
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("Body", called);
+
+ u = Header();
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("Header", called);
+
+ // in_place<Foo>(...) is equivalent to Foo(...) but
+ // constructs Foo directly in the holder, avoiding
+ // a copy.
+
+ u = CommandHolder(in_place<execution::Sync>());
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("executionSync", called);
+
+ u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array()));
+ try {
+ handler.handle(u);
+ } catch (const qpid::Exception&) {}
+
+ u = CommandHolder(in_place<message::Cancel>(Str8()));
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("messageCancel", called);
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp
new file mode 100644
index 0000000000..975d6206ec
--- /dev/null
+++ b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp
@@ -0,0 +1,429 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "amqp_0_10/unit_test.h"
+#include "amqp_0_10/allSegmentTypes.h"
+
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/Buffer.h"
+
+#include "qpid/amqp_0_10/Packer.h"
+#include "qpid/amqp_0_10/built_in_types.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include "qpid/amqp_0_10/specification.h"
+#include "qpid/amqp_0_10/ControlHolder.h"
+#include "qpid/amqp_0_10/Struct32.h"
+#include "qpid/amqp_0_10/FrameHeader.h"
+#include "qpid/amqp_0_10/Map.h"
+#include "qpid/amqp_0_10/Unit.h"
+#include "allSegmentTypes.h"
+
+#include <boost/test/test_case_template.hpp>
+#include <boost/type_traits/is_arithmetic.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/optional.hpp>
+#include <boost/mpl/vector.hpp>
+#include <boost/mpl/back_inserter.hpp>
+#include <boost/mpl/copy.hpp>
+#include <boost/mpl/empty_sequence.hpp>
+#include <boost/current_function.hpp>
+#include <iterator>
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <netinet/in.h>
+
+// Missing operators needed for tests.
+namespace boost {
+template <class T, size_t N>
+std::ostream& operator<<(std::ostream& out, const array<T,N>& a) {
+ std::ostream_iterator<T> o(out, " ");
+ std::copy(a.begin(), a.end(), o);
+ return out;
+}
+} // boost
+
+QPID_AUTO_TEST_SUITE(SerializeTestSuite)
+
+using namespace std;
+namespace mpl=boost::mpl;
+using namespace qpid::amqp_0_10;
+using qpid::framing::in_place;
+
+template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; };
+template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; };
+template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; };
+
+typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes;
+typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes;
+typedef mpl::vector<Double, Float>::type FloatTypes;
+typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes;
+typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes;
+
+typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes;
+typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes;
+
+// TODO aconway 2008-02-20: should test 64 bit integrals for order also.
+QPID_AUTO_TEST_CASE(testNetworkByteOrder) {
+ string data;
+
+ uint32_t l = 0x11223344;
+ Codec::encode(std::back_inserter(data))(l);
+ uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data());
+ uint32_t l2 = ntohl(enc);
+ BOOST_CHECK_EQUAL(l, l2);
+
+ data.clear();
+ uint16_t s = 0x1122;
+ Codec::encode(std::back_inserter(data))(s);
+ uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data()));
+ BOOST_CHECK_EQUAL(s, s2);
+}
+
+QPID_AUTO_TEST_CASE(testSetLimit) {
+ typedef Codec::Encoder<back_insert_iterator<string> > Encoder;
+ string data;
+ Encoder encode(back_inserter(data), 3);
+ encode('1')('2')('3');
+ try {
+ encode('4');
+ BOOST_FAIL("Expected exception");
+ } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception
+ BOOST_CHECK_EQUAL(data, "123");
+}
+
+QPID_AUTO_TEST_CASE(testScopedLimit) {
+ typedef Codec::Encoder<back_insert_iterator<string> > Encoder;
+ string data;
+ Encoder encode(back_inserter(data), 10);
+ encode(Str8("123")); // 4 bytes
+ {
+ Encoder::ScopedLimit l(encode, 3);
+ encode('a')('b')('c');
+ try {
+ encode('d');
+ BOOST_FAIL("Expected exception");
+ } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception
+ }
+ BOOST_CHECK_EQUAL(data, "\003123abc");
+ encode('x')('y')('z');
+ try {
+ encode('!');
+ BOOST_FAIL("Expected exception");
+ } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception
+ BOOST_CHECK_EQUAL(data.size(), 10u);
+}
+
+// Assign test values to the various types.
+void testValue(bool& b) { b = true; }
+void testValue(Bit&) { }
+template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; }
+void testValue(CharUtf32& c) { c = 43; }
+void testValue(long long& l) { l = 0x012345; }
+void testValue(Datetime& dt) { dt = qpid::sys::now(); }
+void testValue(Uuid& uuid) { uuid=Uuid(true); }
+template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; }
+void testValue(SequenceNo& s) { s = 42; }
+template <size_t N> void testValue(Bin<N>& a) { a.assign(42); }
+template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) {
+ char msg[]="foobar";
+ s.assign(msg, msg+sizeof(msg));
+}
+void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; }
+void testValue(Str8& s) { s = "foobar"; }
+void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); }
+
+//typedef mpl::vector<Str8, Str16>::type TestTypes;
+/*BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes)
+{
+ string data;
+ T t;
+ testValue(t);
+ Codec::encode(std::back_inserter(data))(t);
+
+ BOOST_CHECK_EQUAL(Codec::size(t), data.size());
+
+ T t2;
+ Codec::decode(data.begin())(t2);
+ BOOST_CHECK_EQUAL(t,t2);
+}
+*/
+
+struct TestMe {
+ bool encoded, decoded;
+ char value;
+ TestMe(char v) : encoded(), decoded(), value(v) {}
+ template <class S> void encode(S& s) const {
+ const_cast<TestMe*>(this)->encoded=true; s(value);
+ }
+ template <class S> void decode(S& s) { decoded=true; s(value); }
+ template <class S> void serialize(S& s) { s.split(*this); }
+};
+
+QPID_AUTO_TEST_CASE(testSplit) {
+ string data;
+ TestMe t1('x');
+ Codec::encode(std::back_inserter(data))(t1);
+ BOOST_CHECK(t1.encoded);
+ BOOST_CHECK(!t1.decoded);
+ BOOST_CHECK_EQUAL(data, "x");
+
+ TestMe t2('y');
+ Codec::decode(data.begin())(t2);
+ BOOST_CHECK(!t2.encoded);
+ BOOST_CHECK(t2.decoded);
+ BOOST_CHECK_EQUAL(t2.value, 'x');
+}
+
+QPID_AUTO_TEST_CASE(testControlEncodeDecode) {
+ string data;
+ Control::Holder h(in_place<connection::Tune>(1,2,3,4));
+ Codec::encode(std::back_inserter(data))(h);
+
+ BOOST_CHECK_EQUAL(data.size(), Codec::size(h));
+
+ Codec::Decoder<string::iterator> decode(data.begin());
+ Control::Holder h2;
+ decode(h2);
+
+ BOOST_REQUIRE(h2.get());
+ BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE);
+ BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE));
+ connection::Tune& tune=static_cast<connection::Tune&>(*h2.get());
+ BOOST_CHECK_EQUAL(tune.channelMax, 1u);
+ BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
+ BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
+}
+
+QPID_AUTO_TEST_CASE(testStruct32) {
+ message::DeliveryProperties dp;
+ dp.priority=message::MEDIUM;
+ dp.routingKey="foo";
+ Struct32 s(dp);
+ string data;
+ Codec::encode(back_inserter(data))(s);
+
+ uint32_t structSize; // Starts with size
+ Codec::decode(data.begin())(structSize);
+ BOOST_CHECK_EQUAL(structSize, Codec::size(dp) + 2); // +2 for code
+ BOOST_CHECK_EQUAL(structSize, data.size()-4); // encoded body
+
+ BOOST_CHECK_EQUAL(data.size(), Codec::size(s));
+ Struct32 s2;
+ Codec::decode(data.begin())(s2);
+ message::DeliveryProperties* dp2 = s2.getIf<message::DeliveryProperties>();
+ BOOST_REQUIRE(dp2);
+ BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM);
+ BOOST_CHECK_EQUAL(dp2->routingKey, "foo");
+}
+
+QPID_AUTO_TEST_CASE(testStruct32Unknown) {
+ // Verify we can recode an unknown struct unchanged.
+ Struct32 s;
+ string data;
+ Codec::encode(back_inserter(data))(uint32_t(10));
+ data.append(10, 'X');
+ Codec::decode(data.begin())(s);
+ string data2;
+ Codec::encode(back_inserter(data2))(s);
+ BOOST_CHECK_EQUAL(data.size(), data2.size());
+ BOOST_CHECK_EQUAL(data, data2);
+}
+
+struct DummyPacked {
+ static const uint8_t PACK=1;
+ boost::optional<char> i, j;
+ char k;
+ Bit l,m;
+ DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {}
+ template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); }
+};
+
+Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); }
+
+QPID_AUTO_TEST_CASE(testPackBits) {
+ DummyPacked d('a','b','c');
+ BOOST_CHECK_EQUAL(packBits(d), 7u);
+ d.j = boost::none;
+ BOOST_CHECK_EQUAL(packBits(d), 5u);
+ d.m = true;
+ BOOST_CHECK_EQUAL(packBits(d), 0x15u);
+}
+
+
+QPID_AUTO_TEST_CASE(testPacked) {
+ string data;
+
+ Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c');
+ BOOST_CHECK_EQUAL(data, "abc");
+ data.clear();
+
+ DummyPacked dummy('a','b','c');
+
+ Codec::encode(back_inserter(data))(dummy);
+ BOOST_CHECK_EQUAL(data.size(), 4u);
+ BOOST_CHECK_EQUAL(data, string("\007abc"));
+ data.clear();
+
+ dummy.i = boost::none;
+ Codec::encode(back_inserter(data))(dummy);
+ BOOST_CHECK_EQUAL(data, string("\6bc"));
+ data.clear();
+
+ const char* missing = "\5xy";
+ Codec::decode(missing)(dummy);
+ BOOST_CHECK(dummy.i);
+ BOOST_CHECK_EQUAL(*dummy.i, 'x');
+ BOOST_CHECK(!dummy.j);
+ BOOST_CHECK_EQUAL(dummy.k, 'y');
+}
+
+QPID_AUTO_TEST_CASE(testUnitControl) {
+ string data;
+ Control::Holder h(in_place<connection::Tune>(1,2,3,4));
+ Codec::encode(std::back_inserter(data))(h);
+
+ Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL));
+ Codec::decode(data.begin())(unit);
+
+ BOOST_REQUIRE(unit.get<ControlHolder>());
+
+ string data2;
+ Codec::encode(back_inserter(data2))(unit);
+
+ BOOST_CHECK_EQUAL(data, data2);
+}
+
+QPID_AUTO_TEST_CASE(testArray) {
+ ArrayDomain<char> a;
+ a.resize(3, 'x');
+ string data;
+ Codec::encode(back_inserter(data))(a);
+
+ ArrayDomain<char> b;
+ Codec::decode(data.begin())(b);
+ BOOST_CHECK_EQUAL(b.size(), 3u);
+ string data3;
+ Codec::encode(back_inserter(data3))(a);
+ BOOST_CHECK_EQUAL(data, data3);
+
+ Array x;
+ Codec::decode(data.begin())(x);
+ BOOST_CHECK_EQUAL(x.size(), 3u);
+ BOOST_CHECK_EQUAL(x[0].size(), 1u);
+ BOOST_CHECK_EQUAL(*x[0].begin(), 'x');
+ BOOST_CHECK_EQUAL(*x[2].begin(), 'x');
+
+ string data2;
+ Codec::encode(back_inserter(data2))(x);
+ BOOST_CHECK_EQUAL(data,data2);
+}
+
+QPID_AUTO_TEST_CASE(testStruct) {
+ string data;
+
+ message::DeliveryProperties dp;
+ BOOST_CHECK(!dp.discardUnroutable);
+ dp.immediate = true;
+ dp.redelivered = false;
+ dp.priority = message::MEDIUM;
+ dp.exchange = "foo";
+
+ Codec::encode(back_inserter(data))(dp);
+ // Skip 4 bytes size, little-endian decode for pack bits.
+ uint16_t encodedBits=uint8_t(data[5]);
+ encodedBits <<= 8;
+ encodedBits += uint8_t(data[4]);
+ BOOST_CHECK_EQUAL(encodedBits, packBits(dp));
+
+ data.clear();
+ Struct32 h(dp);
+ Codec::encode(back_inserter(data))(h);
+
+ Struct32 h2;
+ Codec::decode(data.begin())(h2);
+ BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE));
+ BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE));
+ message::DeliveryProperties* dp2 =
+ dynamic_cast<message::DeliveryProperties*>(h2.get());
+ BOOST_CHECK(dp2);
+ BOOST_CHECK(!dp2->discardUnroutable);
+ BOOST_CHECK(dp2->immediate);
+ BOOST_CHECK(!dp2->redelivered);
+ BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM);
+ BOOST_CHECK_EQUAL(dp2->exchange, "foo");
+}
+
+struct RecodeUnit {
+ template <class T>
+ void operator() (const T& t) {
+ BOOST_MESSAGE(BOOST_CURRENT_FUNCTION << " called with: " << t);
+ using qpid::framing::Buffer;
+ using qpid::framing::AMQFrame;
+
+ session::Header sh;
+ BOOST_CHECK_EQUAL(Codec::size(sh), 2u);
+
+ // Encode unit.
+ Unit u(t);
+ string data;
+ Codec::encode(back_inserter(data))(u.getHeader())(u);
+ data.push_back(char(0xCE)); // Preview end-of-frame
+
+ // Decode AMQFrame
+ Buffer buf(&data[0], data.size());
+ AMQFrame f;
+ f.decode(buf);
+ BOOST_MESSAGE("AMQFrame decoded: " << f);
+ // Encode AMQFrame
+ string data2(f.size(), ' ');
+ Buffer buf2(&data2[0], data.size());
+ f.encode(buf2);
+
+ // Verify encoded by unit == encoded by AMQFrame
+ BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION);
+
+ // Decode unit
+ // FIXME aconway 2008-04-15: must set limit to decode a header.
+ Codec::Decoder<string::iterator> decode(data2.begin(), data2.size()-1);
+
+ FrameHeader h;
+ decode(h);
+ BOOST_CHECK_EQUAL(u.getHeader(), h);
+ Unit u2(h);
+ decode(u2);
+
+ // Re-encode unit
+ string data3;
+ Codec::encode(back_inserter(data3))(u2.getHeader())(u2);
+ data3.push_back(char(0xCE)); // Preview end-of-frame
+
+ BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testSerializeAllSegmentTypes) {
+ RecodeUnit recode;
+ allSegmentTypes(recode);
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/background.ps1 b/qpid/cpp/src/tests/background.ps1
new file mode 100644
index 0000000000..36e9e4e6e9
--- /dev/null
+++ b/qpid/cpp/src/tests/background.ps1
@@ -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.
+#
+
+# Run a PowerShell scriptblock in a background process.
+param(
+ [scriptblock] $script # scriptblock to run
+)
+
+# break out of the script on any errors
+trap { break }
+
+# In order to pass a scriptblock to another powershell instance, it must
+# be encoded to pass through the command line.
+$encodedScript = [convert]::ToBase64String(
+ [Text.Encoding]::Unicode.GetBytes([string] $script))
+
+#$p = new-object System.Diagnostics.Process
+$si = new-object System.Diagnostics.ProcessStartInfo
+$si.WorkingDirectory = $pwd
+$si.FileName = (get-command powershell.exe).Definition
+$si.Arguments = "-encodedCommand $encodedScript"
+
+###### debugging setup
+#$si.CreateNoWindow = $true
+# UseShellExecute false required for RedirectStandard(Error, Output)
+#$si.UseShellExecute = $false
+#$si.RedirectStandardError = $true
+#$si.RedirectStandardOutput = $true
+######
+$si.UseShellExecute = $true
+
+##### Debugging, instead of the plain Start() above.
+#$output = [io.File]::AppendText("start.out")
+#$error = [io.File]::AppendText("start.err")
+$p = [System.Diagnostics.Process]::Start($si)
+#$output.WriteLine($p.StandardOutput.ReadToEnd())
+#$error.WriteLine($p.StandardError.ReadToEnd())
+#$p.WaitForExit()
+#$output.Close()
diff --git a/qpid/cpp/src/tests/benchmark b/qpid/cpp/src/tests/benchmark
new file mode 100755
index 0000000000..c075837847
--- /dev/null
+++ b/qpid/cpp/src/tests/benchmark
@@ -0,0 +1,95 @@
+#!/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.
+#
+# A basic "benchmark" to generate performacne samples of throughput
+# and latency against a single cluster member while they are replicating.
+#
+# Must be run in the qpid src/tests build directory.
+#
+
+usage() {
+cat <<EOF
+Usage: $0 [options] -- client hosts --- broker hosts
+Read the script for options.
+EOF
+}
+# Defaults
+TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts.
+SCRIPTDIR=${SCRIPTDIR:-`dirname $0`} # Path to local test scripts directory.
+SAMPLES=10 # Runs of each test.
+COUNT=${COUNT:-10000} # Count for pub/sub tests.
+SIZE=${SIZE:-600} # Size of messages
+ECHO=${ECHO:-1000} # Count for echo test.
+NSUBS=${NSUBS:-4}
+NPUBS=${NPUBS:-4}
+
+collect() { eval $COLLECT=\""\$$COLLECT $*"\"; }
+COLLECT=ARGS
+while test $# -gt 0; do
+ case $1 in
+ --testdir) TESTDIR=$2 ; shift 2 ;;
+ --samples) SAMPLES=$2 ; shift 2 ;;
+ --count) COUNT=$2 ; shift 2 ;;
+ --echos) ECHO=$2 ; shift 2 ;;
+ --size) SIZE=$2 ; shift 2 ;;
+ --nsubs) NSUBS=$2 ; shift 2 ;;
+ --npubs) NPUBS=$2 ; shift 2 ;;
+ --) COLLECT=CLIENTARG; shift ;;
+ ---) COLLECT=BROKERARG; shift;;
+ *) collect $1; shift ;;
+ esac
+done
+
+CLIENTS=${CLIENTARG:-$CLIENTS}
+BROKERS=${BROKERARG:-$BROKERS}
+test -z "$CLIENTS" && { echo "Must specify at least one client host."; exit 1; }
+test -z "$BROKERS" && { echo "Must specify at least one broker host."; exit 1; }
+
+export TESTDIR # For perfdist
+CLIENTS=($CLIENTS) # Convert to array
+BROKERS=($BROKERS)
+trap "rm -f $FILES" EXIT
+
+dosamples() {
+ FILE=`mktemp`
+ FILES="$FILES $FILE"
+ TABS=`echo "$HEADING" | sed s'/[^ ]//g'`
+ {
+ echo "\"$*\"$TABS"
+ echo "$HEADING"
+ for (( i=0; i<$SAMPLES; ++i)) ; do echo "`$*`" ; done
+ echo
+ } | tee $FILE
+}
+
+HEADING="pub sub total Mb"
+dosamples $SCRIPTDIR/perfdist --size $SIZE --count $COUNT --nsubs $NSUBS --npubs $NPUBS -s -- ${CLIENTS[*]} --- ${BROKERS[*]}
+HEADING="pub"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/publish --routing-key perftest0 --size $SIZE --count $COUNT -s -b ${BROKERS[0]}
+HEADING="sub"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/consume --queue perftest0 -s --count $COUNT -b ${BROKERS[0]}
+HEADING="min max avg"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/echotest --count $ECHO -s -b ${BROKERS[0]}
+
+echo
+echo "Tab separated spreadsheet (also saved as benchmark.tab):"
+echo
+
+echo "benchmark -- ${CLIENTS[*]} --- ${BROKERS[*]} " | tee benchmark.tab
+paste $FILES | tee -a benchmark.tab
diff --git a/qpid/cpp/src/tests/brokermgmt.mk b/qpid/cpp/src/tests/brokermgmt.mk
new file mode 100644
index 0000000000..cf9a47200c
--- /dev/null
+++ b/qpid/cpp/src/tests/brokermgmt.mk
@@ -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.
+#
+
+# Build a unit test for the broker's internal management agent.
+
+BROKERMGMT_GEN_SRC= \
+ brokermgmt_gen/qmf/org/apache/qpid/broker/mgmt/test/Package.cpp \
+ brokermgmt_gen/qmf/org/apache/qpid/broker/mgmt/test/Package.h \
+ brokermgmt_gen/qmf/org/apache/qpid/broker/mgmt/test/TestObject.h \
+ brokermgmt_gen/qmf/org/apache/qpid/broker/mgmt/test/TestObject.cpp
+
+$(BROKERMGMT_GEN_SRC): brokermgmt_gen.timestamp
+
+if GENERATE
+BROKERMGMT_DEPS=../mgen.timestamp
+endif # GENERATE
+brokermgmt_gen.timestamp: BrokerMgmtAgent.xml ${BROKERMGMT_DEPS}
+ $(QMF_GEN) -b -o brokermgmt_gen/qmf $(srcdir)/BrokerMgmtAgent.xml
+ touch $@
+
+BrokerMgmtAgent.$(OBJEXT): $(BROKERMGMT_GEN_SRC)
+
+CLEANFILES+=$(BROKERMGMT_GEN_SRC) brokermgmt_gen.timestamp
+
+unit_test_SOURCES+=BrokerMgmtAgent.cpp ${BROKERMGMT_GEN_SRC}
+INCLUDES+= -Ibrokermgmt_gen
+
+EXTRA_DIST+=BrokerMgmtAgent.xml
diff --git a/qpid/cpp/src/tests/brokertest.py b/qpid/cpp/src/tests/brokertest.py
new file mode 100644
index 0000000000..a19dd305e5
--- /dev/null
+++ b/qpid/cpp/src/tests/brokertest.py
@@ -0,0 +1,671 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Support library for tests that start multiple brokers, e.g. cluster
+# or federation
+
+import os, signal, string, tempfile, subprocess, socket, threading, time, imp, re
+import qpid, traceback, signal
+from qpid import connection, messaging, util
+from qpid.compat import format_exc
+from qpid.harness import Skipped
+from unittest import TestCase
+from copy import copy
+from threading import Thread, Lock, Condition
+from logging import getLogger
+import qmf.console
+
+log = getLogger("qpid.brokertest")
+
+# Values for expected outcome of process at end of test
+EXPECT_EXIT_OK=1 # Expect to exit with 0 status before end of test.
+EXPECT_EXIT_FAIL=2 # Expect to exit with non-0 status before end of test.
+EXPECT_RUNNING=3 # Expect to still be running at end of test
+EXPECT_UNKNOWN=4 # No expectation, don't check exit status.
+
+def find_exe(program):
+ """Find an executable in the system PATH"""
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+ mydir, name = os.path.split(program)
+ if mydir:
+ if is_exe(program): return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file): return exe_file
+ return None
+
+def is_running(pid):
+ try:
+ os.kill(pid, 0)
+ return True
+ except:
+ return False
+
+class BadProcessStatus(Exception):
+ pass
+
+def error_line(filename, n=1):
+ """Get the last n line(s) of filename for error messages"""
+ result = []
+ try:
+ f = open(filename)
+ try:
+ for l in f:
+ if len(result) == n: result.pop(0)
+ result.append(" "+l)
+ finally:
+ f.close()
+ except: return ""
+ return ":\n" + "".join(result)
+
+def retry(function, timeout=10, delay=.01):
+ """Call function until it returns True or timeout expires.
+ Double the delay for each retry. Return True if function
+ returns true, False if timeout expires."""
+ deadline = time.time() + timeout
+ while not function():
+ remaining = deadline - time.time()
+ if remaining <= 0: return False
+ delay = min(delay, remaining)
+ time.sleep(delay)
+ delay *= 2
+ return True
+
+class AtomicCounter:
+ def __init__(self):
+ self.count = 0
+ self.lock = Lock()
+
+ def next(self):
+ self.lock.acquire();
+ ret = self.count
+ self.count += 1
+ self.lock.release();
+ return ret
+
+_popen_id = AtomicCounter() # Popen identifier for use in output file names.
+
+# Constants for file descriptor arguments to Popen
+FILE = "FILE" # Write to file named after process
+PIPE = subprocess.PIPE
+
+class Popen(subprocess.Popen):
+ """
+ Can set and verify expectation of process status at end of test.
+ Dumps command line, stdout, stderr to data dir for debugging.
+ """
+
+ def __init__(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
+ """Run cmd (should be a list of program and arguments)
+ expect - if set verify expectation at end of test.
+ stdout, stderr - can have the same values as for subprocess.Popen as well as
+ FILE (the default) which means write to a file named after the process.
+ stdin - like subprocess.Popen but defauts to PIPE
+ """
+ self._clean = False
+ self._clean_lock = Lock()
+ assert find_exe(cmd[0]), "executable not found: "+cmd[0]
+ if type(cmd) is type(""): cmd = [cmd] # Make it a list.
+ self.cmd = [ str(x) for x in cmd ]
+ self.expect = expect
+ self.id = _popen_id.next()
+ self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.id)
+ if stdout == FILE: stdout = open(self.outfile("out"), "w")
+ if stderr == FILE: stderr = open(self.outfile("err"), "w")
+ try:
+ subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None,
+ stdin=stdin, stdout=stdout, stderr=stderr,
+ close_fds=True)
+ except ValueError: # Windows can't do close_fds
+ subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None,
+ stdin=stdin, stdout=stdout, stderr=stderr)
+
+ f = open(self.outfile("cmd"), "w")
+ try: f.write("%s\n%d"%(self.cmd_str(), self.pid))
+ finally: f.close()
+ log.debug("Started process %s: %s" % (self.pname, " ".join(self.cmd)))
+
+ def __str__(self): return "Popen<%s>"%(self.pname)
+
+ def outfile(self, ext): return "%s.%s" % (self.pname, ext)
+
+ def unexpected(self,msg):
+ err = error_line(self.outfile("err")) or error_line(self.outfile("out"))
+ raise BadProcessStatus("%s %s%s" % (self.pname, msg, err))
+
+ def stop(self): # Clean up at end of test.
+ try:
+ if self.expect == EXPECT_UNKNOWN:
+ try: self.kill() # Just make sure its dead
+ except: pass
+ elif self.expect == EXPECT_RUNNING:
+ try: self.kill()
+ except: self.unexpected("expected running, exit code %d" % self.wait())
+ else:
+ retry(lambda: self.poll() is not None)
+ if self.returncode is None: # Still haven't stopped
+ self.kill()
+ self.unexpected("still running")
+ elif self.expect == EXPECT_EXIT_OK and self.returncode != 0:
+ self.unexpected("exit code %d" % self.returncode)
+ elif self.expect == EXPECT_EXIT_FAIL and self.returncode == 0:
+ self.unexpected("expected error")
+ finally:
+ self.wait() # Clean up the process.
+
+ def communicate(self, input=None):
+ ret = subprocess.Popen.communicate(self, input)
+ self.cleanup()
+ return ret
+
+ def is_running(self): return self.poll() is None
+
+ def assert_running(self):
+ if not self.is_running(): self.unexpected("Exit code %d" % self.returncode)
+
+ def wait(self):
+ ret = subprocess.Popen.wait(self)
+ self._cleanup()
+ return ret
+
+ def terminate(self):
+ try: subprocess.Popen.terminate(self)
+ except AttributeError: # No terminate method
+ try:
+ os.kill( self.pid , signal.SIGTERM)
+ except AttributeError: # no os.kill, using taskkill.. (Windows only)
+ os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
+ self._cleanup()
+
+ def kill(self):
+ try: subprocess.Popen.kill(self)
+ except AttributeError: # No terminate method
+ try:
+ os.kill( self.pid , signal.SIGKILL)
+ except AttributeError: # no os.kill, using taskkill.. (Windows only)
+ os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
+ self._cleanup()
+
+ def _cleanup(self):
+ """Clean up after a dead process"""
+ self._clean_lock.acquire()
+ if not self._clean:
+ self._clean = True
+ try: self.stdin.close()
+ except: pass
+ try: self.stdout.close()
+ except: pass
+ try: self.stderr.close()
+ except: pass
+ self._clean_lock.release()
+
+ def cmd_str(self): return " ".join([str(s) for s in self.cmd])
+
+def checkenv(name):
+ value = os.getenv(name)
+ if not value: raise Exception("Environment variable %s is not set" % name)
+ return value
+
+def find_in_file(str, filename):
+ if not os.path.exists(filename): return False
+ f = open(filename)
+ try: return str in f.read()
+ finally: f.close()
+
+class Broker(Popen):
+ "A broker process. Takes care of start, stop and logging."
+ _broker_count = 0
+
+ def __str__(self): return "Broker<%s %s>"%(self.name, self.pname)
+
+ def find_log(self):
+ self.log = "%s.log" % self.name
+ i = 1
+ while (os.path.exists(self.log)):
+ self.log = "%s-%d.log" % (self.name, i)
+ i += 1
+
+ def get_log(self):
+ return os.path.abspath(self.log)
+
+ def __init__(self, test, args=[], name=None, expect=EXPECT_RUNNING, port=0, log_level=None, wait=None):
+ """Start a broker daemon. name determines the data-dir and log
+ file names."""
+
+ self.test = test
+ self._port=port
+ if BrokerTest.store_lib:
+ args = args + ['--load-module', BrokerTest.store_lib]
+ if BrokerTest.sql_store_lib:
+ args = args + ['--load-module', BrokerTest.sql_store_lib]
+ args = args + ['--catalog', BrokerTest.sql_catalog]
+ if BrokerTest.sql_clfs_store_lib:
+ args = args + ['--load-module', BrokerTest.sql_clfs_store_lib]
+ args = args + ['--catalog', BrokerTest.sql_catalog]
+ cmd = [BrokerTest.qpidd_exec, "--port", port, "--no-module-dir"] + args
+ if not "--auth" in args: cmd.append("--auth=no")
+ if wait != None:
+ cmd += ["--wait", str(wait)]
+ if name: self.name = name
+ else:
+ self.name = "broker%d" % Broker._broker_count
+ Broker._broker_count += 1
+ self.find_log()
+ cmd += ["--log-to-file", self.log]
+ cmd += ["--log-to-stderr=no"]
+ if log_level != None:
+ cmd += ["--log-enable=%s" % log_level]
+ self.datadir = self.name
+ cmd += ["--data-dir", self.datadir]
+ Popen.__init__(self, cmd, expect, stdout=PIPE)
+ test.cleanup_stop(self)
+ self._host = "127.0.0.1"
+ log.debug("Started broker %s (%s, %s)" % (self.name, self.pname, self.log))
+ self._log_ready = False
+
+ def startQmf(self, handler=None):
+ self.qmf_session = qmf.console.Session(handler)
+ self.qmf_broker = self.qmf_session.addBroker("%s:%s" % (self.host(), self.port()))
+
+ def host(self): return self._host
+
+ def port(self):
+ # Read port from broker process stdout if not already read.
+ if (self._port == 0):
+ try: self._port = int(self.stdout.readline())
+ except ValueError:
+ raise Exception("Can't get port for broker %s (%s)%s" %
+ (self.name, self.pname, error_line(self.log,5)))
+ return self._port
+
+ def unexpected(self,msg):
+ raise BadProcessStatus("%s: %s (%s)" % (msg, self.name, self.pname))
+
+ def connect(self, **kwargs):
+ """New API connection to the broker."""
+ return messaging.Connection.establish(self.host_port(), **kwargs)
+
+ def connect_old(self):
+ """Old API connection to the broker."""
+ socket = qpid.util.connect(self.host(),self.port())
+ connection = qpid.connection.Connection (sock=socket)
+ connection.start()
+ return connection;
+
+ def declare_queue(self, queue):
+ c = self.connect_old()
+ s = c.session(str(qpid.datatypes.uuid4()))
+ s.queue_declare(queue=queue)
+ c.close()
+
+ def _prep_sender(self, queue, durable, xprops):
+ s = queue + "; {create:always, node:{durable:" + str(durable)
+ if xprops != None: s += ", x-declare:{" + xprops + "}"
+ return s + "}}"
+
+ def send_message(self, queue, message, durable=True, xprops=None, session=None):
+ if session == None:
+ s = self.connect().session()
+ else:
+ s = session
+ s.sender(self._prep_sender(queue, durable, xprops)).send(message)
+ if session == None:
+ s.connection.close()
+
+ def send_messages(self, queue, messages, durable=True, xprops=None, session=None):
+ if session == None:
+ s = self.connect().session()
+ else:
+ s = session
+ sender = s.sender(self._prep_sender(queue, durable, xprops))
+ for m in messages: sender.send(m)
+ if session == None:
+ s.connection.close()
+
+ def get_message(self, queue):
+ s = self.connect().session()
+ m = s.receiver(queue+"; {create:always}", capacity=1).fetch(timeout=1)
+ s.acknowledge()
+ s.connection.close()
+ return m
+
+ def get_messages(self, queue, n):
+ s = self.connect().session()
+ receiver = s.receiver(queue+"; {create:always}", capacity=n)
+ m = [receiver.fetch(timeout=1) for i in range(n)]
+ s.acknowledge()
+ s.connection.close()
+ return m
+
+ def host_port(self): return "%s:%s" % (self.host(), self.port())
+
+ def log_ready(self):
+ """Return true if the log file exists and contains a broker ready message"""
+ if not self._log_ready:
+ self._log_ready = find_in_file("notice Broker running", self.log)
+ return self._log_ready
+
+ def ready(self, **kwargs):
+ """Wait till broker is ready to serve clients"""
+ # First make sure the broker is listening by checking the log.
+ if not retry(self.log_ready, timeout=60):
+ raise Exception(
+ "Timed out waiting for broker %s%s"%(self.name, error_line(self.log,5)))
+ # Create a connection and a session. For a cluster broker this will
+ # return after cluster init has finished.
+ try:
+ c = self.connect(**kwargs)
+ try: c.session()
+ finally: c.close()
+ except Exception,e: raise RethrownException(
+ "Broker %s not responding: (%s)%s"%(self.name,e,error_line(self.log, 5)))
+
+ def store_state(self):
+ f = open(os.path.join(self.datadir, "cluster", "store.status"))
+ try: uuids = f.readlines()
+ finally: f.close()
+ null_uuid="00000000-0000-0000-0000-000000000000\n"
+ if len(uuids) < 2: return "unknown" # we looked while the file was being updated.
+ if uuids[0] == null_uuid: return "empty"
+ if uuids[1] == null_uuid: return "dirty"
+ return "clean"
+
+class Cluster:
+ """A cluster of brokers in a test."""
+
+ _cluster_count = 0
+
+ def __init__(self, test, count=0, args=[], expect=EXPECT_RUNNING, wait=True):
+ self.test = test
+ self._brokers=[]
+ self.name = "cluster%d" % Cluster._cluster_count
+ Cluster._cluster_count += 1
+ # Use unique cluster name
+ self.args = copy(args)
+ self.args += [ "--cluster-name", "%s-%s:%d" % (self.name, socket.gethostname(), os.getpid()) ]
+ self.args += [ "--log-enable=info+", "--log-enable=debug+:cluster"]
+ assert BrokerTest.cluster_lib, "Cannot locate cluster plug-in"
+ self.args += [ "--load-module", BrokerTest.cluster_lib ]
+ self.start_n(count, expect=expect, wait=wait)
+
+ def start(self, name=None, expect=EXPECT_RUNNING, wait=True, args=[], port=0):
+ """Add a broker to the cluster. Returns the index of the new broker."""
+ if not name: name="%s-%d" % (self.name, len(self._brokers))
+ self._brokers.append(self.test.broker(self.args+args, name, expect, wait, port=port))
+ return self._brokers[-1]
+
+ def start_n(self, count, expect=EXPECT_RUNNING, wait=True, args=[]):
+ for i in range(count): self.start(expect=expect, wait=wait, args=args)
+
+ # Behave like a list of brokers.
+ def __len__(self): return len(self._brokers)
+ def __getitem__(self,index): return self._brokers[index]
+ def __iter__(self): return self._brokers.__iter__()
+
+class BrokerTest(TestCase):
+ """
+ Tracks processes started by test and kills at end of test.
+ Provides a well-known working directory for each test.
+ """
+
+ # Environment settings.
+ qpidd_exec = os.path.abspath(checkenv("QPIDD_EXEC"))
+ cluster_lib = os.getenv("CLUSTER_LIB")
+ xml_lib = os.getenv("XML_LIB")
+ qpid_config_exec = os.getenv("QPID_CONFIG_EXEC")
+ qpid_route_exec = os.getenv("QPID_ROUTE_EXEC")
+ receiver_exec = os.getenv("RECEIVER_EXEC")
+ sender_exec = os.getenv("SENDER_EXEC")
+ sql_store_lib = os.getenv("STORE_SQL_LIB")
+ sql_clfs_store_lib = os.getenv("STORE_SQL_CLFS_LIB")
+ sql_catalog = os.getenv("STORE_CATALOG")
+ store_lib = os.getenv("STORE_LIB")
+ test_store_lib = os.getenv("TEST_STORE_LIB")
+ rootdir = os.getcwd()
+
+ def configure(self, config): self.config=config
+
+ def setUp(self):
+ outdir = self.config.defines.get("OUTDIR") or "brokertest.tmp"
+ self.dir = os.path.join(self.rootdir, outdir, self.id())
+ os.makedirs(self.dir)
+ os.chdir(self.dir)
+ self.stopem = [] # things to stop at end of test
+
+ def tearDown(self):
+ err = []
+ for p in self.stopem:
+ try: p.stop()
+ except Exception, e: err.append(str(e))
+ self.stopem = [] # reset in case more processes start
+ os.chdir(self.rootdir)
+ if err: raise Exception("Unexpected process status:\n "+"\n ".join(err))
+
+ def cleanup_stop(self, stopable):
+ """Call thing.stop at end of test"""
+ self.stopem.append(stopable)
+
+ def popen(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
+ """Start a process that will be killed at end of test, in the test dir."""
+ os.chdir(self.dir)
+ p = Popen(cmd, expect, stdin=stdin, stdout=stdout, stderr=stderr)
+ self.cleanup_stop(p)
+ return p
+
+ def broker(self, args=[], name=None, expect=EXPECT_RUNNING, wait=True, port=0, log_level=None):
+ """Create and return a broker ready for use"""
+ b = Broker(self, args=args, name=name, expect=expect, port=port, log_level=log_level)
+ if (wait):
+ try: b.ready()
+ except Exception, e:
+ raise RethrownException("Failed to start broker %s(%s): %s" % (b.name, b.log, e))
+ return b
+
+ def cluster(self, count=0, args=[], expect=EXPECT_RUNNING, wait=True):
+ """Create and return a cluster ready for use"""
+ cluster = Cluster(self, count, args, expect=expect, wait=wait)
+ return cluster
+
+ def browse(self, session, queue, timeout=0):
+ """Assert that the contents of messages on queue (as retrieved
+ using session and timeout) exactly match the strings in
+ expect_contents"""
+ r = session.receiver("%s;{mode:browse}"%(queue))
+ try:
+ contents = []
+ try:
+ while True: contents.append(r.fetch(timeout=timeout).content)
+ except messaging.Empty: pass
+ finally: pass #FIXME aconway 2011-04-14: r.close()
+ return contents
+
+ def assert_browse(self, session, queue, expect_contents, timeout=0):
+ """Assert that the contents of messages on queue (as retrieved
+ using session and timeout) exactly match the strings in
+ expect_contents"""
+ actual_contents = self.browse(session, queue, timeout)
+ self.assertEqual(expect_contents, actual_contents)
+
+def join(thread, timeout=10):
+ thread.join(timeout)
+ if thread.isAlive(): raise Exception("Timed out joining thread %s"%thread)
+
+class RethrownException(Exception):
+ """Captures the stack trace of the current exception to be thrown later"""
+ def __init__(self, msg=""):
+ Exception.__init__(self, msg+"\n"+format_exc())
+
+class StoppableThread(Thread):
+ """
+ Base class for threads that do something in a loop and periodically check
+ to see if they have been stopped.
+ """
+ def __init__(self):
+ self.stopped = False
+ self.error = None
+ Thread.__init__(self)
+
+ def stop(self):
+ self.stopped = True
+ join(self)
+ if self.error: raise self.error
+
+class NumberedSender(Thread):
+ """
+ Thread to run a sender client and send numbered messages until stopped.
+ """
+
+ def __init__(self, broker, max_depth=None, queue="test-queue"):
+ """
+ max_depth: enable flow control, ensure sent - received <= max_depth.
+ Requires self.notify_received(n) to be called each time messages are received.
+ """
+ Thread.__init__(self)
+ self.sender = broker.test.popen(
+ ["qpid-send",
+ "--broker", "localhost:%s"%broker.port(),
+ "--address", "%s;{create:always}"%queue,
+ "--failover-updates",
+ "--content-stdin"
+ ],
+ expect=EXPECT_RUNNING,
+ stdin=PIPE)
+ self.condition = Condition()
+ self.max = max_depth
+ self.received = 0
+ self.stopped = False
+ self.error = None
+
+ def write_message(self, n):
+ self.sender.stdin.write(str(n)+"\n")
+ self.sender.stdin.flush()
+
+ def run(self):
+ try:
+ self.sent = 0
+ while not self.stopped:
+ if self.max:
+ self.condition.acquire()
+ while not self.stopped and self.sent - self.received > self.max:
+ self.condition.wait()
+ self.condition.release()
+ self.write_message(self.sent)
+ self.sent += 1
+ except Exception: self.error = RethrownException(self.sender.pname)
+
+ def notify_received(self, count):
+ """Called by receiver to enable flow control. count = messages received so far."""
+ self.condition.acquire()
+ self.received = count
+ self.condition.notify()
+ self.condition.release()
+
+ def stop(self):
+ self.condition.acquire()
+ try:
+ self.stopped = True
+ self.condition.notify()
+ finally: self.condition.release()
+ join(self)
+ self.write_message(-1) # end-of-messages marker.
+ if self.error: raise self.error
+
+class NumberedReceiver(Thread):
+ """
+ Thread to run a receiver client and verify it receives
+ sequentially numbered messages.
+ """
+ def __init__(self, broker, sender = None, queue="test-queue"):
+ """
+ sender: enable flow control. Call sender.received(n) for each message received.
+ """
+ Thread.__init__(self)
+ self.test = broker.test
+ self.receiver = self.test.popen(
+ ["qpid-receive",
+ "--broker", "localhost:%s"%broker.port(),
+ "--address", "%s;{create:always}"%queue,
+ "--failover-updates",
+ "--forever"
+ ],
+ expect=EXPECT_RUNNING,
+ stdout=PIPE)
+ self.lock = Lock()
+ self.error = None
+ self.sender = sender
+
+ def read_message(self):
+ return int(self.receiver.stdout.readline())
+
+ def run(self):
+ try:
+ self.received = 0
+ m = self.read_message()
+ while m != -1:
+ assert(m <= self.received) # Check for missing messages
+ if (m == self.received): # Ignore duplicates
+ self.received += 1
+ if self.sender:
+ self.sender.notify_received(self.received)
+ m = self.read_message()
+ except Exception:
+ self.error = RethrownException(self.receiver.pname)
+
+ def stop(self):
+ """Returns when termination message is received"""
+ join(self)
+ if self.error: raise self.error
+
+class ErrorGenerator(StoppableThread):
+ """
+ Thread that continuously generates errors by trying to consume from
+ a non-existent queue. For cluster regression tests, error handling
+ caused issues in the past.
+ """
+
+ def __init__(self, broker):
+ StoppableThread.__init__(self)
+ self.broker=broker
+ broker.test.cleanup_stop(self)
+ self.start()
+
+ def run(self):
+ c = self.broker.connect_old()
+ try:
+ while not self.stopped:
+ try:
+ c.session(str(qpid.datatypes.uuid4())).message_subscribe(
+ queue="non-existent-queue")
+ assert(False)
+ except qpid.session.SessionException: pass
+ time.sleep(0.01)
+ except: pass # Normal if broker is killed.
+
+def import_script(path):
+ """
+ Import executable script at path as a module.
+ Requires some trickery as scripts are not in standard module format
+ """
+ f = open(path)
+ try:
+ name=os.path.split(path)[1].replace("-","_")
+ return imp.load_module(name, f, path, ("", "r", imp.PY_SOURCE))
+ finally: f.close()
diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py
new file mode 100755
index 0000000000..6c75927461
--- /dev/null
+++ b/qpid/cpp/src/tests/cli_tests.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import os
+import imp
+from qpid.testlib import TestBase010
+# from brokertest import import_script, checkenv
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+def import_script(path):
+ """
+ Import executable script at path as a module.
+ Requires some trickery as scripts are not in standard module format
+ """
+ f = open(path)
+ try:
+ name=os.path.split(path)[1].replace("-","_")
+ return imp.load_module(name, f, path, ("", "r", imp.PY_SOURCE))
+ finally: f.close()
+
+def checkenv(name):
+ value = os.getenv(name)
+ if not value: raise Exception("Environment variable %s is not set" % name)
+ return value
+
+class CliTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def cli_dir(self):
+ return self.defines["cli-dir"]
+
+ def makeQueue(self, qname, arguments, api=False):
+ if api:
+ ret = self.qpid_config_api(" add queue " + qname + " " + arguments)
+ else:
+ ret = os.system(self.qpid_config_command(" add queue " + qname + " " + arguments))
+
+ self.assertEqual(ret, 0)
+ queues = self.qmf.getObjects(_class="queue")
+ for queue in queues:
+ if queue.name == qname:
+ return queue
+ assert False
+
+ def test_queue_params(self):
+ self.startQmf()
+ queue1 = self.makeQueue("test_queue_params1", "--limit-policy none")
+ queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject")
+ queue3 = self.makeQueue("test_queue_params3", "--limit-policy flow-to-disk")
+ queue4 = self.makeQueue("test_queue_params4", "--limit-policy ring")
+ queue5 = self.makeQueue("test_queue_params5", "--limit-policy ring-strict")
+
+ LIMIT = "qpid.policy_type"
+ assert LIMIT not in queue1.arguments
+ self.assertEqual(queue2.arguments[LIMIT], "reject")
+ self.assertEqual(queue3.arguments[LIMIT], "flow_to_disk")
+ self.assertEqual(queue4.arguments[LIMIT], "ring")
+ self.assertEqual(queue5.arguments[LIMIT], "ring_strict")
+
+ queue6 = self.makeQueue("test_queue_params6", "--order fifo")
+ queue7 = self.makeQueue("test_queue_params7", "--order lvq")
+ queue8 = self.makeQueue("test_queue_params8", "--order lvq-no-browse")
+
+ LVQ = "qpid.last_value_queue"
+ LVQNB = "qpid.last_value_queue_no_browse"
+
+ assert LVQ not in queue6.arguments
+ assert LVQ in queue7.arguments
+ assert LVQ not in queue8.arguments
+
+ assert LVQNB not in queue6.arguments
+ assert LVQNB not in queue7.arguments
+ assert LVQNB in queue8.arguments
+
+
+ def test_queue_params_api(self):
+ self.startQmf()
+ queue1 = self.makeQueue("test_queue_params1", "--limit-policy none", True)
+ queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject", True)
+ queue3 = self.makeQueue("test_queue_params3", "--limit-policy flow-to-disk", True)
+ queue4 = self.makeQueue("test_queue_params4", "--limit-policy ring", True)
+ queue5 = self.makeQueue("test_queue_params5", "--limit-policy ring-strict", True)
+
+ LIMIT = "qpid.policy_type"
+ assert LIMIT not in queue1.arguments
+ self.assertEqual(queue2.arguments[LIMIT], "reject")
+ self.assertEqual(queue3.arguments[LIMIT], "flow_to_disk")
+ self.assertEqual(queue4.arguments[LIMIT], "ring")
+ self.assertEqual(queue5.arguments[LIMIT], "ring_strict")
+
+ queue6 = self.makeQueue("test_queue_params6", "--order fifo", True)
+ queue7 = self.makeQueue("test_queue_params7", "--order lvq", True)
+ queue8 = self.makeQueue("test_queue_params8", "--order lvq-no-browse", True)
+
+ LVQ = "qpid.last_value_queue"
+ LVQNB = "qpid.last_value_queue_no_browse"
+
+ assert LVQ not in queue6.arguments
+ assert LVQ in queue7.arguments
+ assert LVQ not in queue8.arguments
+
+ assert LVQNB not in queue6.arguments
+ assert LVQNB not in queue7.arguments
+ assert LVQNB in queue8.arguments
+
+
+ def test_qpid_config(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+
+ ret = os.system(self.qpid_config_command(" add queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_api(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config_api"
+
+ ret = self.qpid_config_api(" add queue " + qname)
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = self.qpid_config_api(" del queue " + qname)
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+
+ def test_qpid_config_sasl_plain_expect_succeed(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config_sasl_plain_expect_succeed"
+ cmd = " --sasl-mechanism PLAIN -a guest/guest@localhost:"+str(self.broker.port) + " add queue " + qname
+ ret = self.qpid_config_api(cmd)
+ self.assertEqual(ret, 0)
+
+ def test_qpid_config_sasl_plain_expect_fail(self):
+ """Fails because no user name and password is supplied"""
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config_sasl_plain_expect_succeed"
+ cmd = " --sasl-mechanism PLAIN -a localhost:"+str(self.broker.port) + " add queue " + qname
+ ret = self.qpid_config_api(cmd)
+ assert ret != 0
+
+ # helpers for some of the test methods
+ def helper_find_exchange(self, xchgname, typ, expected=True):
+ xchgs = self.qmf.getObjects(_class = "exchange")
+ found = False
+ for xchg in xchgs:
+ if xchg.name == xchgname:
+ if typ:
+ self.assertEqual(xchg.type, typ)
+ found = True
+ self.assertEqual(found, expected)
+
+ def helper_create_exchange(self, xchgname, typ="direct", opts=""):
+ foo = self.qpid_config_command(opts + " add exchange " + typ + " " + xchgname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_exchange(xchgname, typ, True)
+
+ def helper_destroy_exchange(self, xchgname):
+ foo = self.qpid_config_command(" del exchange " + xchgname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_exchange(xchgname, False, expected=False)
+
+ def helper_find_queue(self, qname, expected=True):
+ queues = self.qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, expected)
+
+ def helper_create_queue(self, qname):
+ foo = self.qpid_config_command(" add queue " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_queue(qname, True)
+
+ def helper_destroy_queue(self, qname):
+ foo = self.qpid_config_command(" del queue " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_queue(qname, False)
+
+
+ # test the bind-queue-to-header-exchange functionality
+ def test_qpid_config_headers(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+ xchgname = "test_xchg"
+
+ # first create a header xchg
+ self.helper_create_exchange(xchgname, typ="headers")
+
+ # create the queue
+ self.helper_create_queue(qname)
+
+ # now bind the queue to the xchg
+ foo = self.qpid_config_command(" bind " + xchgname + " " + qname +
+ " key all foo=bar baz=quux")
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+
+ # he likes it, mikey. Ok, now tear it all down. first the binding
+ ret = os.system(self.qpid_config_command(" unbind " + xchgname + " " + qname +
+ " key"))
+ self.assertEqual(ret, 0)
+
+ # then the queue
+ self.helper_destroy_queue(qname)
+
+ # then the exchange
+ self.helper_destroy_exchange(xchgname)
+
+
+ def test_qpid_config_xml(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+ xchgname = "test_xchg"
+
+ # first create a header xchg
+ self.helper_create_exchange(xchgname, typ="xml")
+
+ # create the queue
+ self.helper_create_queue(qname)
+
+ # now bind the queue to the xchg
+ foo = self.qpid_config_command("-f test.xquery bind " + xchgname + " " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+
+ # he likes it, mikey. Ok, now tear it all down. first the binding
+ ret = os.system(self.qpid_config_command(" unbind " + xchgname + " " + qname +
+ " key"))
+ self.assertEqual(ret, 0)
+
+ # then the queue
+ self.helper_destroy_queue(qname)
+
+ # then the exchange
+ self.helper_destroy_exchange(xchgname)
+
+ def test_qpid_config_durable(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+
+ ret = os.system(self.qpid_config_command(" add queue --durable " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, True)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_altex(self):
+ self.startQmf();
+ qmf = self.qmf
+ exName = "testalt"
+ qName = "testqalt"
+ altName = "amq.direct"
+
+ ret = os.system(self.qpid_config_command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName)))
+ self.assertEqual(ret, 0)
+
+ exchanges = qmf.getObjects(_class="exchange")
+ found = False
+ for exchange in exchanges:
+ if exchange.name == altName:
+ self.assertEqual(exchange.altExchange, None)
+
+ if exchange.name == exName:
+ found = True
+ if not exchange.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(exchange._altExchange_.name, altName)
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" add queue %s --alternate-exchange=%s" % (qName, altName)))
+ self.assertEqual(ret, 0)
+
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qName:
+ found = True
+ if not queue.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(queue._altExchange_.name, altName)
+ self.assertEqual(found, True)
+
+ def test_qpid_config_list_queues_arguments(self):
+ """
+ Test to verify that when the type of a policy limit is
+ actually a string (though still a valid value), it does not
+ upset qpid-config
+ """
+ self.startQmf();
+ qmf = self.qmf
+
+ names = ["queue_capacity%s" % (i) for i in range(1, 6)]
+ for name in names:
+ self.session.queue_declare(queue=name, exclusive=True,
+ arguments={'qpid.max_count' : str(i), 'qpid.max_size': '100'})
+
+ output = os.popen(self.qpid_config_command(" queues")).readlines()
+ queues = [line.split()[0] for line in output[2:len(output)]] #ignore first two lines (header)
+
+ for name in names:
+ assert name in queues, "%s not in %s" % (name, queues)
+
+ def test_qpid_route(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ command = self.cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\
+ (self.broker.port, self.remote_host(), self.remote_port())
+ ret = os.system(command)
+ self.assertEqual(ret, 0)
+
+ links = qmf.getObjects(_class="link")
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+ def test_qpid_route_api(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ ret = self.qpid_route_api("dynamic add "
+ + "guest/guest@localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+
+ self.assertEqual(ret, 0)
+
+ links = qmf.getObjects(_class="link")
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+
+ def test_qpid_route_api(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ ret = self.qpid_route_api("dynamic add "
+ + " --client-sasl-mechanism PLAIN "
+ + "guest/guest@localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+
+ self.assertEqual(ret, 0)
+
+ links = qmf.getObjects(_class="link")
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+ def test_qpid_route_api_expect_fail(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ ret = self.qpid_route_api("dynamic add "
+ + " --client-sasl-mechanism PLAIN "
+ + "localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+ assert ret != 0
+
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
+
+ def qpid_config_command(self, arg = ""):
+ return self.cli_dir() + "/qpid-config -a localhost:%d" % self.broker.port + " " + arg
+
+ def qpid_config_api(self, arg = ""):
+ script = import_script(checkenv("QPID_CONFIG_EXEC"))
+ broker = ["-a", "localhost:"+str(self.broker.port)]
+ return script.main(broker + arg.split())
+
+ def qpid_route_api(self, arg = ""):
+ script = import_script(checkenv("QPID_ROUTE_EXEC"))
+ return script.main(arg.split())
diff --git a/qpid/cpp/src/tests/cluster.cmake b/qpid/cpp/src/tests/cluster.cmake
new file mode 100644
index 0000000000..3471173e97
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster.cmake
@@ -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.
+#
+
+#
+# Cluster tests cmake fragment, to be included in CMakeLists.txt
+#
+
+add_executable (failover_soak failover_soak.cpp ForkedBroker.cpp ${platform_test_additions})
+target_link_libraries (failover_soak qpidclient)
+remember_location(failover_soak)
+
+add_executable (cluster_authentication_soak cluster_authentication_soak.cpp ForkedBroker.cpp ${platform_test_additions})
+target_link_libraries (cluster_authentication_soak qpidclient)
+remember_location(cluster_authentication_soak)
+
+set (cluster_test_SOURCES
+ cluster_test
+ unit_test
+ ClusterFixture
+ ForkedBroker
+ PartialFailure
+ ClusterFailover
+ InitialStatusMap
+ StoreStatus
+ )
+add_executable (cluster_test ${cluster_test_SOURCES} ${platform_test_additions})
+target_link_libraries (cluster_test ${qpid_test_boost_libs} qpidclient qpidbroker cluster_shared)
+remember_location(cluster_test)
+
+add_test (cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_test${test_script_suffix})
+add_test (cluster_tests ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_tests${test_script_suffix})
+add_test (cluster_read_credit ${CMAKE_CURRENT_SOURCE_DIR}/cluster_read_credit${test_script_suffix})
+add_test (cluster_test_watchdog ${CMAKE_CURRENT_SOURCE_DIR}/test_watchdog${test_script_suffix})
+add_test (federated_cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/federated_cluster_test${test_script_suffix})
+add_test (clustered_replication_test ${CMAKE_CURRENT_SOURCE_DIR}/clustered_replication_test${test_script_suffix})
+
+# FIXME aconway 2009-12-01: translate to cmake
+# # Clean up after cluster_test and start_cluster
+# CLEANFILES += cluster_test.acl cluster.ports
+
+# EXTRA_DIST += \
+# ais_check \
+# run_cluster_test \
+# cluster_read_credit \
+# test_watchdog \
+# start_cluster \
+# stop_cluster \
+# restart_cluster \
+# cluster_python_tests \
+# cluster_python_tests_failing.txt \
+# federated_cluster_test \
+# clustered_replication_test \
+# run_cluster_tests \
+# run_long_cluster_tests \
+# testlib.py \
+# cluster_tests.py \
+# long_cluster_tests.py \
+# cluster_tests.fail
+
+# LONG_TESTS += \
+# run_long_cluster_tests \
+# start_cluster \
+# cluster_python_tests \
+# stop_cluster
+
+# qpidtest_PROGRAMS += cluster_test
+
+# cluster_test_SOURCES = \
+
+# cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework
+
+# qpidtest_SCRIPTS += run_cluster_tests cluster_tests.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail
+
+# endif
diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk
new file mode 100644
index 0000000000..7d17dd7bde
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster.mk
@@ -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.
+#
+
+
+# Include cluster scripts and extra files in distribution even if
+# we're not configured for cluster.
+
+# Useful scripts for doing cluster testing.
+CLUSTER_TEST_SCRIPTS_LIST= \
+ allhosts rsynchosts \
+ qpid-build-rinstall qpid-src-rinstall \
+ qpid-test-cluster \
+ qpid-cluster-benchmark
+
+EXTRA_DIST += \
+ $(CLUSTER_TEST_SCRIPTS_LIST) \
+ ais_check \
+ run_cluster_test \
+ cluster_read_credit \
+ test_watchdog \
+ start_cluster \
+ stop_cluster \
+ restart_cluster \
+ cluster_python_tests \
+ cluster_python_tests_failing.txt \
+ federated_cluster_test \
+ clustered_replication_test \
+ run_cluster_tests \
+ run_long_cluster_tests \
+ testlib.py \
+ brokertest.py \
+ cluster_tests.py \
+ cluster_test_logs.py \
+ long_cluster_tests.py \
+ cluster_tests.fail
+
+
+if HAVE_LIBCPG
+
+#
+# Cluster tests makefile fragment, to be included in Makefile.am
+#
+
+# NOTE: Programs using the openais library must be run with gid=ais
+# You should do "newgrp ais" before running the tests to run these.
+#
+
+
+# ais_check checks pre-requisites for cluster tests and runs them if ok.
+TESTS += \
+ run_cluster_test \
+ cluster_read_credit \
+ test_watchdog \
+ run_cluster_tests \
+ federated_cluster_test \
+ clustered_replication_test
+
+# Clean up after cluster_test and start_cluster
+CLEANFILES += cluster_test.acl cluster.ports
+
+LONG_TESTS += \
+ run_long_cluster_tests \
+ start_cluster \
+ cluster_python_tests \
+ stop_cluster
+
+qpidtest_PROGRAMS += cluster_test
+
+cluster_test_SOURCES = \
+ cluster_test.cpp \
+ unit_test.cpp \
+ ClusterFixture.cpp \
+ ClusterFixture.h \
+ ForkedBroker.h \
+ ForkedBroker.cpp \
+ PartialFailure.cpp \
+ ClusterFailover.cpp
+
+cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework
+
+qpidtest_SCRIPTS += run_cluster_tests brokertest.py cluster_tests.py cluster_test_logs.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail
+qpidtest_SCRIPTS += $(CLUSTER_TEST_SCRIPTS_LIST)
+
+endif
diff --git a/qpid/cpp/src/tests/cluster_authentication_soak.cpp b/qpid/cpp/src/tests/cluster_authentication_soak.cpp
new file mode 100644
index 0000000000..b8e8a22693
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_authentication_soak.cpp
@@ -0,0 +1,310 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include <boost/assign.hpp>
+
+#include "qpid/framing/Uuid.h"
+
+#include <ForkedBroker.h>
+#include <qpid/client/Connection.h>
+
+#include <sasl/sasl.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+
+
+using namespace std;
+using boost::assign::list_of;
+using namespace qpid::framing;
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+vector<pid_t> brokerPids;
+
+typedef vector<ForkedBroker *> brokerVector;
+
+
+
+
+
+int runSilent = 1;
+int newbiePort = 0;
+
+
+void
+makeClusterName ( string & s ) {
+ stringstream ss;
+ ss << "authenticationSoakCluster_" << Uuid(true).str();
+ s = ss.str();
+}
+
+
+
+void
+startBroker ( brokerVector & brokers , int brokerNumber, string const & clusterName ) {
+ stringstream prefix, clusterArg;
+ prefix << "soak-" << brokerNumber;
+ clusterArg << "--cluster-name=" << clusterName;
+
+ std::vector<std::string> argv;
+
+ argv.push_back ("../qpidd");
+ argv.push_back ("--no-module-dir");
+ argv.push_back ("--load-module=../.libs/cluster.so");
+ argv.push_back (clusterArg.str());
+ argv.push_back ("--cluster-username=zig");
+ argv.push_back ("--cluster-password=zig");
+ argv.push_back ("--cluster-mechanism=ANONYMOUS");
+ argv.push_back ("--sasl-config=./sasl_config");
+ argv.push_back ("--auth=yes");
+ argv.push_back ("--mgmt-enable=yes");
+ argv.push_back ("--log-prefix");
+ argv.push_back (prefix.str());
+ argv.push_back ("--log-to-file");
+ argv.push_back (prefix.str()+".log");
+ argv.push_back ("TMP_DATA_DIR");
+
+ ForkedBroker * newbie = new ForkedBroker (argv);
+ newbiePort = newbie->getPort();
+ brokers.push_back ( newbie );
+}
+
+
+
+
+bool
+runPerftest ( bool hangTest ) {
+ stringstream portSs;
+ portSs << newbiePort;
+ string portStr = portSs.str();
+ char const * path = "./qpid-perftest";
+
+ vector<char const *> argv;
+ argv.push_back ( "./qpid-perftest" );
+ argv.push_back ( "-p" );
+ argv.push_back ( portStr.c_str() );
+ argv.push_back ( "--username" );
+ argv.push_back ( "zig" );
+ argv.push_back ( "--password" );
+ argv.push_back ( "zig" );
+ argv.push_back ( "--mechanism" );
+ argv.push_back ( "DIGEST-MD5" );
+ argv.push_back ( "--count" );
+ argv.push_back ( "20000" );
+ argv.push_back ( 0 );
+
+ pid_t pid = fork();
+
+ if ( ! pid ) {
+ int i=open("/dev/null",O_RDWR);
+ dup2 ( i, fileno(stdout) );
+ dup2 ( i, fileno(stderr) );
+
+ execv ( path, const_cast<char * const *>(&argv[0]) );
+ // The exec failed: we are still in parent process.
+ perror ( "error running qpid-perftest: " );
+ return false;
+ }
+ else {
+ if ( hangTest ) {
+ if ( ! runSilent )
+ cerr << "Pausing perftest " << pid << endl;
+ kill ( pid, 19 );
+ }
+
+ struct timeval startTime,
+ currentTime,
+ duration;
+
+ gettimeofday ( & startTime, 0 );
+
+ while ( 1 ) {
+ sleep ( 2 );
+ int status;
+ int returned_pid = waitpid ( pid, &status, WNOHANG );
+ if ( returned_pid == pid ) {
+ int exit_status = WEXITSTATUS(status);
+ if ( exit_status ) {
+ cerr << "qpid-perftest failed. exit_status was: " << exit_status << endl;
+ return false;
+ }
+ else {
+ return true; // qpid-perftest succeeded.
+ }
+ }
+ else { // qpid-perftest has not yet completed.
+ gettimeofday ( & currentTime, 0 );
+ timersub ( & currentTime, & startTime, & duration );
+ if ( duration.tv_sec > 60 ) {
+ kill ( pid, 9 );
+ cerr << "qpid-perftest pid " << pid << " hanging: killed.\n";
+ return false;
+ }
+ }
+ }
+
+ }
+}
+
+
+
+bool
+allBrokersAreAlive ( brokerVector & brokers ) {
+ for ( unsigned int i = 0; i < brokers.size(); ++ i )
+ if ( ! brokers[i]->isRunning() )
+ return false;
+
+ return true;
+}
+
+
+
+
+
+void
+killAllBrokers ( brokerVector & brokers ) {
+ for ( unsigned int i = 0; i < brokers.size(); ++ i ) {
+ brokers[i]->kill ( 9 );
+ }
+}
+
+
+
+
+void
+killOneBroker ( brokerVector & brokers ) {
+ int doomedBroker = getpid() % brokers.size();
+ cout << "Killing broker " << brokers[doomedBroker]->getPID() << endl;
+ brokers[doomedBroker]->kill ( 9 );
+ sleep ( 2 );
+}
+
+
+
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+
+
+/*
+ * Please note that this test has self-test capability.
+ * It is intended to detect
+ * 1. perftest hangs.
+ * 2. broker deaths
+ * Both of these condtions can be forced when running manually
+ * to ensure that the test really does detect them.
+ * See command-line arguments 3 and 4.
+ */
+int
+main ( int argc, char ** argv )
+{
+ // I need the SASL_PATH_TYPE_CONFIG feature, which did not appear until SASL 2.1.22
+#if (SASL_VERSION_FULL < ((2<<16)|(1<<8)|22))
+ cout << "Skipping SASL test, SASL version too low." << endl;
+ return 0;
+#endif
+
+ int n_iterations = argc > 1 ? atoi(argv[1]) : 1;
+ runSilent = argc > 2 ? atoi(argv[2]) : 1; // default to silent
+ int killBroker = argc > 3 ? atoi(argv[3]) : 0; // Force the kill of one broker.
+ int hangTest = argc > 4 ? atoi(argv[4]) : 0; // Force the first perftest to hang.
+ int n_brokers = 3;
+ brokerVector brokers;
+
+ srand ( getpid() );
+ string clusterName;
+ makeClusterName ( clusterName );
+ for ( int i = 0; i < n_brokers; ++ i ) {
+ startBroker ( brokers, i, clusterName );
+ }
+
+ sleep ( 3 );
+
+ /* Run all qpid-perftest iterations, and only then check for brokers
+ * still being up. If you just want a quick check for the failure
+ * mode in which a single iteration would kill all brokers except
+ * the client-connected one, just run it with the iterations arg
+ * set to 1.
+ */
+ for ( int iteration = 0; iteration < n_iterations; ++ iteration ) {
+ if ( ! runPerftest ( hangTest ) ) {
+ if ( ! runSilent )
+ cerr << "qpid-perftest " << iteration << " failed.\n";
+ return 1;
+ }
+ if ( ! ( iteration % 10 ) ) {
+ if ( ! runSilent )
+ cerr << "qpid-perftest " << iteration << " complete. -------------- \n";
+ }
+ }
+ if ( ! runSilent )
+ cerr << "\nqpid-perftest " << n_iterations << " iterations complete. -------------- \n\n";
+
+ /* If the command-line tells us to kill a broker, do
+ * it now. Use this option to prove that this test
+ * really can detect broker-deaths.
+ */
+ if ( killBroker ) {
+ killOneBroker ( brokers );
+ }
+
+ if ( ! allBrokersAreAlive ( brokers ) ) {
+ if ( ! runSilent )
+ cerr << "not all brokers are alive.\n";
+ killAllBrokers ( brokers );
+ return 2;
+ }
+
+ killAllBrokers ( brokers );
+ if ( ! runSilent )
+ cout << "success.\n";
+
+ return 0;
+}
+
+
+
diff --git a/qpid/cpp/src/tests/cluster_python_tests b/qpid/cpp/src/tests/cluster_python_tests
new file mode 100755
index 0000000000..9d9137ed57
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_python_tests
@@ -0,0 +1,27 @@
+#!/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.
+#
+
+# Skip if cluster services not running.
+. `dirname $0`/ais_check
+
+FAILING=`dirname $0`/cluster_python_tests_failing.txt
+source `dirname $0`/python_tests
+
diff --git a/qpid/cpp/src/tests/cluster_python_tests_failing.txt b/qpid/cpp/src/tests/cluster_python_tests_failing.txt
new file mode 100644
index 0000000000..7ba8089946
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_python_tests_failing.txt
@@ -0,0 +1,32 @@
+qpid_tests.broker_0_10.management.ManagementTest.test_purge_queue
+qpid_tests.broker_0_10.management.ManagementTest.test_connection_close
+qpid_tests.broker_0_10.dtx.DtxTests.test_bad_resume
+qpid_tests.broker_0_10.dtx.DtxTests.test_commit_unknown
+qpid_tests.broker_0_10.dtx.DtxTests.test_end
+qpid_tests.broker_0_10.dtx.DtxTests.test_end_suspend_and_fail
+qpid_tests.broker_0_10.dtx.DtxTests.test_end_unknown_xid
+qpid_tests.broker_0_10.dtx.DtxTests.test_forget_xid_on_completion
+qpid_tests.broker_0_10.dtx.DtxTests.test_get_timeout
+qpid_tests.broker_0_10.dtx.DtxTests.test_get_timeout_unknown
+qpid_tests.broker_0_10.dtx.DtxTests.test_implicit_end
+qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_not_ended
+qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_one_phase_false
+qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_one_phase_true
+qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_prepare_not_ended
+qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_rollback_not_ended
+qpid_tests.broker_0_10.dtx.DtxTests.test_prepare_unknown
+qpid_tests.broker_0_10.dtx.DtxTests.test_recover
+qpid_tests.broker_0_10.dtx.DtxTests.test_rollback_unknown
+qpid_tests.broker_0_10.dtx.DtxTests.test_select_required
+qpid_tests.broker_0_10.dtx.DtxTests.test_set_timeout
+qpid_tests.broker_0_10.dtx.DtxTests.test_simple_commit
+qpid_tests.broker_0_10.dtx.DtxTests.test_simple_prepare_commit
+qpid_tests.broker_0_10.dtx.DtxTests.test_simple_prepare_rollback
+qpid_tests.broker_0_10.dtx.DtxTests.test_simple_rollback
+qpid_tests.broker_0_10.dtx.DtxTests.test_start_already_known
+qpid_tests.broker_0_10.dtx.DtxTests.test_start_join
+qpid_tests.broker_0_10.dtx.DtxTests.test_start_join_and_resume
+qpid_tests.broker_0_10.dtx.DtxTests.test_suspend_resume
+qpid_tests.broker_0_10.dtx.DtxTests.test_suspend_start_end_resume
+qpid_tests.broker_0_10.message.MessageTests.test_ttl
+qpid_tests.broker_0_10.management.ManagementTest.test_broker_connectivity_oldAPI
diff --git a/qpid/cpp/src/tests/cluster_read_credit b/qpid/cpp/src/tests/cluster_read_credit
new file mode 100755
index 0000000000..370d4098c5
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_read_credit
@@ -0,0 +1,27 @@
+#!/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.
+#
+
+# Regression test for http://issues.apache.org/jira/browse/QPID-2086
+
+srcdir=`dirname $0`
+. $srcdir/ais_check
+$srcdir/start_cluster 1 --cluster-read-max=2 || exit 1
+trap $srcdir/stop_cluster EXIT
+seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue
diff --git a/qpid/cpp/src/tests/cluster_test.cpp b/qpid/cpp/src/tests/cluster_test.cpp
new file mode 100644
index 0000000000..f2ccd0ba84
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test.cpp
@@ -0,0 +1,1231 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ForkedBroker.h"
+#include "BrokerFixture.h"
+#include "ClusterFixture.h"
+
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/assign.hpp>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <iterator>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(cluster_test)
+
+bool durableFlag = std::getenv("STORE_LIB") != 0;
+
+void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) {
+ ostringstream clusterLib;
+ clusterLib << getLibPath("CLUSTER_LIB");
+ args += "--auth", "no", "--no-module-dir", "--load-module", clusterLib.str();
+ if (durableFlag)
+ args += "--load-module", getLibPath("STORE_LIB"), "TMP_DATA_DIR";
+ else
+ args += "--no-data-dir";
+}
+
+ClusterFixture::Args prepareArgs(const bool durableFlag = false) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ return args;
+}
+
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=2*sys::TIME_SEC;
+
+
+ostream& operator<<(ostream& o, const cpg_name* n) {
+ return o << Cpg::str(*n);
+}
+
+ostream& operator<<(ostream& o, const cpg_address& a) {
+ return o << "(" << a.nodeid <<","<<a.pid<<","<<a.reason<<")";
+}
+
+template <class T>
+ostream& operator<<(ostream& o, const pair<T*, int>& array) {
+ o << "{ ";
+ ostream_iterator<cpg_address> i(o, " ");
+ copy(array.first, array.first+array.second, i);
+ o << "}";
+ return o;
+}
+
+template <class C> set<int> makeSet(const C& c) {
+ set<int> s;
+ copy(c.begin(), c.end(), inserter(s, s.begin()));
+ return s;
+}
+
+class Sender {
+ public:
+ Sender(boost::shared_ptr<ConnectionImpl> ci, uint16_t ch) : connection(ci), channel(ch) {}
+ void send(const AMQBody& body, bool firstSeg, bool lastSeg, bool firstFrame, bool lastFrame) {
+ AMQFrame f(body);
+ f.setChannel(channel);
+ f.setFirstSegment(firstSeg);
+ f.setLastSegment(lastSeg);
+ f.setFirstFrame(firstFrame);
+ f.setLastFrame(lastFrame);
+ connection->expand(f.encodedSize(), false);
+ connection->handle(f);
+ }
+
+ private:
+ boost::shared_ptr<ConnectionImpl> connection;
+ uint16_t channel;
+};
+
+int64_t getMsgSequence(const Message& m) {
+ return m.getMessageProperties().getApplicationHeaders().getAsInt64("qpid.msg_sequence");
+}
+
+Message ttlMessage(const string& data, const string& key, uint64_t ttl, bool durable = false) {
+ Message m(data, key);
+ m.getDeliveryProperties().setTtl(ttl);
+ if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ return m;
+}
+
+Message makeMessage(const string& data, const string& key, bool durable = false) {
+ Message m(data, key);
+ if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ return m;
+}
+
+vector<string> browse(Client& c, const string& q, int n) {
+ SubscriptionSettings browseSettings(
+ FlowControl::messageCredit(n),
+ ACCEPT_MODE_NONE,
+ ACQUIRE_MODE_NOT_ACQUIRED,
+ 0 // No auto-ack.
+ );
+ LocalQueue lq;
+ c.subs.subscribe(lq, q, browseSettings);
+ c.session.messageFlush(q);
+ vector<string> result;
+ for (int i = 0; i < n; ++i) {
+ Message m;
+ if (!lq.get(m, TIMEOUT))
+ break;
+ result.push_back(m.getData());
+ }
+ c.subs.getSubscription(q).cancel();
+ return result;
+}
+
+ConnectionSettings aclSettings(int port, const std::string& id) {
+ ConnectionSettings settings;
+ settings.port = port;
+ settings.mechanism = "PLAIN";
+ settings.username = id;
+ settings.password = id;
+ return settings;
+}
+
+// An illegal frame body
+struct PoisonPill : public AMQBody {
+ virtual uint8_t type() const { return 0xFF; }
+ virtual void encode(Buffer& ) const {}
+ virtual void decode(Buffer& , uint32_t=0) {}
+ virtual uint32_t encodedSize() const { return 0; }
+
+ virtual void print(std::ostream&) const {};
+ virtual void accept(AMQBodyConstVisitor&) const {};
+
+ virtual AMQMethodBody* getMethod() { return 0; }
+ virtual const AMQMethodBody* getMethod() const { return 0; }
+
+ /** Match if same type and same class/method ID for methods */
+ static bool match(const AMQBody& , const AMQBody& ) { return false; }
+ virtual boost::intrusive_ptr<AMQBody> clone() const { return new PoisonPill; }
+};
+
+QPID_AUTO_TEST_CASE(testBadClientData) {
+ // Ensure that bad data on a client connection closes the
+ // connection but does not stop the broker.
+ ClusterFixture::Args args;
+ prepareArgs(args, false);
+ args += "--log-enable=critical"; // Supress expected errors
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0]);
+ Client c1(cluster[1]);
+ boost::shared_ptr<client::ConnectionImpl> ci =
+ client::ConnectionAccess::getImpl(c0.connection);
+ AMQFrame poison(boost::intrusive_ptr<AMQBody>(new PoisonPill));
+ ci->expand(poison.encodedSize(), false);
+ ci->handle(poison);
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(c0.session.queueQuery("q0"), Exception);
+ }
+ Client c00(cluster[0]);
+ BOOST_CHECK_EQUAL(c00.session.queueQuery("q00").getQueue(), "");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getQueue(), "");
+}
+
+QPID_AUTO_TEST_CASE(testAcl) {
+ ofstream policyFile("cluster_test.acl");
+ policyFile << "acl allow foo@QPID create queue name=foo" << endl
+ << "acl allow foo@QPID create queue name=foo2" << endl
+ << "acl deny foo@QPID create queue name=bar" << endl
+ << "acl allow all all" << endl;
+ policyFile.close();
+ char cwd[1024];
+ BOOST_CHECK(::getcwd(cwd, sizeof(cwd)));
+ ostringstream aclLib;
+ aclLib << getLibPath("ACL_LIB");
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ args += "--log-enable=critical"; // Supress expected errors
+ args += "--acl-file", string(cwd) + "/cluster_test.acl",
+ "--cluster-mechanism", "PLAIN",
+ "--cluster-username", "cluster",
+ "--cluster-password", "cluster",
+ "--load-module", aclLib.str();
+ ClusterFixture cluster(2, args, -1);
+
+ Client c0(aclSettings(cluster[0], "c0"), "c0");
+ Client c1(aclSettings(cluster[1], "c1"), "c1");
+ Client foo(aclSettings(cluster[1], "foo"), "foo");
+
+ foo.session.queueDeclare("foo", arg::durable=durableFlag);
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("foo").getQueue(), "foo");
+
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::UnauthorizedAccessException);
+ }
+ BOOST_CHECK(c0.session.queueQuery("bar").getQueue().empty());
+ BOOST_CHECK(c1.session.queueQuery("bar").getQueue().empty());
+
+ cluster.add();
+ Client c2(aclSettings(cluster[2], "c2"), "c2");
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::UnauthorizedAccessException);
+ }
+ BOOST_CHECK(c2.session.queueQuery("bar").getQueue().empty());
+}
+
+QPID_AUTO_TEST_CASE(testMessageTimeToLive) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=ttlMessage("a", "q", 200, durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=ttlMessage("x", "p", 100000, durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("y", "p", durableFlag));
+ cluster.add();
+ Client c2(cluster[1], "c2");
+
+ BOOST_CHECK_EQUAL(browse(c0, "p", 1), list_of<string>("x"));
+ BOOST_CHECK_EQUAL(browse(c1, "p", 1), list_of<string>("x"));
+ BOOST_CHECK_EQUAL(browse(c2, "p", 1), list_of<string>("x"));
+
+ sys::usleep(200*1000);
+ BOOST_CHECK_EQUAL(browse(c0, "q", 1), list_of<string>("b"));
+ BOOST_CHECK_EQUAL(browse(c1, "q", 1), list_of<string>("b"));
+ BOOST_CHECK_EQUAL(browse(c2, "q", 1), list_of<string>("b"));
+}
+
+QPID_AUTO_TEST_CASE(testSequenceOptions) {
+ // Make sure the exchange qpid.msg_sequence property is properly replicated.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ FieldTable ftargs;
+ ftargs.setInt("qpid.msg_sequence", 1);
+ c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
+ c0.session.exchangeDeclare(arg::exchange="ex", arg::type="direct", arg::arguments=ftargs);
+ c0.session.exchangeBind(arg::exchange="ex", arg::queue="q", arg::bindingKey="k");
+ c0.session.messageTransfer(arg::content=makeMessage("1", "k", durableFlag), arg::destination="ex");
+ c0.session.messageTransfer(arg::content=makeMessage("2", "k", durableFlag), arg::destination="ex");
+ BOOST_CHECK_EQUAL(1, getMsgSequence(c0.subs.get("q", TIMEOUT)));
+ BOOST_CHECK_EQUAL(2, getMsgSequence(c0.subs.get("q", TIMEOUT)));
+
+ cluster.add();
+ Client c1(cluster[1]);
+ c1.session.messageTransfer(arg::content=makeMessage("3", "k", durableFlag), arg::destination="ex");
+ BOOST_CHECK_EQUAL(3, getMsgSequence(c1.subs.get("q", TIMEOUT)));
+}
+
+QPID_AUTO_TEST_CASE(testTxTransaction) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("A", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("B", "q", durableFlag));
+
+ // Start a transaction that will commit.
+ Session commitSession = c0.connection.newSession("commit");
+ SubscriptionManager commitSubs(commitSession);
+ commitSession.txSelect();
+ commitSession.messageTransfer(arg::content=makeMessage("a", "q", durableFlag));
+ commitSession.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
+ BOOST_CHECK_EQUAL(commitSubs.get("q", TIMEOUT).getData(), "A");
+
+ // Start a transaction that will roll back.
+ Session rollbackSession = c0.connection.newSession("rollback");
+ SubscriptionManager rollbackSubs(rollbackSession);
+ rollbackSession.txSelect();
+ rollbackSession.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
+ Message rollbackMessage = rollbackSubs.get("q", TIMEOUT);
+ BOOST_CHECK_EQUAL(rollbackMessage.getData(), "B");
+
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+ // Add new member mid transaction.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+
+ // More transactional work
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ rollbackSession.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
+ commitSession.messageTransfer(arg::content=makeMessage("c", "q", durableFlag));
+ rollbackSession.messageTransfer(arg::content=makeMessage("3", "q", durableFlag));
+
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Commit/roll back.
+ commitSession.txCommit();
+ rollbackSession.txRollback();
+ rollbackSession.messageRelease(rollbackMessage.getId());
+
+ // Verify queue status: just the comitted messages and dequeues should remain.
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 4u);
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "B");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "a");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "b");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "c");
+
+ commitSession.close();
+ rollbackSession.close();
+}
+
+QPID_AUTO_TEST_CASE(testUnacked) {
+ // Verify replication of unacknowledged messages.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ Message m;
+
+ // Create unacked message: acquired but not accepted.
+ SubscriptionSettings manualAccept(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 0);
+ c0.session.queueDeclare("q1", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("11","q1", durableFlag));
+ LocalQueue q1;
+ c0.subs.subscribe(q1, "q1", manualAccept);
+ BOOST_CHECK_EQUAL(q1.get(TIMEOUT).getData(), "11"); // Acquired but not accepted
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q1").getMessageCount(), 0u); // Gone from queue
+
+ // Create unacked message: not acquired, accepted or completeed.
+ SubscriptionSettings manualAcquire(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_NOT_ACQUIRED, 0);
+ c0.session.queueDeclare("q2", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("21","q2", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("22","q2", durableFlag));
+ LocalQueue q2;
+ c0.subs.subscribe(q2, "q2", manualAcquire);
+ m = q2.get(TIMEOUT); // Not acquired or accepted, still on queue
+ BOOST_CHECK_EQUAL(m.getData(), "21");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 2u); // Not removed
+ c0.subs.getSubscription("q2").acquire(m); // Acquire manually
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // Removed
+ BOOST_CHECK_EQUAL(q2.get(TIMEOUT).getData(), "22"); // Not acquired or accepted, still on queue
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // 1 not acquired.
+
+ // Create empty credit record: acquire and accept but don't complete.
+ SubscriptionSettings manualComplete(FlowControl::messageWindow(1), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 1, MANUAL_COMPLETION);
+ c0.session.queueDeclare("q3", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("31", "q3", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("32", "q3", durableFlag));
+ LocalQueue q3;
+ c0.subs.subscribe(q3, "q3", manualComplete);
+ Message m31=q3.get(TIMEOUT);
+ BOOST_CHECK_EQUAL(m31.getData(), "31"); // Automatically acquired & accepted but not completed.
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 1u);
+
+ // Add new member while there are unacked messages.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+
+ // Check queue counts
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 1u);
+
+ // Complete the empty credit message, should unblock the message behind it.
+ BOOST_CHECK_THROW(q3.get(0), Exception);
+ c0.session.markCompleted(SequenceSet(m31.getId()), true);
+ BOOST_CHECK_EQUAL(q3.get(TIMEOUT).getData(), "32");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 0u);
+
+ // Close the original session - unacked messages should be requeued.
+ c0.session.close();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 2u);
+
+ BOOST_CHECK_EQUAL(c1.subs.get("q1", TIMEOUT).getData(), "11");
+ BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "21");
+ BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "22");
+}
+
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testUpdateTxState() {
+ // Verify that we update transaction state correctly to new members.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ // Do work in a transaction.
+ c0.session.txSelect();
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("1","q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("2","q", durableFlag));
+ Message m;
+ BOOST_CHECK(c0.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "1");
+
+ // New member, TX not comitted, c1 should see nothing.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
+
+ // After commit c1 shoudl see results of tx.
+ c0.session.txCommit();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "2");
+
+ // Another transaction with both members active.
+ c0.session.messageTransfer(arg::content=makeMessage("3","q", durableFlag));
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
+ c0.session.txCommit();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "3");
+}
+
+QPID_AUTO_TEST_CASE(testUpdateMessageBuilder) {
+ // Verify that we update a partially recieved message to a new member.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ Sender sender(ConnectionAccess::getImpl(c0.connection), c0.session.getChannel());
+
+ // Send first 2 frames of message.
+ MessageTransferBody transfer(
+ ProtocolVersion(), string(), // default exchange.
+ framing::message::ACCEPT_MODE_NONE,
+ framing::message::ACQUIRE_MODE_PRE_ACQUIRED);
+ sender.send(transfer, true, false, true, true);
+ AMQHeaderBody header;
+ header.get<DeliveryProperties>(true)->setRoutingKey("q");
+ if (durableFlag)
+ header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_PERSISTENT);
+ else
+ header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_NON_PERSISTENT);
+ sender.send(header, false, false, true, true);
+
+ // No reliable way to ensure the partial message has arrived
+ // before we start the new broker, so we sleep.
+ sys::usleep(2500);
+ cluster.add();
+
+ // Send final 2 frames of message.
+ sender.send(AMQContentBody("ab"), false, true, true, false);
+ sender.send(AMQContentBody("cd"), false, true, false, true);
+
+ // Verify message is enqued correctly on second member.
+ Message m;
+ Client c1(cluster[1], "c1");
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "abcd");
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());
+}
+
+QPID_AUTO_TEST_CASE(testConnectionKnownHosts) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ set<int> kb0 = knownBrokerPorts(c0.connection, 1);
+ BOOST_CHECK_EQUAL(kb0.size(), 1u);
+ BOOST_CHECK_EQUAL(kb0, makeSet(cluster));
+
+ cluster.add();
+ Client c1(cluster[1], "c1");
+ set<int> kb1 = knownBrokerPorts(c1.connection, 2);
+ kb0 = knownBrokerPorts(c0.connection, 2);
+ BOOST_CHECK_EQUAL(kb1.size(), 2u);
+ BOOST_CHECK_EQUAL(kb1, makeSet(cluster));
+ BOOST_CHECK_EQUAL(kb1,kb0);
+
+ cluster.add();
+ Client c2(cluster[2], "c2");
+ set<int> kb2 = knownBrokerPorts(c2.connection, 3);
+ kb1 = knownBrokerPorts(c1.connection, 3);
+ kb0 = knownBrokerPorts(c0.connection, 3);
+ BOOST_CHECK_EQUAL(kb2.size(), 3u);
+ BOOST_CHECK_EQUAL(kb2, makeSet(cluster));
+ BOOST_CHECK_EQUAL(kb2,kb0);
+ BOOST_CHECK_EQUAL(kb2,kb1);
+
+ cluster.killWithSilencer(1,c1.connection,9);
+ kb0 = knownBrokerPorts(c0.connection, 2);
+ kb2 = knownBrokerPorts(c2.connection, 2);
+ BOOST_CHECK_EQUAL(kb0.size(), 2u);
+ BOOST_CHECK_EQUAL(kb0, kb2);
+}
+
+QPID_AUTO_TEST_CASE(testUpdateConsumers) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.subs.subscribe(c0.lq, "q", FlowControl::zero());
+ LocalQueue lp;
+ c0.subs.subscribe(lp, "p", FlowControl::messageCredit(1));
+ c0.session.sync();
+
+ // Start new members
+ cluster.add(); // Local
+ Client c1(cluster[1], "c1");
+ cluster.add();
+ Client c2(cluster[2], "c2");
+
+ // Transfer messages
+ c0.session.messageTransfer(arg::content=makeMessage("aaa", "q", durableFlag));
+
+ c0.session.messageTransfer(arg::content=makeMessage("bbb", "p", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("ccc", "p", durableFlag));
+
+ // Activate the subscription, ensure message removed on all queues.
+ c0.subs.setFlowControl("q", FlowControl::unlimited());
+ Message m;
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "aaa");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Check second subscription's flow control: gets first message, not second.
+ BOOST_CHECK(lp.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "bbb");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("p").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("p").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 1u);
+
+ BOOST_CHECK(c0.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "ccc");
+
+ // Kill the subscribing member, ensure further messages are not removed.
+ cluster.killWithSilencer(0,c0.connection,9);
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c1.connection, 2).size(), 2u);
+ for (int i = 0; i < 10; ++i) {
+ c1.session.messageTransfer(arg::content=makeMessage("xxx", "q", durableFlag));
+ BOOST_REQUIRE(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_REQUIRE_EQUAL(m.getData(), "xxx");
+ }
+}
+
+// Test that message data and delivery properties are updated properly.
+QPID_AUTO_TEST_CASE(testUpdateMessages) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ // Create messages with different delivery properties
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.exchangeBind(arg::exchange="amq.fanout", arg::queue="q");
+ c0.session.messageTransfer(arg::content=makeMessage("foo","q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar","q", durableFlag),
+ arg::destination="amq.fanout");
+
+ while (c0.session.queueQuery("q").getMessageCount() != 2)
+ sys::usleep(1000); // Wait for message to show up on broker 0.
+
+ // Add a new broker, it will catch up.
+ cluster.add();
+
+ // Do some work post-add
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("pfoo","p", durableFlag));
+
+ // Do some work post-join
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 2).size(), 2u);
+ c0.session.messageTransfer(arg::content=makeMessage("pbar","p", durableFlag));
+
+ // Verify new brokers have state.
+ Message m;
+
+ Client c1(cluster[1], "c1");
+
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "foo");
+ BOOST_CHECK(m.getDeliveryProperties().hasExchange());
+ BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "");
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "bar");
+ BOOST_CHECK(m.getDeliveryProperties().hasExchange());
+ BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "amq.fanout");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Add another broker, don't wait for join - should be stalled till ready.
+ cluster.add();
+ Client c2(cluster[2], "c2");
+ BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "pfoo");
+ BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "pbar");
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testWiringReplication) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
+ Client c0(cluster[0]);
+ BOOST_CHECK(c0.session.queueQuery("q").getQueue().empty());
+ BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty());
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.exchangeDeclare("ex", arg::type="direct");
+ c0.session.close();
+ c0.connection.close();
+ // Verify all brokers get wiring update.
+ for (size_t i = 0; i < cluster.size(); ++i) {
+ BOOST_MESSAGE("i == "<< i);
+ Client c(cluster[i]);
+ BOOST_CHECK_EQUAL("q", c.session.queueQuery("q").getQueue());
+ BOOST_CHECK_EQUAL("direct", c.session.exchangeQuery("ex").getType());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testMessageEnqueue) {
+ // Enqueue on one broker, dequeue on another.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0]);
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
+ c0.session.close();
+ Client c1(cluster[1]);
+ Message msg;
+ BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(string("foo"), msg.getData());
+ BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(string("bar"), msg.getData());
+}
+
+QPID_AUTO_TEST_CASE(testMessageDequeue) {
+ // Enqueue on one broker, dequeue on two others.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
+
+ Message msg;
+
+ // Dequeue on 2 others, ensure correct order.
+ Client c1(cluster[1], "c1");
+ BOOST_CHECK(c1.subs.get(msg, "q"));
+ BOOST_CHECK_EQUAL("foo", msg.getData());
+
+ Client c2(cluster[2], "c2");
+ BOOST_CHECK(c1.subs.get(msg, "q"));
+ BOOST_CHECK_EQUAL("bar", msg.getData());
+
+ // Queue should be empty on all cluster members.
+ BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount());
+ BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount());
+ BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount());
+}
+
+QPID_AUTO_TEST_CASE(testDequeueWaitingSubscription) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
+ Client c0(cluster[0]);
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 3).size(), 3u); // Wait for brokers.
+
+ // First start a subscription.
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.subs.subscribe(c0.lq, "q", FlowControl::messageCredit(2));
+
+ // Now send messages
+ Client c1(cluster[1]);
+ c1.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c1.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
+
+ // Check they arrived
+ Message m;
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL("foo", m.getData());
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL("bar", m.getData());
+
+ // Queue should be empty on all cluster members.
+ Client c2(cluster[2]);
+ BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount());
+ BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount());
+ BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount());
+}
+
+QPID_AUTO_TEST_CASE(queueDurabilityPropagationToNewbie)
+{
+ /*
+ Start with a single broker.
+ Set up two queues: one durable, and one not.
+ Add a new broker to the cluster.
+ Make sure it has one durable and one non-durable queue.
+ */
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0]);
+ c0.session.queueDeclare("durable_queue", arg::durable=true);
+ c0.session.queueDeclare("non_durable_queue", arg::durable=false);
+ cluster.add();
+ Client c1(cluster[1]);
+ QueueQueryResult durable_query = c1.session.queueQuery ( "durable_queue" );
+ QueueQueryResult non_durable_query = c1.session.queueQuery ( "non_durable_queue" );
+ BOOST_CHECK_EQUAL(durable_query.getQueue(), std::string("durable_queue"));
+ BOOST_CHECK_EQUAL(non_durable_query.getQueue(), std::string("non_durable_queue"));
+
+ BOOST_CHECK_EQUAL ( durable_query.getDurable(), true );
+ BOOST_CHECK_EQUAL ( non_durable_query.getDurable(), false );
+}
+
+
+QPID_AUTO_TEST_CASE(testHeartbeatCancelledOnFailover)
+{
+
+ struct Sender : FailoverManager::Command
+ {
+ std::string queue;
+ std::string content;
+
+ Sender(const std::string& q, const std::string& c) : queue(q), content(c) {}
+
+ void execute(AsyncSession& session, bool)
+ {
+ session.messageTransfer(arg::content=makeMessage(content, queue, durableFlag));
+ }
+ };
+
+ struct Receiver : FailoverManager::Command, MessageListener, qpid::sys::Runnable
+ {
+ FailoverManager& mgr;
+ std::string queue;
+ std::string expectedContent;
+ qpid::client::Subscription subscription;
+ qpid::sys::Monitor lock;
+ bool ready, failed;
+
+ Receiver(FailoverManager& m, const std::string& q, const std::string& c) : mgr(m), queue(q), expectedContent(c), ready(false), failed(false) {}
+
+ void received(Message& message)
+ {
+ BOOST_CHECK_EQUAL(expectedContent, message.getData());
+ subscription.cancel();
+ }
+
+ void execute(AsyncSession& session, bool)
+ {
+ session.queueDeclare(arg::queue=queue, arg::durable=durableFlag);
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queue);
+ session.sync();
+ setReady();
+ subs.run();
+ //cleanup:
+ session.queueDelete(arg::queue=queue);
+ }
+
+ void run()
+ {
+ try {
+ mgr.execute(*this);
+ }
+ catch (const std::exception& e) {
+ BOOST_MESSAGE("Exception in mgr.execute: " << e.what());
+ failed = true;
+ }
+ }
+
+ void waitForReady()
+ {
+ qpid::sys::Monitor::ScopedLock l(lock);
+ while (!ready) {
+ lock.wait();
+ }
+ }
+
+ void setReady()
+ {
+ qpid::sys::Monitor::ScopedLock l(lock);
+ ready = true;
+ lock.notify();
+ }
+ };
+
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
+ ConnectionSettings settings;
+ settings.port = cluster[1];
+ settings.heartbeat = 1;
+ FailoverManager fmgr(settings);
+ Sender sender("my-queue", "my-data");
+ Receiver receiver(fmgr, "my-queue", "my-data");
+ qpid::sys::Thread runner(receiver);
+ receiver.waitForReady();
+ {
+ ScopedSuppressLogging allQuiet; // suppress connection closed messages
+ cluster.kill(1);
+ //sleep for 2 secs to allow the heartbeat task to fire on the now dead connection:
+ ::usleep(2*1000*1000);
+ }
+ fmgr.execute(sender);
+ runner.join();
+ BOOST_CHECK(!receiver.failed);
+ fmgr.close();
+}
+
+QPID_AUTO_TEST_CASE(testPolicyUpdate) {
+ //tests that the policys internal state is accurate on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(REJECT, 0, 2);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ c1.session.messageTransfer(arg::content=makeMessage("one", "q", durableFlag));
+ cluster.add();
+ Client c2(cluster[1], "c2");
+ c2.session.messageTransfer(arg::content=makeMessage("two", "q", durableFlag));
+
+ BOOST_CHECK_THROW(c2.session.messageTransfer(arg::content=makeMessage("three", "q", durableFlag)), framing::ResourceLimitExceededException);
+
+ Message received;
+ BOOST_CHECK(c1.subs.get(received, "q"));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("one"));
+ BOOST_CHECK(c1.subs.get(received, "q"));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("two"));
+ BOOST_CHECK(!c1.subs.get(received, "q"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveQueueUpdate) {
+ //tests that exclusive queues are accurately replicated on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ c1.session.queueDeclare("q", arg::exclusive=true, arg::autoDelete=true, arg::alternateExchange="amq.fanout");
+ cluster.add();
+ Client c2(cluster[1], "c2");
+ QueueQueryResult result = c2.session.queueQuery("q");
+ BOOST_CHECK_EQUAL(result.getQueue(), std::string("q"));
+ BOOST_CHECK(result.getExclusive());
+ BOOST_CHECK(result.getAutoDelete());
+ BOOST_CHECK(!result.getDurable());
+ BOOST_CHECK_EQUAL(result.getAlternateExchange(), std::string("amq.fanout"));
+ BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::exclusive=true, arg::passive=true), framing::ResourceLockedException);
+ c1.session.close();
+ c1.connection.close();
+ c2.session = c2.connection.newSession();
+ BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::passive=true), framing::NotFoundException);
+ }
+}
+
+/**
+ * Subscribes to specified queue and acquires up to the specified
+ * number of message but does not accept or release them. These
+ * message are therefore 'locked' by the clients session.
+ */
+Subscription lockMessages(Client& client, const std::string& queue, int count)
+{
+ LocalQueue q;
+ SubscriptionSettings settings(FlowControl::messageCredit(count));
+ settings.autoAck = 0;
+ Subscription sub = client.subs.subscribe(q, queue, settings);
+ client.session.messageFlush(sub.getName());
+ return sub;
+}
+
+/**
+ * check that the specified queue contains the expected set of
+ * messages (matched on content) for all nodes in the cluster
+ */
+void checkQueue(ClusterFixture& cluster, const std::string& queue, const std::vector<std::string>& messages)
+{
+ for (size_t i = 0; i < cluster.size(); i++) {
+ Client client(cluster[i], (boost::format("%1%_%2%") % "c" % (i+1)).str());
+ BOOST_CHECK_EQUAL(browse(client, queue, messages.size()), messages);
+ client.close();
+ }
+}
+
+void send(Client& client, const std::string& queue, int count, int start=1, const std::string& base="m",
+ const std::string& lvqKey="")
+{
+ for (int i = 0; i < count; i++) {
+ Message message = makeMessage((boost::format("%1%_%2%") % base % (i+start)).str(), queue, durableFlag);
+ if (!lvqKey.empty()) message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqKey);
+ client.session.messageTransfer(arg::content=message);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRingQueueUpdate) {
+ //tests that ring queues are accurately replicated on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(RING, 0, 5);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ send(c1, "q", 5);
+ lockMessages(c1, "q", 1);
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+ //send one more message
+ send(c1, "q", 1, 6);
+ //release locked message
+ c1.close();
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRingQueueUpdate2) {
+ //tests that ring queues are accurately replicated on newly joined
+ //nodes; just like testRingQueueUpdate, but new node joins after
+ //the sixth message has been sent.
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(RING, 0, 5);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ send(c1, "q", 5);
+ lockMessages(c1, "q", 1);
+ //send sixth message
+ send(c1, "q", 1, 6);
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+ //release locked message
+ c1.close();
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testLvqUpdate) {
+ //tests that lvqs are accurately replicated on newly joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setOrdering(LVQ);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+
+ send(c1, "q", 5, 1, "a", "a");
+ send(c1, "q", 2, 1, "b", "b");
+ send(c1, "q", 1, 1, "c", "c");
+ send(c1, "q", 1, 3, "b", "b");
+
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("a_5")("b_3")("c_1"));
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testBrowsedLvqUpdate) {
+ //tests that lvqs are accurately replicated on newly joined nodes
+ //if the lvq state has been affected by browsers
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setOrdering(LVQ);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+
+ send(c1, "q", 1, 1, "a", "a");
+ send(c1, "q", 2, 1, "b", "b");
+ send(c1, "q", 1, 1, "c", "c");
+ checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1"));
+ send(c1, "q", 4, 2, "a", "a");
+ send(c1, "q", 1, 3, "b", "b");
+
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1")("a_5")("b_3"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRelease) {
+ //tests that releasing a messages that was unacked when one node
+ //joined works correctly
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ c1.session.queueDeclare("q", arg::durable=durableFlag);
+ for (int i = 0; i < 5; i++) {
+ c1.session.messageTransfer(arg::content=makeMessage((boost::format("%1%_%2%") % "m" % (i+1)).str(), "q", durableFlag));
+ }
+ //receive but don't ack a message
+ LocalQueue lq;
+ SubscriptionSettings lqSettings(FlowControl::messageCredit(1));
+ lqSettings.autoAck = 0;
+ Subscription lqSub = c1.subs.subscribe(lq, "q", lqSettings);
+ c1.session.messageFlush("q");
+ Message received;
+ BOOST_CHECK(lq.get(received));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("m_1"));
+
+ //add new node
+ cluster.add();
+
+ lqSub.release(lqSub.getUnaccepted());
+
+ //check state of queue on both nodes
+ vector<string> expected = list_of<string>("m_1")("m_2")("m_3")("m_4")("m_5");
+ Client c3(cluster[0], "c3");
+ BOOST_CHECK_EQUAL(browse(c3, "q", 5), expected);
+ Client c2(cluster[1], "c2");
+ BOOST_CHECK_EQUAL(browse(c2, "q", 5), expected);
+ }
+}
+
+
+// Browse for 1 message with byte credit, return true if a message was
+// received false if not.
+bool browseByteCredit(Client& c, const string& q, int n, Message& m) {
+ SubscriptionSettings browseSettings(
+ FlowControl(1, n, false), // 1 message, n bytes credit, no window
+ ACCEPT_MODE_NONE,
+ ACQUIRE_MODE_NOT_ACQUIRED,
+ 0 // No auto-ack.
+ );
+ LocalQueue lq;
+ Subscription s = c.subs.subscribe(lq, q, browseSettings);
+ c.session.messageFlush(arg::destination=q, arg::sync=true);
+ c.session.sync();
+ c.subs.getSubscription(q).cancel();
+ return lq.get(m, 0); // No timeout, flush should push message thru.
+}
+
+// Ensure cluster update preserves exact message size, use byte credt as test.
+QPID_AUTO_TEST_CASE(testExactByteCredit) {
+ ClusterFixture cluster(1, prepareArgs(), -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q");
+ c0.session.messageTransfer(arg::content=Message("MyMessage", "q"));
+ cluster.add();
+
+ int size=36; // Size of message on broker: headers+body
+ Client c1(cluster[1], "c1");
+ Message m;
+
+ // Ensure we get the message with exact credit.
+ BOOST_CHECK(browseByteCredit(c0, "q", size, m));
+ BOOST_CHECK(browseByteCredit(c1, "q", size, m));
+ // and not with one byte less.
+ BOOST_CHECK(!browseByteCredit(c0, "q", size-1, m));
+ BOOST_CHECK(!browseByteCredit(c1, "q", size-1, m));
+}
+
+// Test that consumer positions are updated correctly.
+// Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=541927
+//
+QPID_AUTO_TEST_CASE(testUpdateConsumerPosition) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+ // Set the acquire mode to 'not-acquired' the consumer moves along the queue
+ // but does not acquire (remove) messages.
+ settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
+ Subscription s = c0.subs.subscribe(c0.lq, "q", settings);
+ c0.session.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
+ BOOST_CHECK_EQUAL("1", c0.lq.get(TIMEOUT).getData());
+
+ // Add another member, send/receive another message and acquire
+ // the messages. With the bug, this creates an inconsistency
+ // because the browse position was not updated to the new member.
+ cluster.add();
+ c0.session.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
+ BOOST_CHECK_EQUAL("2", c0.lq.get(TIMEOUT).getData());
+ s.acquire(s.getUnacquired());
+ s.accept(s.getUnaccepted());
+
+ // In the bug we now have 0 messages on cluster[0] and 1 message on cluster[1]
+ // Subscribing on cluster[1] provokes an error that shuts down cluster[0]
+ Client c1(cluster[1], "c1");
+ Subscription s1 = c1.subs.subscribe(c1.lq, "q"); // Default auto-ack=1
+ Message m;
+ BOOST_CHECK(!c1.lq.get(m, TIMEOUT/10));
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testFairsharePriorityDelivery) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ FieldTable arguments;
+ arguments.setInt("x-qpid-priorities", 10);
+ arguments.setInt("x-qpid-fairshare", 5);
+ c0.session.queueDeclare("q", arg::durable=durableFlag, arg::arguments=arguments);
+
+ //send messages of different priorities
+ for (int i = 0; i < 20; i++) {
+ Message msg = makeMessage((boost::format("msg-%1%") % i).str(), "q", durableFlag);
+ msg.getDeliveryProperties().setPriority(i % 2 ? 9 : 5);
+ c0.session.messageTransfer(arg::content=msg);
+ }
+
+ //pull off a couple of the messages (first four should be the top priority messages
+ for (int i = 0; i < 4; i++) {
+ BOOST_CHECK_EQUAL((boost::format("msg-%1%") % ((i*2)+1)).str(), c0.subs.get("q", TIMEOUT).getData());
+ }
+
+ // Add another member
+ cluster.add();
+ Client c1(cluster[1], "c1");
+
+ //pull off some more messages
+ BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 9).str(), c0.subs.get("q", TIMEOUT).getData());
+ BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 0).str(), c1.subs.get("q", TIMEOUT).getData());
+ BOOST_CHECK_EQUAL((boost::format("msg-%1%") % 2).str(), c0.subs.get("q", TIMEOUT).getData());
+
+ //check queue has same content on both nodes
+ BOOST_CHECK_EQUAL(browse(c0, "q", 12), browse(c1, "q", 12));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/cluster_test_logs.py b/qpid/cpp/src/tests/cluster_test_logs.py
new file mode 100755
index 0000000000..9f7d1e2f6c
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_logs.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Functions for comparing broker log files, used by cluster_tests.py.
+
+import os, os.path, re, glob
+from itertools import izip
+
+def split_log(log):
+ """Split a broker log at checkpoints where a member joins.
+ Return the set of checkpoints discovered."""
+ checkpoint_re = re.compile("Member joined, frameSeq=([0-9]+), queue snapshot:")
+ outfile = None
+ checkpoints = []
+ for l in open(log):
+ match = checkpoint_re.search(l)
+ if match:
+ checkpoint = match.groups()[0]
+ checkpoints.append(checkpoint)
+ if outfile: outfile.close()
+ outfile = open("%s.%s"%(log, checkpoint), 'w')
+
+ if outfile: outfile.write(l)
+ if outfile: outfile.close()
+ return checkpoints
+
+def filter_log(log):
+ """Filter the contents of a log file to remove data that is expected
+ to differ between brokers in a cluster. Filtered log contents between
+ the same checkpoints should match across the cluster."""
+ out = open("%s.filter"%(log), 'w')
+ # Lines to skip entirely, expected differences
+ skip = "|".join([
+ 'local connection', # Only on local broker
+ 'UPDATER|UPDATEE', # Ignore update process
+ 'stall for update|unstall, ignore update|cancelled offer .* unstall',
+ 'caught up',
+ 'active for links|Passivating links|Activating links',
+ 'info Connection.* connected to', # UpdateClient connection
+ 'warning Connection [\d+ [0-9.:]+] closed', # UpdateClient connection
+ 'warning Broker closed connection: 200, OK',
+ 'task late',
+ 'task overran',
+ 'warning CLOSING .* unsent data',
+ 'Inter-broker link ',
+ 'Running in a cluster, marking store',
+ 'debug Sending keepalive signal to watchdog', # Watchdog timer thread
+ 'last broker standing joined by 1 replicas, updating queue policies.',
+ 'Connection .* timed out: closing' # heartbeat connection close
+ ])
+ # Regex to match a UUID
+ uuid='\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w'
+ # Substitutions to remove expected differences
+ subs = [
+ (r'\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d ', ''), # Remove timestamp
+ (r'cluster\([0-9.: ]*', 'cluster('), # Remove cluster node id
+ (r' local\)| shadow\)', ')'), # Remove local/shadow indication
+ (r'CATCHUP', 'READY'), # Treat catchup as equivalent to ready.
+ (r'OFFER', 'READY'), # Treat offer as equivalent to ready.
+ # System UUID expected to be different
+ (r'(org.apache.qpid.broker:system[:(])%s(\)?)'%(uuid), r'\1UUID\2'),
+
+ # TODO aconway 2010-12-20: review if these should be expected:
+ (r' len=\d+', ' len=NN'), # buffer lengths
+ (r' map={.*_object_name:([^,}]*)[,}].*', r' \1'), # V2 map - just keep name
+ (r'\d+-\d+-\d+--\d+', 'X-X-X--X'), # V1 Object IDs
+ ]
+ # Substitutions to mask known issue: durable test shows inconsistent "changed stats for com.redhat.rhm.store:journal" messages.
+ skip += '|Changed V[12] statistics com.redhat.rhm.store:journal'
+ subs += [(r'to=console.obj.1.0.com.redhat.rhm.store.journal props=\d+ stats=\d+',
+ 'to=console.obj.1.0.com.redhat.rhm.store.journal props=NN stats=NN')]
+
+ skip_re = re.compile(skip)
+ subs = [(re.compile(pattern), subst) for pattern, subst in subs]
+ for l in open(log):
+ if skip_re.search(l): continue
+ for pattern,subst in subs: l = re.sub(pattern,subst,l)
+ out.write(l)
+ out.close()
+
+def verify_logs():
+ """Compare log files from cluster brokers, verify that they correspond correctly."""
+ for l in glob.glob("*.log"): filter_log(l)
+ checkpoints = set()
+ for l in glob.glob("*.filter"): checkpoints = checkpoints.union(set(split_log(l)))
+ errors=[]
+ for c in checkpoints:
+ fragments = glob.glob("*.filter.%s"%(c))
+ fragments.sort(reverse=True, key=os.path.getsize)
+ while len(fragments) >= 2:
+ a = fragments.pop(0)
+ b = fragments[0]
+ for ab in izip(open(a), open(b)):
+ if ab[0] != ab[1]:
+ errors.append("\n %s %s"%(a, b))
+ break
+ if errors:
+ raise Exception("Files differ in %s"%(os.getcwd())+"".join(errors))
+
+# Can be run as a script.
+if __name__ == "__main__":
+ verify_logs()
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/README.txt b/qpid/cpp/src/tests/cluster_test_scripts/README.txt
new file mode 100644
index 0000000000..e861a2f397
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/README.txt
@@ -0,0 +1,20 @@
+Cluster test scripts.
+
+A set of scripts to start and stop cluster and test clients on
+multiple hosts using ssh.
+
+Pre-requisites: You must be
+ - set up for password-free ssh access to the test hosts.
+ - a member of the ais group on all the test hosts.
+
+Configuration:
+
+Copy defaults.sh to config.sh and edit the values as necessary.
+
+Test scripts:
+
+Test scripts use the functions in functions.sh to start & monitor
+cluster and clients.
+A test script can collect other scripts.
+
+
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/cluster_check b/qpid/cpp/src/tests/cluster_test_scripts/cluster_check
new file mode 100755
index 0000000000..05fcc1bcd2
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/cluster_check
@@ -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.
+#
+
+# Check that all members of a cluster are running
+
+source config.sh
+
+HOSTS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+for ((i=0; i<${#HOSTS[*]}; ++i)); do
+ host=${HOSTS[$i]}
+ port=${PORTS[$i]}
+ ssh $host "$QPIDD -cp $port" > /dev/null || {
+ ret=1
+ echo "ERROR: broker not running $host:$port"
+ }
+done
+exit $ret
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/cluster_start b/qpid/cpp/src/tests/cluster_test_scripts/cluster_start
new file mode 100755
index 0000000000..8911358f7e
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/cluster_start
@@ -0,0 +1,56 @@
+#!/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.
+#
+
+# Start a cluster
+#
+# Arguments: NAME HOST [host...]
+# Start a cluster called NAME with N nodes running on the given HOSTs
+# repeat the host name to run multiple brokers on one host. Use dynamic
+# ports.
+#
+# Log files, data directories and hosts/ports files are all stored under
+# $HOME/cluster_test/$NAME
+#
+
+source config.sh
+
+CLUSTER_NAME=`date +"${USER}_%F_%T"`
+HOSTS=($BROKER_HOSTS)
+for ((i = 0; i < ${#HOSTS[*]}; ++i)) ; do
+ host=${HOSTS[$i]}
+ datadir=$CLUSTER_HOME/broker$i
+ log=$datadir/qpidd.log
+ ssh $host "rm -rf $datadir; mkdir -p $datadir" || {
+ echo "ERROR: can't make data dir $datadir"; exit 1
+ }
+ port=`ssh $host "echo $QPIDD -dp0 --cluster-name=$CLUSTER_NAME \
+ --data-dir=$datadir \
+ --log-to-file=$log --log-prefix=broker$i \
+ $QPIDD_OPTS | newgrp ais"` || {
+ error "ERROR: can't start broker $i on $host"; exit 1;
+ }
+ PORTS="$PORTS $port"
+done
+
+echo "$BROKER_HOSTS" > $CLUSTER_HOME/hosts
+echo "$PORTS" > $CLUSTER_HOME/ports
+
+`dirname $0`/cluster_check $NAME
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/cluster_stop b/qpid/cpp/src/tests/cluster_test_scripts/cluster_stop
new file mode 100755
index 0000000000..09aa8f3b21
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/cluster_stop
@@ -0,0 +1,38 @@
+#!/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.
+#
+
+# Stop the cluster.
+
+source config.sh
+
+HOSTS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+for ((i=0; i<${#HOSTS[*]}; ++i)); do
+ host=${HOSTS[$i]}
+ port=${PORTS[$i]}
+ ssh $host "$QPIDD -qp $port" > /dev/null || {
+ ret=1
+ echo "ERROR: stopping broker at $host:$port"
+ }
+done
+
+exit $ret
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/config_example.sh b/qpid/cpp/src/tests/cluster_test_scripts/config_example.sh
new file mode 100755
index 0000000000..d47c9a9c77
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/config_example.sh
@@ -0,0 +1,44 @@
+# Cluster configuration.
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# All output stored under $HOME/$CLUSTER_HOME.
+CLUSTER_HOME=$HOME/cluster_test
+
+# Hosts where brokers will be run. Repeat hostname to run multiple brokers on 1 host.
+BROKER_HOSTS="mrg22 mrg23 mrg24 mrg25 mrg26"
+
+# Hosts where clients will be run.
+CLIENT_HOSTS="$BROKER_HOSTS"
+
+# Paths to executables
+QPIDD=qpidd
+PERFTEST=perftest
+
+# Directory containing tests
+TESTDIR=/usr/bin
+
+# Options for qpidd, must be sufficient to load the cluster plugin.
+# Scripts will add --cluster-name, --daemon, --port and --log-to-file options here.
+QPIDD_OPTS=" \
+--auth=no \
+--log-enable=notice+ \
+--log-enable=debug+:cluster \
+"
diff --git a/qpid/cpp/src/tests/cluster_test_scripts/perftest b/qpid/cpp/src/tests/cluster_test_scripts/perftest
new file mode 100755
index 0000000000..984761eb5f
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_test_scripts/perftest
@@ -0,0 +1,54 @@
+#!/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.
+#
+
+# Run a distributed perftest against a cluster.
+# Args: npubs nsubs [perftest-options]
+
+source config.sh
+
+NPUBS=${1:-4} ; shift
+NSUBS=${1:-4} ; shift
+OPTS="--npubs $NPUBS --nsubs $NSUBS $*"
+
+CLIENTS=($CLIENT_HOSTS)
+BROKERS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+start() {
+ client=${CLIENTS[i % ${#CLIENTS[*]}]}
+ broker=${BROKERS[i % ${#BROKERS[*]}]}
+ port=${PORTS[i % ${#PORTS[*]}]}
+ ssh -n $client $PERFTEST $OPTS $* -b $broker -p $port &
+ PIDS="$PIDS $!"
+}
+
+ssh ${CLIENTS[0]} $PERFTEST $OPTS --setup -b ${BROKERS[0]} -p${PORTS[0]}
+for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done
+for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done
+ssh ${CLIENTS[0]} $PERFTEST $OPTS --control -b ${BROKERS[0]} -p${PORTS[0]}
+
+for pid in $PIDS; do
+ wait $pid || echo "ERROR: client process $pid failed"
+done
+
+`dirname $0`/cluster_check
+
+
diff --git a/qpid/cpp/src/tests/cluster_tests.fail b/qpid/cpp/src/tests/cluster_tests.fail
new file mode 100644
index 0000000000..b28b04f643
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_tests.fail
@@ -0,0 +1,3 @@
+
+
+
diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py
new file mode 100755
index 0000000000..593791297a
--- /dev/null
+++ b/qpid/cpp/src/tests/cluster_tests.py
@@ -0,0 +1,1057 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, time, imp, re, subprocess, glob, cluster_test_logs
+from qpid import datatypes, messaging
+from brokertest import *
+from qpid.harness import Skipped
+from qpid.messaging import Message, Empty, Disposition, REJECTED
+from threading import Thread, Lock, Condition
+from logging import getLogger
+from itertools import chain
+from tempfile import NamedTemporaryFile
+
+log = getLogger("qpid.cluster_tests")
+
+# Note: brokers that shut themselves down due to critical error during
+# normal operation will still have an exit code of 0. Brokers that
+# shut down because of an error found during initialize will exit with
+# a non-0 code. Hence the apparently inconsistent use of EXPECT_EXIT_OK
+# and EXPECT_EXIT_FAIL in some of the tests below.
+
+# TODO aconway 2010-03-11: resolve this - ideally any exit due to an error
+# should give non-0 exit status.
+
+# Import scripts as modules
+qpid_cluster=import_script(checkenv("QPID_CLUSTER_EXEC"))
+
+def readfile(filename):
+ """Returns te content of file named filename as a string"""
+ f = file(filename)
+ try: return f.read()
+ finally: f.close()
+
+class ShortTests(BrokerTest):
+ """Short cluster functionality tests."""
+
+ def test_message_replication(self):
+ """Test basic cluster message replication."""
+ # Start a cluster, send some messages to member 0.
+ cluster = self.cluster(2)
+ s0 = cluster[0].connect().session()
+ s0.sender("q; {create:always}").send(Message("x"))
+ s0.sender("q; {create:always}").send(Message("y"))
+ s0.connection.close()
+
+ # Verify messages available on member 1.
+ s1 = cluster[1].connect().session()
+ m = s1.receiver("q", capacity=1).fetch(timeout=1)
+ s1.acknowledge()
+ self.assertEqual("x", m.content)
+ s1.connection.close()
+
+ # Start member 2 and verify messages available.
+ s2 = cluster.start().connect().session()
+ m = s2.receiver("q", capacity=1).fetch(timeout=1)
+ s2.acknowledge()
+ self.assertEqual("y", m.content)
+ s2.connection.close()
+
+ def test_store_direct_update_match(self):
+ """Verify that brokers stores an identical message whether they receive it
+ direct from clients or during an update, no header or other differences"""
+ cluster = self.cluster(0, args=["--load-module", self.test_store_lib])
+ cluster.start(args=["--test-store-dump", "direct.dump"])
+ # Try messages with various headers
+ cluster[0].send_message("q", Message(durable=True, content="foobar",
+ subject="subject",
+ reply_to="reply_to",
+ properties={"n":10}))
+ # Try messages of different sizes
+ for size in range(0,10000,100):
+ cluster[0].send_message("q", Message(content="x"*size, durable=True))
+ # Try sending via named exchange
+ c = cluster[0].connect_old()
+ s = c.session(str(qpid.datatypes.uuid4()))
+ s.exchange_bind(exchange="amq.direct", binding_key="foo", queue="q")
+ props = s.delivery_properties(routing_key="foo", delivery_mode=2)
+ s.message_transfer(
+ destination="amq.direct",
+ message=qpid.datatypes.Message(props, "content"))
+
+ # Now update a new member and compare their dumps.
+ cluster.start(args=["--test-store-dump", "updatee.dump"])
+ assert readfile("direct.dump") == readfile("updatee.dump")
+ os.remove("direct.dump")
+ os.remove("updatee.dump")
+
+ def test_sasl(self):
+ """Test SASL authentication and encryption in a cluster"""
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ acl=os.path.join(os.getcwd(), "policy.acl")
+ aclf=file(acl,"w")
+ aclf.write("""
+acl deny zag@QPID create queue
+acl allow all all
+""")
+ aclf.close()
+ cluster = self.cluster(2, args=["--auth", "yes",
+ "--sasl-config", sasl_config,
+ "--load-module", os.getenv("ACL_LIB"),
+ "--acl-file", acl])
+
+ # Valid user/password, ensure queue is created.
+ c = cluster[0].connect(username="zig", password="zig")
+ c.session().sender("ziggy;{create:always}")
+ c.close()
+ c = cluster[1].connect(username="zig", password="zig")
+ c.session().receiver("ziggy;{assert:always}")
+ c.close()
+ for b in cluster: b.ready() # Make sure all brokers still running.
+
+ # Valid user, bad password
+ try:
+ cluster[0].connect(username="zig", password="foo").close()
+ self.fail("Expected exception")
+ except messaging.exceptions.ConnectionError: pass
+ for b in cluster: b.ready() # Make sure all brokers still running.
+
+ # Bad user ID
+ try:
+ cluster[0].connect(username="foo", password="bar").close()
+ self.fail("Expected exception")
+ except messaging.exceptions.ConnectionError: pass
+ for b in cluster: b.ready() # Make sure all brokers still running.
+
+ # Action disallowed by ACL
+ c = cluster[0].connect(username="zag", password="zag")
+ try:
+ s = c.session()
+ s.sender("zaggy;{create:always}")
+ s.close()
+ self.fail("Expected exception")
+ except messaging.exceptions.UnauthorizedAccess: pass
+ # make sure the queue was not created at the other node.
+ c = cluster[0].connect(username="zag", password="zag")
+ try:
+ s = c.session()
+ s.sender("zaggy;{assert:always}")
+ s.close()
+ self.fail("Expected exception")
+ except messaging.exceptions.NotFound: pass
+
+ def test_user_id_update(self):
+ """Ensure that user-id of an open session is updated to new cluster members"""
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ cluster = self.cluster(1, args=["--auth", "yes", "--sasl-config", sasl_config,])
+ c = cluster[0].connect(username="zig", password="zig")
+ s = c.session().sender("q;{create:always}")
+ s.send(Message("x", user_id="zig")) # Message sent before start new broker
+ cluster.start()
+ s.send(Message("y", user_id="zig")) # Messsage sent after start of new broker
+ # Verify brokers are healthy and messages are on the queue.
+ self.assertEqual("x", cluster[0].get_message("q").content)
+ self.assertEqual("y", cluster[1].get_message("q").content)
+
+ def test_link_events(self):
+ """Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=611543"""
+ args = ["--mgmt-pub-interval", 1] # Publish management information every second.
+ broker1 = self.cluster(1, args)[0]
+ broker2 = self.cluster(1, args)[0]
+ qp = self.popen(["qpid-printevents", broker1.host_port()], EXPECT_RUNNING)
+ qr = self.popen(["qpid-route", "route", "add",
+ broker1.host_port(), broker2.host_port(),
+ "amq.fanout", "key"
+ ], EXPECT_EXIT_OK)
+ # Look for link event in printevents output.
+ retry(lambda: find_in_file("brokerLinkUp", qp.outfile("out")))
+ broker1.ready()
+ broker2.ready()
+
+ def test_queue_cleaner(self):
+ """ Regression test to ensure that cleanup of expired messages works correctly """
+ cluster = self.cluster(2, args=["--queue-purge-interval", 3])
+
+ s0 = cluster[0].connect().session()
+ sender = s0.sender("my-lvq; {create: always, node:{x-declare:{arguments:{'qpid.last_value_queue':1}}}}")
+ #send 10 messages that will all expire and be cleaned up
+ for i in range(1, 10):
+ msg = Message("message-%s" % i)
+ msg.properties["qpid.LVQ_key"] = "a"
+ msg.ttl = 0.1
+ sender.send(msg)
+ #wait for queue cleaner to run
+ time.sleep(3)
+
+ #test all is ok by sending and receiving a message
+ msg = Message("non-expiring")
+ msg.properties["qpid.LVQ_key"] = "b"
+ sender.send(msg)
+ s0.connection.close()
+ s1 = cluster[1].connect().session()
+ m = s1.receiver("my-lvq", capacity=1).fetch(timeout=1)
+ s1.acknowledge()
+ self.assertEqual("non-expiring", m.content)
+ s1.connection.close()
+
+ for b in cluster: b.ready() # Make sure all brokers still running.
+
+
+ def test_amqfailover_visible(self):
+ """Verify that the amq.failover exchange can be seen by
+ QMF-based tools - regression test for BZ615300."""
+ broker1 = self.cluster(1)[0]
+ broker2 = self.cluster(1)[0]
+ qs = subprocess.Popen(["qpid-stat", "-e", broker1.host_port()], stdout=subprocess.PIPE)
+ out = qs.communicate()[0]
+ assert out.find("amq.failover") > 0
+
+ def evaluate_address(self, session, address):
+ """Create a receiver just to evaluate an address for its side effects"""
+ r = session.receiver(address)
+ r.close()
+
+ def test_expire_fanout(self):
+ """Regression test for QPID-2874: Clustered broker crashes in assertion in
+ cluster/ExpiryPolicy.cpp.
+ Caused by a fan-out message being updated as separate messages"""
+ cluster = self.cluster(1)
+ session0 = cluster[0].connect().session()
+ # Create 2 queues bound to fanout exchange.
+ self.evaluate_address(session0, "q1;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:q1}]}}")
+ self.evaluate_address(session0, "q2;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:q2}]}}")
+ queues = ["q1", "q2"]
+ # Send a fanout message with a long timeout
+ s = session0.sender("amq.fanout")
+ s.send(Message("foo", ttl=100), sync=False)
+ # Start a new member, check the messages
+ cluster.start()
+ session1 = cluster[1].connect().session()
+ for q in queues: self.assert_browse(session1, "q1", ["foo"])
+
+ def test_route_update(self):
+ """Regression test for https://issues.apache.org/jira/browse/QPID-2982
+ Links and bridges associated with routes were not replicated on update.
+ This meant extra management objects and caused an exit if a management
+ client was attached.
+ """
+ args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
+ cluster0 = self.cluster(1, args=args)
+ cluster1 = self.cluster(1, args=args)
+ assert 0 == subprocess.call(
+ ["qpid-route", "route", "add", cluster0[0].host_port(),
+ cluster1[0].host_port(), "dummy-exchange", "dummy-key", "-d"])
+ cluster0.start()
+
+ # Wait for qpid-tool:list on cluster0[0] to generate expected output.
+ pattern = re.compile("org.apache.qpid.broker.*link")
+ qpid_tool = subprocess.Popen(["qpid-tool", cluster0[0].host_port()],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ class Scanner(Thread):
+ def __init__(self): self.found = False; Thread.__init__(self)
+ def run(self):
+ for l in qpid_tool.stdout:
+ if pattern.search(l): self.found = True; return
+ scanner = Scanner()
+ scanner.start()
+ start = time.time()
+ try:
+ # Wait up to 5 second timeout for scanner to find expected output
+ while not scanner.found and time.time() < start + 5:
+ qpid_tool.stdin.write("list\n") # Ask qpid-tool to list
+ for b in cluster0: b.ready() # Raise if any brokers are down
+ finally:
+ qpid_tool.stdin.write("quit\n")
+ qpid_tool.wait()
+ scanner.join()
+ assert scanner.found
+ # Regression test for https://issues.apache.org/jira/browse/QPID-3235
+ # Inconsistent stats when changing elder.
+
+ # Force a change of elder
+ cluster0.start()
+ cluster0[0].kill()
+ time.sleep(2) # Allow a management interval to pass.
+ # Verify logs are consistent
+ cluster_test_logs.verify_logs()
+
+ def test_redelivered(self):
+ """Verify that redelivered flag is set correctly on replayed messages"""
+ cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
+ url = "amqp:tcp:%s,tcp:%s" % (cluster[0].host_port(), cluster[1].host_port())
+ queue = "my-queue"
+ cluster[0].declare_queue(queue)
+ self.sender = self.popen(
+ ["qpid-send",
+ "--broker", url,
+ "--address", queue,
+ "--sequence=true",
+ "--send-eos=1",
+ "--messages=100000",
+ "--connection-options={reconnect:true}"
+ ])
+ self.receiver = self.popen(
+ ["qpid-receive",
+ "--broker", url,
+ "--address", queue,
+ "--ignore-duplicates",
+ "--check-redelivered",
+ "--connection-options={reconnect:true}",
+ "--forever"
+ ])
+ time.sleep(1)#give sender enough time to have some messages to replay
+ cluster[0].kill()
+ self.sender.wait()
+ self.receiver.wait()
+ cluster[1].kill()
+
+ class BlockedSend(Thread):
+ """Send a message, send is expected to block.
+ Verify that it does block (for a given timeout), then allow
+ waiting till it unblocks when it is expected to do so."""
+ def __init__(self, sender, msg):
+ self.sender, self.msg = sender, msg
+ self.blocked = True
+ self.condition = Condition()
+ self.timeout = 0.1 # Time to wait for expected results.
+ Thread.__init__(self)
+ def run(self):
+ try:
+ self.sender.send(self.msg, sync=True)
+ self.condition.acquire()
+ try:
+ self.blocked = False
+ self.condition.notify()
+ finally: self.condition.release()
+ except Exception,e: print "BlockedSend exception: %s"%e
+ def start(self):
+ Thread.start(self)
+ time.sleep(self.timeout)
+ assert self.blocked # Expected to block
+ def assert_blocked(self): assert self.blocked
+ def wait(self): # Now expecting to unblock
+ self.condition.acquire()
+ try:
+ while self.blocked:
+ self.condition.wait(self.timeout)
+ if self.blocked: raise Exception("Timed out waiting for send to unblock")
+ finally: self.condition.release()
+ self.join()
+
+ def queue_flowlimit_test(self, brokers):
+ """Verify that the queue's flowlimit configuration and state are
+ correctly replicated.
+ The brokers argument allows this test to run on single broker,
+ cluster of 2 pre-startd brokers or cluster where second broker
+ starts after queue is in flow control.
+ """
+ # configure a queue with a specific flow limit on first broker
+ ssn0 = brokers.first().connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
+ brokers.first().startQmf()
+ q1 = [q for q in brokers.first().qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ oid = q1.getObjectId()
+ self.assertEqual(q1.name, "flq")
+ self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert not q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 0)
+
+ # fill the queue on one broker until flow control is active
+ for x in range(5): s0.send(Message(str(x)))
+ sender = ShortTests.BlockedSend(s0, Message(str(6)))
+ sender.start() # Tests that sender does block
+ # Verify the broker queue goes into a flowStopped state
+ deadline = time.time() + 1
+ while not q1.flowStopped and time.time() < deadline: q1.update()
+ assert q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 1)
+ sender.assert_blocked() # Still blocked
+
+ # Now verify the both brokers in cluster have same configuration
+ brokers.second().startQmf()
+ qs = brokers.second().qmf_session.getObjects(_objectId=oid)
+ self.assertEqual(len(qs), 1)
+ q2 = qs[0]
+ self.assertEqual(q2.name, "flq")
+ self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert q2.flowStopped
+ self.assertEqual(q2.flowStoppedCount, 1)
+
+ # now drain the queue using a session to the other broker
+ ssn1 = brokers.second().connect().session()
+ r1 = ssn1.receiver("flq", capacity=6)
+ for x in range(4):
+ r1.fetch(timeout=0)
+ ssn1.acknowledge()
+ sender.wait() # Verify no longer blocked.
+
+ # and re-verify state of queue on both brokers
+ q1.update()
+ assert not q1.flowStopped
+ q2.update()
+ assert not q2.flowStopped
+
+ ssn0.connection.close()
+ ssn1.connection.close()
+ cluster_test_logs.verify_logs()
+
+ def test_queue_flowlimit(self):
+ """Test flow limits on a standalone broker"""
+ broker = self.broker()
+ class Brokers:
+ def first(self): return broker
+ def second(self): return broker
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_cluster(self):
+ cluster = self.cluster(2)
+ class Brokers:
+ def first(self): return cluster[0]
+ def second(self): return cluster[1]
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_cluster_join(self):
+ cluster = self.cluster(1)
+ class Brokers:
+ def first(self): return cluster[0]
+ def second(self):
+ if len(cluster) == 1: cluster.start()
+ return cluster[1]
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_replicate(self):
+ """ Verify that a queue which is in flow control BUT has drained BELOW
+ the flow control 'stop' threshold, is correctly replicated when a new
+ broker is added to the cluster.
+ """
+
+ class AsyncSender(Thread):
+ """Send a fixed number of msgs from a sender in a separate thread
+ so it may block without blocking the test.
+ """
+ def __init__(self, broker, address, count=1, size=4):
+ Thread.__init__(self)
+ self.daemon = True
+ self.broker = broker
+ self.queue = address
+ self.count = count
+ self.size = size
+ self.done = False
+
+ def run(self):
+ self.sender = subprocess.Popen(["qpid-send",
+ "--capacity=1",
+ "--content-size=%s" % self.size,
+ "--messages=%s" % self.count,
+ "--failover-updates",
+ "--connection-options={reconnect:true}",
+ "--address=%s" % self.queue,
+ "--broker=%s" % self.broker.host_port()])
+ self.sender.wait()
+ self.done = True
+
+ cluster = self.cluster(2)
+ # create a queue with rather draconian flow control settings
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':100, 'qpid.flow_resume_count':20}}}}")
+
+ # fire off the sending thread to broker[0], and wait until the queue
+ # hits flow control on broker[1]
+ sender = AsyncSender(cluster[0], "flq", count=110);
+ sender.start();
+
+ cluster[1].startQmf()
+ q_obj = [q for q in cluster[1].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ deadline = time.time() + 10
+ while not q_obj.flowStopped and time.time() < deadline:
+ q_obj.update()
+ assert q_obj.flowStopped
+ assert not sender.done
+ assert q_obj.msgDepth < 110
+
+ # Now drain enough messages on broker[1] to drop below the flow stop
+ # threshold, but not relieve flow control...
+ receiver = subprocess.Popen(["qpid-receive",
+ "--messages=15",
+ "--timeout=1",
+ "--print-content=no",
+ "--failover-updates",
+ "--connection-options={reconnect:true}",
+ "--ack-frequency=1",
+ "--address=flq",
+ "--broker=%s" % cluster[1].host_port()])
+ receiver.wait()
+ q_obj.update()
+ assert q_obj.flowStopped
+ assert not sender.done
+ current_depth = q_obj.msgDepth
+
+ # add a new broker to the cluster, and verify that the queue is in flow
+ # control on that broker
+ cluster.start()
+ cluster[2].startQmf()
+ q_obj = [q for q in cluster[2].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ assert q_obj.flowStopped
+ assert q_obj.msgDepth == current_depth
+
+ # now drain the queue on broker[2], and verify that the sender becomes
+ # unblocked
+ receiver = subprocess.Popen(["qpid-receive",
+ "--messages=95",
+ "--timeout=1",
+ "--print-content=no",
+ "--failover-updates",
+ "--connection-options={reconnect:true}",
+ "--ack-frequency=1",
+ "--address=flq",
+ "--broker=%s" % cluster[2].host_port()])
+ receiver.wait()
+ q_obj.update()
+ assert not q_obj.flowStopped
+ assert q_obj.msgDepth == 0
+
+ # verify that the sender has become unblocked
+ sender.join(timeout=5)
+ assert not sender.isAlive()
+ assert sender.done
+
+ def test_blocked_queue_delete(self):
+ """Verify that producers which are blocked on a queue due to flow
+ control are unblocked when that queue is deleted.
+ """
+
+ cluster = self.cluster(2)
+ cluster[0].startQmf()
+ cluster[1].startQmf()
+
+ # configure a queue with a specific flow limit on first broker
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
+ q1 = [q for q in cluster[0].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ oid = q1.getObjectId()
+ self.assertEqual(q1.name, "flq")
+ self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert not q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 0)
+
+ # fill the queue on one broker until flow control is active
+ for x in range(5): s0.send(Message(str(x)))
+ sender = ShortTests.BlockedSend(s0, Message(str(6)))
+ sender.start() # Tests that sender does block
+ # Verify the broker queue goes into a flowStopped state
+ deadline = time.time() + 1
+ while not q1.flowStopped and time.time() < deadline: q1.update()
+ assert q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 1)
+ sender.assert_blocked() # Still blocked
+
+ # Now verify the both brokers in cluster have same configuration
+ qs = cluster[1].qmf_session.getObjects(_objectId=oid)
+ self.assertEqual(len(qs), 1)
+ q2 = qs[0]
+ self.assertEqual(q2.name, "flq")
+ self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert q2.flowStopped
+ self.assertEqual(q2.flowStoppedCount, 1)
+
+ # now delete the blocked queue from other broker
+ ssn1 = cluster[1].connect().session()
+ self.evaluate_address(ssn1, "flq;{delete:always}")
+ sender.wait() # Verify no longer blocked.
+
+ ssn0.connection.close()
+ ssn1.connection.close()
+ cluster_test_logs.verify_logs()
+
+
+ def test_alternate_exchange_update(self):
+ """Verify that alternate-exchange on exchanges and queues is propagated to new members of a cluster. """
+ cluster = self.cluster(1)
+ s0 = cluster[0].connect().session()
+ # create alt queue bound to amq.fanout exchange, will be destination for alternate exchanges
+ self.evaluate_address(s0, "alt;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:alt}]}}")
+ # create direct exchange ex with alternate-exchange amq.fanout and no queues bound
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'amq.fanout'}}}")
+ # create queue q with alternate-exchange amq.fanout
+ self.evaluate_address(s0, "q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'amq.fanout'}}}")
+
+ def verify(broker):
+ s = broker.connect().session()
+ # Verify unmatched message goes to ex's alternate.
+ s.sender("ex").send("foo")
+ self.assertEqual("foo", s.receiver("alt").fetch(timeout=0).content)
+ # Verify rejected message goes to q's alternate.
+ s.sender("q").send("bar")
+ msg = s.receiver("q").fetch(timeout=0)
+ self.assertEqual("bar", msg.content)
+ s.acknowledge(msg, Disposition(REJECTED)) # Reject the message
+ self.assertEqual("bar", s.receiver("alt").fetch(timeout=0).content)
+
+ verify(cluster[0])
+ cluster.start()
+ verify(cluster[1])
+
+ def test_binding_order(self):
+ """Regression test for binding order inconsistency in cluster"""
+ cluster = self.cluster(1)
+ c0 = cluster[0].connect()
+ s0 = c0.session()
+ # Declare multiple queues bound to same key on amq.topic
+ def declare(q,max=0):
+ if max: declare = 'x-declare:{arguments:{"qpid.max_count":%d, "qpid.flow_stop_count":0}}'%max
+ else: declare = 'x-declare:{}'
+ bind='x-bindings:[{queue:%s,key:key,exchange:"amq.topic"}]'%(q)
+ s0.sender("%s;{create:always,node:{%s,%s}}" % (q,declare,bind))
+ declare('d',max=4) # Only one with a limit
+ for q in ['c', 'b','a']: declare(q)
+ # Add a cluster member, send enough messages to exceed the max count
+ cluster.start()
+ try:
+ s = s0.sender('amq.topic/key')
+ for m in xrange(1,6): s.send(Message(str(m)))
+ self.fail("Expected capacity exceeded exception")
+ except messaging.exceptions.TargetCapacityExceeded: pass
+ c1 = cluster[1].connect()
+ s1 = c1.session()
+ s0 = c0.session() # Old session s0 is broken by exception.
+ # Verify queue contents are consistent.
+ for q in ['a','b','c','d']:
+ self.assertEqual(self.browse(s0, q), self.browse(s1, q))
+ # Verify queue contents are "best effort"
+ for q in ['a','b','c']: self.assert_browse(s1,q,[str(n) for n in xrange(1,6)])
+ self.assert_browse(s1,'d',[str(n) for n in xrange(1,5)])
+
+ def test_deleted_exchange(self):
+ """QPID-3215: cached exchange reference can cause cluster inconsistencies
+ if exchange is deleted/recreated
+ Verify stand-alone case
+ """
+ cluster = self.cluster()
+ # Verify we do not route message via an exchange that has been destroyed.
+ cluster.start()
+ s0 = cluster[0].connect().session()
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
+ self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
+ send0 = s0.sender("ex/foo")
+ send0.send("foo")
+ self.assert_browse(s0, "q", ["foo"])
+ self.evaluate_address(s0, "ex;{delete:always}")
+ try:
+ send0.send("bar") # Should fail, exchange is deleted.
+ self.fail("Expected not-found exception")
+ except qpid.messaging.NotFound: pass
+ self.assert_browse(cluster[0].connect().session(), "q", ["foo"])
+
+ def test_deleted_exchange_inconsistent(self):
+ """QPID-3215: cached exchange reference can cause cluster inconsistencies
+ if exchange is deleted/recreated
+
+ Verify cluster inconsistency.
+ """
+ cluster = self.cluster()
+ cluster.start()
+ s0 = cluster[0].connect().session()
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
+ self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
+ send0 = s0.sender("ex/foo")
+ send0.send("foo")
+ self.assert_browse(s0, "q", ["foo"])
+
+ cluster.start()
+ s1 = cluster[1].connect().session()
+ self.evaluate_address(s0, "ex;{delete:always}")
+ try:
+ send0.send("bar")
+ self.fail("Expected not-found exception")
+ except qpid.messaging.NotFound: pass
+
+ self.assert_browse(s1, "q", ["foo"])
+
+
+class LongTests(BrokerTest):
+ """Tests that can run for a long time if -DDURATION=<minutes> is set"""
+ def duration(self):
+ d = self.config.defines.get("DURATION")
+ if d: return float(d)*60
+ else: return 3 # Default is to be quick
+
+ def test_failover(self):
+ """Test fail-over during continuous send-receive with errors"""
+
+ # Original cluster will all be killed so expect exit with failure
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
+ for b in cluster: ErrorGenerator(b)
+
+ # Start sender and receiver threads
+ cluster[0].declare_queue("test-queue")
+ sender = NumberedSender(cluster[1], 1000) # Max queue depth
+ receiver = NumberedReceiver(cluster[2], sender)
+ receiver.start()
+ sender.start()
+
+ # Kill original brokers, start new ones for the duration.
+ endtime = time.time() + self.duration()
+ i = 0
+ while time.time() < endtime:
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ ErrorGenerator(b)
+ time.sleep(5)
+ sender.stop()
+ receiver.stop()
+ for i in range(i, len(cluster)): cluster[i].kill()
+
+ def test_management(self, args=[]):
+ """
+ Stress test: Run management clients and other clients concurrently
+ while killing and restarting brokers.
+ """
+
+ class ClientLoop(StoppableThread):
+ """Run a client executable in a loop."""
+ def __init__(self, broker, cmd):
+ StoppableThread.__init__(self)
+ self.broker=broker
+ self.cmd = cmd # Client command.
+ self.lock = Lock()
+ self.process = None # Client process.
+ self.start()
+
+ def run(self):
+ try:
+ while True:
+ self.lock.acquire()
+ try:
+ if self.stopped: break
+ self.process = self.broker.test.popen(
+ self.cmd, expect=EXPECT_UNKNOWN)
+ finally:
+ self.lock.release()
+ try:
+ exit = self.process.wait()
+ except OSError, e:
+ # Process may already have been killed by self.stop()
+ break
+ except Exception, e:
+ self.process.unexpected(
+ "client of %s: %s"%(self.broker.name, e))
+ self.lock.acquire()
+ try:
+ if self.stopped: break
+ if exit != 0:
+ self.process.unexpected(
+ "client of %s exit code %s"%(self.broker.name, exit))
+ finally:
+ self.lock.release()
+ except Exception, e:
+ self.error = RethrownException("Error in ClientLoop.run")
+
+ def stop(self):
+ """Stop the running client and wait for it to exit"""
+ self.lock.acquire()
+ try:
+ if self.stopped: return
+ self.stopped = True
+ if self.process:
+ try: self.process.kill() # Kill the client.
+ except OSError: pass # The client might not be running.
+ finally: self.lock.release()
+ StoppableThread.stop(self)
+
+ # body of test_management()
+
+ args += ["--mgmt-pub-interval", 1]
+ args += ["--log-enable=trace+:management"]
+ # Use store if present.
+ if BrokerTest.store_lib: args +=["--load-module", BrokerTest.store_lib]
+ cluster = self.cluster(3, args)
+
+ clients = [] # Per-broker list of clients that only connect to one broker.
+ mclients = [] # Management clients that connect to every broker in the cluster.
+
+ def start_clients(broker):
+ """Start ordinary clients for a broker."""
+ cmds=[
+ ["qpid-tool", "localhost:%s"%(broker.port())],
+ ["qpid-perftest", "--count=5000", "--durable=yes",
+ "--base-name", str(qpid.datatypes.uuid4()), "--port", broker.port()],
+ ["qpid-txtest", "--queue-base-name", "tx-%s"%str(qpid.datatypes.uuid4()),
+ "--port", broker.port()],
+ ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())],
+ ["testagent", "localhost", str(broker.port())] ]
+ clients.append([ClientLoop(broker, cmd) for cmd in cmds])
+
+ def start_mclients(broker):
+ """Start management clients that make multiple connections."""
+ cmd = ["qpid-stat", "-b", "localhost:%s" %(broker.port())]
+ mclients.append(ClientLoop(broker, cmd))
+
+ endtime = time.time() + self.duration()
+ # For long duration, first run is a quarter of the duration.
+ runtime = max(5, self.duration() / 4.0)
+ alive = 0 # First live cluster member
+ for i in range(len(cluster)): start_clients(cluster[i])
+ start_mclients(cluster[alive])
+
+ while time.time() < endtime:
+ time.sleep(runtime)
+ runtime = 5 # Remaining runs 5 seconds, frequent broker kills
+ for b in cluster[alive:]: b.ready() # Check if a broker crashed.
+ # Kill the first broker, expect the clients to fail.
+ b = cluster[alive]
+ b.expect = EXPECT_EXIT_FAIL
+ b.kill()
+ # Stop the brokers clients and all the mclients.
+ for c in clients[alive] + mclients:
+ try: c.stop()
+ except: pass # Ignore expected errors due to broker shutdown.
+ clients[alive] = []
+ mclients = []
+ # Start another broker and clients
+ alive += 1
+ cluster.start()
+ start_clients(cluster[-1])
+ start_mclients(cluster[alive])
+ for c in chain(mclients, *clients):
+ c.stop()
+ # Verify that logs are consistent
+ cluster_test_logs.verify_logs()
+
+ def test_management_qmf2(self):
+ self.test_management(args=["--mgmt-qmf2=yes"])
+
+ def test_connect_consistent(self):
+ args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
+ cluster = self.cluster(2, args=args)
+ end = time.time() + self.duration()
+ while (time.time() < end): # Get a management interval
+ for i in xrange(1000): cluster[0].connect().close()
+ cluster_test_logs.verify_logs()
+
+ def test_flowlimit_failover(self):
+ """Test fail-over during continuous send-receive with flow control
+ active.
+ """
+
+ # Original cluster will all be killed so expect exit with failure
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
+ #for b in cluster: ErrorGenerator(b)
+
+ # create a queue with rather draconian flow control settings
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("test-queue; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':2000, 'qpid.flow_resume_count':100}}}}")
+
+ receiver = NumberedReceiver(cluster[2])
+ receiver.start()
+ senders = [NumberedSender(cluster[i]) for i in range(1,3)]
+ for s in senders:
+ s.start()
+
+ # Kill original brokers, start new ones for the duration.
+ endtime = time.time() + self.duration();
+ i = 0
+ while time.time() < endtime:
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ #ErrorGenerator(b)
+ time.sleep(5)
+ #b = cluster[0]
+ #b.startQmf()
+ for s in senders:
+ s.stop()
+ receiver.stop()
+ for i in range(i, len(cluster)): cluster[i].kill()
+
+
+class StoreTests(BrokerTest):
+ """
+ Cluster tests that can only be run if there is a store available.
+ """
+ def args(self):
+ assert BrokerTest.store_lib
+ return ["--load-module", BrokerTest.store_lib]
+
+ def test_store_loaded(self):
+ """Ensure we are indeed loading a working store"""
+ broker = self.broker(self.args(), name="recoverme", expect=EXPECT_EXIT_FAIL)
+ m = Message("x", durable=True)
+ broker.send_message("q", m)
+ broker.kill()
+ broker = self.broker(self.args(), name="recoverme")
+ self.assertEqual("x", broker.get_message("q").content)
+
+ def test_kill_restart(self):
+ """Verify we can kill/resetart a broker with store in a cluster"""
+ cluster = self.cluster(1, self.args())
+ cluster.start("restartme", expect=EXPECT_EXIT_FAIL).kill()
+
+ # Send a message, retrieve from the restarted broker
+ cluster[0].send_message("q", "x")
+ m = cluster.start("restartme").get_message("q")
+ self.assertEqual("x", m.content)
+
+ def stop_cluster(self,broker):
+ """Clean shut-down of a cluster"""
+ self.assertEqual(0, qpid_cluster.main(
+ ["-kf", broker.host_port()]))
+
+ def test_persistent_restart(self):
+ """Verify persistent cluster shutdown/restart scenarios"""
+ cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_FAIL, wait=True)
+ a.send_message("q", Message("1", durable=True))
+ # Kill & restart one member.
+ c.kill()
+ self.assertEqual(a.get_message("q").content, "1")
+ a.send_message("q", Message("2", durable=True))
+ c = cluster.start("c", expect=EXPECT_EXIT_OK)
+ self.assertEqual(c.get_message("q").content, "2")
+ # Shut down the entire cluster cleanly and bring it back up
+ a.send_message("q", Message("3", durable=True))
+ self.stop_cluster(a)
+ a = cluster.start("a", wait=False)
+ b = cluster.start("b", wait=False)
+ c = cluster.start("c", wait=True)
+ self.assertEqual(a.get_message("q").content, "3")
+
+ def test_persistent_partial_failure(self):
+ # Kill 2 members, shut down the last cleanly then restart
+ # Ensure we use the clean database
+ cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=True)
+ a.send_message("q", Message("4", durable=True))
+ a.kill()
+ b.kill()
+ self.assertEqual(c.get_message("q").content, "4")
+ c.send_message("q", Message("clean", durable=True))
+ self.stop_cluster(c)
+ a = cluster.start("a", wait=False)
+ b = cluster.start("b", wait=False)
+ c = cluster.start("c", wait=True)
+ self.assertEqual(a.get_message("q").content, "clean")
+
+ def test_wrong_cluster_id(self):
+ # Start a cluster1 broker, then try to restart in cluster2
+ cluster1 = self.cluster(0, args=self.args())
+ a = cluster1.start("a", expect=EXPECT_EXIT_OK)
+ a.terminate()
+ cluster2 = self.cluster(1, args=self.args())
+ try:
+ a = cluster2.start("a", expect=EXPECT_EXIT_FAIL)
+ a.ready()
+ self.fail("Expected exception")
+ except: pass
+
+ def test_wrong_shutdown_id(self):
+ # Start 2 members and shut down.
+ cluster = self.cluster(0, args=self.args()+["--cluster-size=2"])
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
+ self.stop_cluster(a)
+ self.assertEqual(a.wait(), 0)
+ self.assertEqual(b.wait(), 0)
+
+ # Restart with a different member and shut down.
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=False)
+ self.stop_cluster(a)
+ self.assertEqual(a.wait(), 0)
+ self.assertEqual(c.wait(), 0)
+ # Mix members from both shutdown events, they should fail
+ # TODO aconway 2010-03-11: can't predict the exit status of these
+ # as it depends on the order of delivery of initial-status messages.
+ # See comment at top of this file.
+ a = cluster.start("a", expect=EXPECT_UNKNOWN, wait=False)
+ b = cluster.start("b", expect=EXPECT_UNKNOWN, wait=False)
+ self.assertRaises(Exception, lambda: a.ready())
+ self.assertRaises(Exception, lambda: b.ready())
+
+ def test_solo_store_clean(self):
+ # A single node cluster should always leave a clean store.
+ cluster = self.cluster(0, self.args())
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL)
+ a.send_message("q", Message("x", durable=True))
+ a.kill()
+ a = cluster.start("a")
+ self.assertEqual(a.get_message("q").content, "x")
+
+ def test_last_store_clean(self):
+ # Verify that only the last node in a cluster to shut down has
+ # a clean store. Start with cluster of 3, reduce to 1 then
+ # increase again to ensure that a node that was once alone but
+ # finally did not finish as the last node does not get a clean
+ # store.
+ cluster = self.cluster(0, self.args())
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL)
+ self.assertEqual(a.store_state(), "clean")
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL)
+ c = cluster.start("c", expect=EXPECT_EXIT_FAIL)
+ self.assertEqual(b.store_state(), "dirty")
+ self.assertEqual(c.store_state(), "dirty")
+ retry(lambda: a.store_state() == "dirty")
+
+ a.send_message("q", Message("x", durable=True))
+ a.kill()
+ b.kill() # c is last man, will mark store clean
+ retry(lambda: c.store_state() == "clean")
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL) # c no longer last man
+ retry(lambda: c.store_state() == "dirty")
+ c.kill() # a is now last man
+ retry(lambda: a.store_state() == "clean")
+ a.kill()
+ self.assertEqual(a.store_state(), "clean")
+ self.assertEqual(b.store_state(), "dirty")
+ self.assertEqual(c.store_state(), "dirty")
+
+ def test_restart_clean(self):
+ """Verify that we can re-start brokers one by one in a
+ persistent cluster after a clean oshutdown"""
+ cluster = self.cluster(0, self.args())
+ a = cluster.start("a", expect=EXPECT_EXIT_OK)
+ b = cluster.start("b", expect=EXPECT_EXIT_OK)
+ c = cluster.start("c", expect=EXPECT_EXIT_OK)
+ a.send_message("q", Message("x", durable=True))
+ self.stop_cluster(a)
+ a = cluster.start("a")
+ b = cluster.start("b")
+ c = cluster.start("c")
+ self.assertEqual(c.get_message("q").content, "x")
+
+ def test_join_sub_size(self):
+ """Verify that after starting a cluster with cluster-size=N,
+ we can join new members even if size < N-1"""
+ cluster = self.cluster(0, self.args()+["--cluster-size=3"])
+ a = cluster.start("a", wait=False, expect=EXPECT_EXIT_FAIL)
+ b = cluster.start("b", wait=False, expect=EXPECT_EXIT_FAIL)
+ c = cluster.start("c")
+ a.send_message("q", Message("x", durable=True))
+ a.send_message("q", Message("y", durable=True))
+ a.kill()
+ b.kill()
+ a = cluster.start("a")
+ self.assertEqual(c.get_message("q").content, "x")
+ b = cluster.start("b")
+ self.assertEqual(c.get_message("q").content, "y")
diff --git a/qpid/cpp/src/tests/clustered_replication_test b/qpid/cpp/src/tests/clustered_replication_test
new file mode 100755
index 0000000000..d6c72d9d1b
--- /dev/null
+++ b/qpid/cpp/src/tests/clustered_replication_test
@@ -0,0 +1,110 @@
+#!/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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+source ./test_env.sh
+
+trap stop_brokers INT EXIT
+
+fail() {
+ echo $1
+ exit 1
+}
+
+stop_brokers() {
+ if [[ $PRIMARY1 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $PRIMARY1
+ unset PRIMARY1
+ fi
+ if [[ $PRIMARY2 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $PRIMARY2
+ unset PRIMARY2
+ fi
+ if [[ $DR1 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $DR1
+ unset DR1
+ fi
+ if [[ $DR2 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $DR2
+ unset DR2
+ fi
+}
+
+if test -d $PYTHON_DIR; then
+ . $srcdir/ais_check
+
+ #todo: these cluster names need to be unique to prevent clashes
+ PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$
+ DR_CLUSTER=DR_$(hostname)_$$
+
+ GENERAL_OPTS="--auth no --no-module-dir --no-data-dir --daemon --port 0 --log-to-stderr false"
+ PRIMARY_OPTS="--load-module $REPLICATING_LISTENER_LIB --create-replication-queue true --replication-queue REPLICATION_QUEUE --load-module $CLUSTER_LIB --cluster-name $PRIMARY_CLUSTER"
+ DR_OPTS="--load-module $REPLICATION_EXCHANGE_LIB --load-module $CLUSTER_LIB --cluster-name $DR_CLUSTER"
+
+ rm -f repl*.tmp #cleanup any files left from previous run
+
+ #start first node of primary cluster and set up test queue
+ echo Starting primary cluster
+ PRIMARY1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.1.tmp) || fail "Could not start PRIMARY1"
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue test-queue --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue control-queue --generate-queue-events 1
+
+ #send 10 messages, consume 5 of them
+ for i in `seq 1 10`; do echo Message$i; done | ./sender --port $PRIMARY1
+ ./receiver --port $PRIMARY1 --messages 5 > /dev/null
+
+ #add new node to primary cluster, testing correct transfer of state:
+ echo Adding node to primary cluster
+ PRIMARY2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.2.tmp) || fail "Could not start PRIMARY2 "
+
+ #start DR cluster, set up test queue there and establish replication bridge
+ echo Starting DR cluster
+ DR1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.1.tmp) || fail "Could not start DR1"
+ DR2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.2.tmp) || fail "Could not start DR2"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue control-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add exchange replication REPLICATION_EXCHANGE
+ $PYTHON_COMMANDS/qpid-route queue add localhost:$DR2 localhost:$PRIMARY2 REPLICATION_EXCHANGE REPLICATION_QUEUE || fail "Could not add route."
+
+ #send more messages to primary
+ for i in `seq 11 20`; do echo Message$i; done | ./sender --port $PRIMARY1 --send-eos 1
+
+ #wait for replication events to all be processed:
+ echo Waiting for replication to complete
+ echo Done | ./sender --port $PRIMARY1 --routing-key control-queue --send-eos 1
+ ./receiver --queue control-queue --port $DR1 > /dev/null
+
+ #verify contents of test queue on dr cluster:
+ echo Verifying...
+ ./receiver --port $DR2 > repl.out.tmp
+ for i in `seq 6 20`; do echo Message$i; done | diff repl.out.tmp - || FAIL=1
+
+ if [[ $FAIL ]]; then
+ echo Clustered replication test failed: expectations not met!
+ exit 1
+ else
+ echo Clustered replication test passed
+ rm -f repl*.tmp
+ fi
+
+fi
diff --git a/qpid/cpp/src/tests/config.null b/qpid/cpp/src/tests/config.null
new file mode 100644
index 0000000000..565c7da435
--- /dev/null
+++ b/qpid/cpp/src/tests/config.null
@@ -0,0 +1 @@
+# empty config
diff --git a/qpid/cpp/src/tests/consume.cpp b/qpid/cpp/src/tests/consume.cpp
new file mode 100644
index 0000000000..69110d151f
--- /dev/null
+++ b/qpid/cpp/src/tests/consume.cpp
@@ -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.
+ *
+ */
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+typedef vector<string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ uint count;
+ uint ack;
+ string queue;
+ bool declare;
+ bool summary;
+ bool print;
+ bool durable;
+
+ Args() : count(1000), ack(0), queue("publish-consume"),
+ declare(false), summary(false), print(false)
+ {
+ addOptions()
+ ("count", optValue(count, "N"), "number of messages to publish")
+ ("ack-frequency", optValue(ack, "N"), "ack every N messages (0 means use no-ack mode)")
+ ("queue", optValue(queue, "<queue name>"), "queue to consume from")
+ ("declare", optValue(declare), "declare the queue")
+ ("durable", optValue(durable), "declare the queue durable, use with declare")
+ ("print-data", optValue(print), "Print the recieved data at info level")
+ ("s,summary", optValue(summary), "Print undecorated rate.");
+ }
+};
+
+Args opts;
+
+struct Client
+{
+ Connection connection;
+ Session session;
+
+ Client()
+ {
+ opts.open(connection);
+ session = connection.newSession();
+ }
+
+ void consume()
+ {
+ if (opts.declare)
+ session.queueDeclare(arg::queue=opts.queue, arg::durable=opts.durable);
+ SubscriptionManager subs(session);
+ LocalQueue lq;
+ SubscriptionSettings settings;
+ settings.acceptMode = opts.ack > 0 ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl(opts.count, SubscriptionManager::UNLIMITED,false);
+ Subscription sub = subs.subscribe(lq, opts.queue, settings);
+ Message msg;
+ AbsTime begin=now();
+ for (size_t i = 0; i < opts.count; ++i) {
+ msg=lq.pop();
+ QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId());
+ if (opts.print) QPID_LOG(info, "Data: " << msg.getData());
+ }
+ if (opts.ack != 0)
+ sub.accept(sub.getUnaccepted()); // Cumulative ack for final batch.
+ AbsTime end=now();
+ double secs(double(Duration(begin,end))/TIME_SEC);
+ if (opts.summary) cout << opts.count/secs << endl;
+ else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl;
+ }
+
+ ~Client()
+ {
+ try{
+ session.close();
+ connection.close();
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Client client;
+ client.consume();
+ return 0;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/datagen.cpp b/qpid/cpp/src/tests/datagen.cpp
new file mode 100644
index 0000000000..acbc07d63c
--- /dev/null
+++ b/qpid/cpp/src/tests/datagen.cpp
@@ -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.
+ *
+ */
+
+#include <exception>
+#include <iostream>
+#include <stdlib.h>
+#include <time.h>
+#include "qpid/Options.h"
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ uint count;
+ uint minSize;
+ uint maxSize;
+ uint minChar;
+ uint maxChar;
+ bool help;
+
+ Args() : qpid::Options("Random data generator"),
+ count(1), minSize(8), maxSize(4096),
+ minChar(32), maxChar(126),//safely printable ascii chars
+ help(false)
+ {
+ addOptions()
+ ("count", qpid::optValue(count, "N"), "number of data strings to generate")
+ ("min-size", qpid::optValue(minSize, "N"), "minimum size of data string")
+ ("max-size", qpid::optValue(maxSize, "N"), "maximum size of data string")
+ ("min-char", qpid::optValue(minChar, "N"), "minimum char value used in data string")
+ ("max-char", qpid::optValue(maxChar, "N"), "maximum char value used in data string")
+ ("help", qpid::optValue(help), "print this usage statement");
+ }
+
+ bool parse(int argc, char** argv) {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (maxSize < minSize) throw qpid::Options::Exception("max-size must be greater than min-size");
+ if (maxChar < minChar) throw qpid::Options::Exception("max-char must be greater than min-char");
+
+ if (help) {
+ std::cerr << *this << std::endl << std::endl;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ }
+ return false;
+ }
+
+};
+
+uint random(uint min, uint max)
+{
+ return (rand() % (max-min+1)) + min;
+}
+
+std::string generateData(uint size, uint min, uint max)
+{
+ std::string data;
+ for (uint i = 0; i < size; i++) {
+ data += (char) random(min, max);
+ }
+ return data;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ if (opts.parse(argc, argv)) {
+ srand(time(0));
+ for (uint i = 0; i < opts.count; i++) {
+ std::cout << generateData(random(opts.minSize, opts.maxSize), opts.minChar, opts.maxChar) << std::endl;
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/declare_queues.cpp b/qpid/cpp/src/tests/declare_queues.cpp
new file mode 100644
index 0000000000..bf85b9c04b
--- /dev/null
+++ b/qpid/cpp/src/tests/declare_queues.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Exception.h>
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+
+using namespace qpid::client;
+
+using namespace std;
+
+int
+main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+ if ( argc != 6 )
+ {
+ cerr << "Usage: declare_queues host port durability queue_name_prefix n_queues\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int durability = atoi(argv[3]);
+ char const * queue_name_prefix = argv[4];
+ int n_queues = atoi(argv[5]);
+
+ FailoverManager connection(settings);
+
+ int max_fail = 13;
+ for ( int i = 0; i < n_queues; ++ i ) {
+ stringstream queue_name;
+ queue_name << queue_name_prefix << '_' << i;
+
+ bool queue_created = false;
+ int failure_count;
+
+ // Any non-transport failure is Bad.
+ try
+ {
+ while ( ! queue_created ) {
+ Session session = connection.connect().newSession();
+ // TransportFailures aren't too bad -- they might happen because
+ // we are doing a cluster failover test. But if we get too many,
+ // we will still bug out.
+ failure_count = 0;
+ try {
+ if ( durability )
+ session.queueDeclare(arg::queue=queue_name.str(), arg::durable=true);
+ else
+ session.queueDeclare(arg::queue=queue_name.str());
+ queue_created = true;
+ cout << "declare_queues: Created queue " << queue_name.str() << endl;
+ }
+ catch ( const qpid::TransportFailure& ) {
+ if ( ++ failure_count >= max_fail ) {
+ cerr << "declare_queues failed: too many transport errors.\n";
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ qpid::sys::sleep ( 1 );
+ }
+ }
+ }
+ catch ( const exception & error) {
+ cerr << "declare_queues failed:" << error.what() << endl;
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ }
+}
+
+
+
+
+
diff --git a/qpid/cpp/src/tests/dlclose_noop.c b/qpid/cpp/src/tests/dlclose_noop.c
new file mode 100644
index 0000000000..b78cf486d8
--- /dev/null
+++ b/qpid/cpp/src/tests/dlclose_noop.c
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Loaded via LD_PRELOAD this will turn dlclose into a no-op.
+ *
+ * Allows valgrind to generate useful reports from programs that
+ * dynamically unload libraries before exit, such as CppUnit's
+ * DllPlugInTester.
+ *
+ */
+
+#include <stdio.h>
+void* dlclose(void* handle) { return 0; }
+
diff --git a/qpid/cpp/src/tests/dynamic_log_level_test b/qpid/cpp/src/tests/dynamic_log_level_test
new file mode 100755
index 0000000000..990e56b1b1
--- /dev/null
+++ b/qpid/cpp/src/tests/dynamic_log_level_test
@@ -0,0 +1,57 @@
+#!/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.
+#
+
+# Run a simple test to verify dynamic log level changes
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
+
+LOG_FILE=log_test.log
+trap cleanup EXIT
+
+cleanup() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+error() {
+ echo $*;
+ exit 1;
+}
+
+rm -rf $LOG_FILE
+PORT=$($QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --log-to-file $LOG_FILE) || error "Could not start broker"
+
+echo Broker for log level test started on $PORT, pid is $($QPIDD_EXEC --no-module-dir --check --port $PORT)
+
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='notice+' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=1 body=HIDDEN > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='debug+:Broker' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=2 body=VISIBLE > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='notice+' > /dev/null
+
+#check log includes debug statement for last echo, but not the first
+if [[ $(grep echo $LOG_FILE | wc -l) -ne 1 ]]; then
+ cat $LOG_FILE
+ error "Log contents not as expected"
+else
+ rm -rf $LOG_FILE
+ echo OK
+fi
+
diff --git a/qpid/cpp/src/tests/echotest.cpp b/qpid/cpp/src/tests/echotest.cpp
new file mode 100644
index 0000000000..5114ab883d
--- /dev/null
+++ b/qpid/cpp/src/tests/echotest.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/client/Connection.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Options.h>
+
+#include <iostream>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options,
+ public qpid::client::ConnectionSettings
+{
+ bool help;
+ uint count;
+ uint size;
+ bool summary;
+
+ Args() : qpid::Options("Simple latency test optins"), help(false), count(20), size(0), summary()
+ {
+ using namespace qpid;
+ addOptions()
+ ("help", optValue(help), "Print this usage statement")
+ ("count", optValue(count, "N"), "Number of messages to send")
+ ("size", optValue(count, "N"), "Size of messages")
+ ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
+ ("port,p", optValue(port, "PORT"), "Broker port to connect to")
+ ("username", optValue(username, "USER"), "user name for broker log in.")
+ ("password", optValue(password, "PASSWORD"), "password for broker log in.")
+ ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("s,summary", optValue(summary), "Print only average latency.");
+ }
+};
+
+uint64_t current_time()
+{
+ Duration t(EPOCH, now());
+ return t;
+}
+
+class Listener : public MessageListener
+{
+ private:
+ Session session;
+ SubscriptionManager subscriptions;
+ uint counter;
+ const uint limit;
+ std::string queue;
+ Message request;
+ double total, min, max;
+ bool summary;
+
+ public:
+ Listener(Session& session, uint limit, bool summary);
+ void start(uint size);
+ void received(Message& message);
+};
+
+Listener::Listener(Session& s, uint l, bool summary_) :
+ session(s), subscriptions(s), counter(0), limit(l),
+ queue(session.getId().getName()), total(),
+ min(std::numeric_limits<double>::max()), max(), summary(summary_)
+{}
+
+void Listener::start(uint size)
+{
+ session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true);
+ request.getDeliveryProperties().setRoutingKey(queue);
+ subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE));
+
+ request.getDeliveryProperties().setTimestamp(current_time());
+ if (size) request.setData(std::string(size, 'X'));
+ async(session).messageTransfer(arg::content=request);
+ subscriptions.run();
+}
+
+void Listener::received(Message& response)
+{
+ //extract timestamp and compute latency:
+ uint64_t sentAt = response.getDeliveryProperties().getTimestamp();
+ uint64_t receivedAt = current_time();
+
+ double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC;
+ if (!summary) cout << "Latency: " << latency << "ms" << endl;
+ min = std::min(latency, min);
+ max = std::max(latency, max);
+ total += latency;
+
+ if (++counter < limit) {
+ request.getDeliveryProperties().setTimestamp(current_time());
+ async(session).messageTransfer(arg::content=request);
+ } else {
+ subscriptions.cancel(queue);
+ if (summary) cout << min << "\t" << max << "\t" << total/limit << endl;
+ else cout << "min: " << min << " max: " << max << " average: " << total/limit << endl;
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ opts.parse(argc, argv);
+
+ if (opts.help) {
+ std::cout << opts << std::endl;
+ return 0;
+ }
+
+ Connection connection;
+ try {
+ connection.open(opts);
+ Session session = connection.newSession();
+ Listener listener(session, opts.count, opts.summary);
+ listener.start(opts.size);
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/src/tests/exception_test.cpp b/qpid/cpp/src/tests/exception_test.cpp
new file mode 100644
index 0000000000..3536ffddbe
--- /dev/null
+++ b/qpid/cpp/src/tests/exception_test.cpp
@@ -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.
+ *
+ */
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/framing/reply_exceptions.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(exception_test)
+
+// FIXME aconway 2008-06-12: need to update our exception handling to
+// 0-10 handling and extend this test to provoke all the exceptional
+// conditions we know of and verify the correct exception is thrown.
+
+using namespace std;
+using namespace qpid;
+using namespace sys;
+using namespace client;
+using namespace framing;
+
+using qpid::broker::Broker;
+using boost::bind;
+using boost::function;
+
+template <class Ex>
+struct Catcher : public Runnable {
+ function<void ()> f;
+ bool caught;
+ Thread thread;
+
+ Catcher(function<void ()> f_) : f(f_), caught(false), thread(this) {}
+ ~Catcher() { join(); }
+
+ void run() {
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f();
+ }
+ catch(const Ex& e) {
+ caught=true;
+ BOOST_MESSAGE(string("Caught expected exception: ")+e.what()+"["+typeid(e).name()+"]");
+ }
+ catch(const std::exception& e) {
+ BOOST_ERROR(string("Bad exception: ")+e.what()+"["+typeid(e).name()+"] expected: "+typeid(Ex).name());
+ }
+ catch(...) {
+ BOOST_ERROR(string("Bad exception: unknown"));
+ }
+ }
+
+ bool join() {
+ if (thread) {
+ thread.join();
+ thread=Thread();
+ }
+ return caught;
+ }
+};
+
+QPID_AUTO_TEST_CASE(TestSessionBusy) {
+ SessionFixture f;
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.connection.newSession(f.session.getId().getName());
+ BOOST_FAIL("Expected SessionBusyException for " << f.session.getId().getName());
+ } catch (const SessionBusyException&) {} // FIXME aconway 2008-09-22: client is not throwing correct exception.
+}
+
+QPID_AUTO_TEST_CASE(DisconnectedPop) {
+ ProxySessionFixture fix;
+ ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
+ fix.session.queueDeclare(arg::queue="q");
+ fix.subs.subscribe(fix.lq, "q");
+ Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC));
+ fix.connection.proxy.close();
+ BOOST_CHECK(pop.join());
+}
+
+QPID_AUTO_TEST_CASE(DisconnectedListen) {
+ ProxySessionFixture fix;
+ struct NullListener : public MessageListener {
+ void received(Message&) { BOOST_FAIL("Unexpected message"); }
+ } l;
+ ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
+ fix.session.queueDeclare(arg::queue="q");
+ fix.subs.subscribe(l, "q");
+
+ Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs)));
+ fix.connection.proxy.close();
+ runner.join();
+ BOOST_CHECK_THROW(fix.session.queueDeclare(arg::queue="x"), TransportFailure);
+}
+
+QPID_AUTO_TEST_CASE(NoSuchQueueTest) {
+ ProxySessionFixture fix;
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ BOOST_CHECK_THROW(fix.subs.subscribe(fix.lq, "no such queue"), NotFoundException);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/failover_soak.cpp b/qpid/cpp/src/tests/failover_soak.cpp
new file mode 100644
index 0000000000..c2ac36a757
--- /dev/null
+++ b/qpid/cpp/src/tests/failover_soak.cpp
@@ -0,0 +1,827 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include <boost/assign.hpp>
+
+#include "qpid/framing/Uuid.h"
+
+#include <ForkedBroker.h>
+#include <qpid/client/Connection.h>
+
+
+
+
+
+using namespace std;
+using boost::assign::list_of;
+using namespace qpid::framing;
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+vector<pid_t> pids;
+
+typedef vector<ForkedBroker *> brokerVector;
+
+typedef enum
+{
+ NO_STATUS,
+ RUNNING,
+ COMPLETED
+}
+childStatus;
+
+
+typedef enum
+{
+ NO_TYPE,
+ DECLARING_CLIENT,
+ SENDING_CLIENT,
+ RECEIVING_CLIENT
+}
+childType;
+
+
+ostream& operator<< ( ostream& os, const childType& ct ) {
+ switch ( ct ) {
+ case DECLARING_CLIENT: os << "Declaring Client"; break;
+ case SENDING_CLIENT: os << "Sending Client"; break;
+ case RECEIVING_CLIENT: os << "Receiving Client"; break;
+ default: os << "No Client"; break;
+ }
+
+ return os;
+}
+
+
+
+
+struct child
+{
+ child ( string & name, pid_t pid, childType type )
+ : name(name), pid(pid), retval(-999), status(RUNNING), type(type)
+ {
+ gettimeofday ( & startTime, 0 );
+ }
+
+
+ void
+ done ( int _retval )
+ {
+ retval = _retval;
+ status = COMPLETED;
+ gettimeofday ( & stopTime, 0 );
+ }
+
+
+ void
+ setType ( childType t )
+ {
+ type = t;
+ }
+
+
+ string name;
+ pid_t pid;
+ int retval;
+ childStatus status;
+ childType type;
+ struct timeval startTime,
+ stopTime;
+};
+
+
+
+
+struct children : public vector<child *>
+{
+
+ void
+ add ( string & name, pid_t pid, childType type )
+ {
+ push_back ( new child ( name, pid, type ) );
+ }
+
+
+ child *
+ get ( pid_t pid )
+ {
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ if ( pid == (*i)->pid )
+ return *i;
+
+ return 0;
+ }
+
+
+ void
+ exited ( pid_t pid, int retval )
+ {
+ child * kid = get ( pid );
+ if(! kid)
+ {
+ if ( verbosity > 1 )
+ {
+ cerr << "children::exited warning: Can't find child with pid "
+ << pid
+ << endl;
+ }
+ return;
+ }
+
+ kid->done ( retval );
+ }
+
+
+ int
+ unfinished ( )
+ {
+ int count = 0;
+
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ if ( COMPLETED != (*i)->status )
+ ++ count;
+
+ return count;
+ }
+
+
+ int
+ checkChildren ( )
+ {
+ for ( unsigned int i = 0; i < pids.size(); ++ i )
+ {
+ int pid = pids[i];
+ int returned_pid;
+ int status;
+
+ child * kid = get ( pid );
+
+ if ( kid->status != COMPLETED )
+ {
+ returned_pid = waitpid ( pid, &status, WNOHANG );
+
+ if ( returned_pid == pid )
+ {
+ int exit_status = WEXITSTATUS(status);
+ exited ( pid, exit_status );
+ if ( exit_status ) // this is a child error.
+ return exit_status;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+
+ void
+ killEverybody ( )
+ {
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ kill ( (*i)->pid, 9 );
+ }
+
+
+
+ void
+ print ( )
+ {
+ cout << "--- status of all children --------------\n";
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ cout << "child: " << (*i)->name
+ << " status: " << (*i)->status
+ << endl;
+ cout << "\n\n\n\n";
+ }
+
+ int verbosity;
+};
+
+
+children allMyChildren;
+
+
+void
+childExit ( int )
+{
+ int childReturnCode;
+ pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG);
+
+ if ( pid > 0 )
+ allMyChildren.exited ( pid, childReturnCode );
+}
+
+
+
+int
+mrand ( int maxDesiredVal ) {
+ double zeroToOne = (double) rand() / (double) RAND_MAX;
+ return (int) (zeroToOne * (double) maxDesiredVal);
+}
+
+
+
+int
+mrand ( int minDesiredVal, int maxDesiredVal ) {
+ int interval = maxDesiredVal - minDesiredVal;
+ return minDesiredVal + mrand ( interval );
+}
+
+
+
+void
+makeClusterName ( string & s ) {
+ stringstream ss;
+ ss << "soakTestCluster_" << Uuid(true).str();
+ s = ss.str();
+}
+
+
+
+
+
+void
+printBrokers ( brokerVector & brokers )
+{
+ cout << "Broker List ------------ size: " << brokers.size() << "\n";
+ for ( brokerVector::iterator i = brokers.begin(); i != brokers.end(); ++ i) {
+ cout << "pid: "
+ << (*i)->getPID()
+ << " port: "
+ << (*i)->getPort()
+ << endl;
+ }
+ cout << "end Broker List ------------\n";
+}
+
+
+
+
+ForkedBroker * newbie = 0;
+int newbie_port = 0;
+
+
+
+bool
+wait_for_newbie ( )
+{
+ if ( ! newbie )
+ return true;
+
+ try
+ {
+ Connection connection;
+ connection.open ( "127.0.0.1", newbie_port );
+ connection.close();
+ newbie = 0; // He's no newbie anymore!
+ return true;
+ }
+ catch ( const std::exception& error )
+ {
+ std::cerr << "wait_for_newbie error: "
+ << error.what()
+ << endl;
+ return false;
+ }
+}
+
+bool endsWith(const char* str, const char* suffix) {
+ return (strlen(suffix) < strlen(str) && 0 == strcmp(str+strlen(str)-strlen(suffix), suffix));
+}
+
+
+void
+startNewBroker ( brokerVector & brokers,
+ char const * moduleOrDir,
+ string const clusterName,
+ int verbosity,
+ int durable )
+{
+ static int brokerId = 0;
+ stringstream path, prefix;
+ prefix << "soak-" << brokerId;
+ std::vector<std::string> argv = list_of<string>
+ ("qpidd")
+ ("--cluster-name")(clusterName)
+ ("--auth=no")
+ ("--mgmt-enable=no")
+ ("--log-prefix")(prefix.str())
+ ("--log-to-file")(prefix.str()+".log")
+ ("--log-enable=info+")
+ ("--log-enable=debug+:cluster")
+ ("TMP_DATA_DIR");
+
+ if (endsWith(moduleOrDir, "cluster.so")) {
+ // Module path specified, load only that module.
+ argv.push_back(string("--load-module=")+moduleOrDir);
+ argv.push_back("--no-module-dir");
+ if ( durable ) {
+ std::cerr << "failover_soak warning: durable arg hass no effect. Use \"dir\" option of \"moduleOrDir\".\n";
+ }
+ }
+ else {
+ // Module directory specified, load all modules in dir.
+ argv.push_back(string("--module-dir=")+moduleOrDir);
+ }
+
+ newbie = new ForkedBroker (argv);
+ newbie_port = newbie->getPort();
+ ForkedBroker * broker = newbie;
+
+ if ( verbosity > 0 )
+ std::cerr << "new broker created: pid == "
+ << broker->getPID()
+ << " log-prefix == "
+ << "soak-" << brokerId
+ << endl;
+ brokers.push_back ( broker );
+
+ ++ brokerId;
+}
+
+
+
+
+
+bool
+killFrontBroker ( brokerVector & brokers, int verbosity )
+{
+ cerr << "killFrontBroker: waiting for newbie sync...\n";
+ if ( ! wait_for_newbie() )
+ return false;
+ cerr << "killFrontBroker: newbie synced.\n";
+
+ if ( verbosity > 0 )
+ cout << "killFrontBroker pid: " << brokers[0]->getPID() << " on port " << brokers[0]->getPort() << endl;
+ try { brokers[0]->kill(9); }
+ catch ( const exception& error ) {
+ if ( verbosity > 0 )
+ {
+ cout << "error killing broker: "
+ << error.what()
+ << endl;
+ }
+
+ return false;
+ }
+ delete brokers[0];
+ brokers.erase ( brokers.begin() );
+ return true;
+}
+
+
+
+
+
+/*
+ * The optional delay is to avoid killing newbie brokers that have just
+ * been added and are still in the process of updating. This causes
+ * spurious, test-generated errors that scare everybody.
+ */
+void
+killAllBrokers ( brokerVector & brokers, int delay )
+{
+ if ( delay > 0 )
+ {
+ std::cerr << "Killing all brokers after delay of " << delay << endl;
+ sleep ( delay );
+ }
+
+ for ( uint i = 0; i < brokers.size(); ++ i )
+ try { brokers[i]->kill(9); }
+ catch ( const exception& error )
+ {
+ std::cerr << "killAllBrokers Warning: exception during kill on broker "
+ << i
+ << " "
+ << error.what()
+ << endl;
+ }
+}
+
+
+
+
+
+pid_t
+runDeclareQueuesClient ( brokerVector brokers,
+ char const * host,
+ char const * path,
+ int verbosity,
+ int durable,
+ char const * queue_prefix,
+ int n_queues
+ )
+{
+ string name("declareQueues");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1 )
+ cout << "startDeclareQueuesClient: host: "
+ << host
+ << " port: "
+ << port
+ << endl;
+ stringstream portSs;
+ portSs << port;
+
+ vector<const char*> argv;
+ argv.push_back ( "declareQueues" );
+ argv.push_back ( host );
+ string portStr = portSs.str();
+ argv.push_back ( portStr.c_str() );
+ if ( durable )
+ argv.push_back ( "1" );
+ else
+ argv.push_back ( "0" );
+
+ argv.push_back ( queue_prefix );
+
+ char n_queues_str[20];
+ sprintf ( n_queues_str, "%d", n_queues );
+ argv.push_back ( n_queues_str );
+
+ argv.push_back ( 0 );
+ pid_t pid = fork();
+
+ if ( ! pid ) {
+ execv ( path, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing declareQueues: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, DECLARING_CLIENT );
+ return pid;
+}
+
+
+
+
+
+pid_t
+startReceivingClient ( brokerVector brokers,
+ char const * host,
+ char const * receiverPath,
+ char const * reportFrequency,
+ int verbosity,
+ char const * queue_name
+ )
+{
+ string name("receiver");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1 )
+ cout << "startReceivingClient: port " << port << endl;
+
+ // verbosity has to be > 1 to let clients talk.
+ int client_verbosity = (verbosity > 1 ) ? 1 : 0;
+
+ char portStr[100];
+ char verbosityStr[100];
+ sprintf(portStr, "%d", port);
+ sprintf(verbosityStr, "%d", client_verbosity);
+
+
+ vector<const char*> argv;
+ argv.push_back ( "resumingReceiver" );
+ argv.push_back ( host );
+ argv.push_back ( portStr );
+ argv.push_back ( reportFrequency );
+ argv.push_back ( verbosityStr );
+ argv.push_back ( queue_name );
+ argv.push_back ( 0 );
+
+ pid_t pid = fork();
+ pids.push_back ( pid );
+
+ if ( ! pid ) {
+ execv ( receiverPath, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing receiver: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, RECEIVING_CLIENT );
+ return pid;
+}
+
+
+
+
+
+pid_t
+startSendingClient ( brokerVector brokers,
+ char const * host,
+ char const * senderPath,
+ char const * nMessages,
+ char const * reportFrequency,
+ int verbosity,
+ int durability,
+ char const * queue_name
+ )
+{
+ string name("sender");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1)
+ cout << "startSenderClient: port " << port << endl;
+ char portStr[100];
+ char verbosityStr[100];
+ //
+ // verbosity has to be > 1 to let clients talk.
+ int client_verbosity = (verbosity > 1 ) ? 1 : 0;
+
+ sprintf ( portStr, "%d", port);
+ sprintf ( verbosityStr, "%d", client_verbosity);
+
+ vector<const char*> argv;
+ argv.push_back ( "replayingSender" );
+ argv.push_back ( host );
+ argv.push_back ( portStr );
+ argv.push_back ( nMessages );
+ argv.push_back ( reportFrequency );
+ argv.push_back ( verbosityStr );
+ if ( durability )
+ argv.push_back ( "1" );
+ else
+ argv.push_back ( "0" );
+ argv.push_back ( queue_name );
+ argv.push_back ( 0 );
+
+ pid_t pid = fork();
+ pids.push_back ( pid );
+
+ if ( ! pid ) {
+ execv ( senderPath, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing sender: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, SENDING_CLIENT );
+ return pid;
+}
+
+
+
+#define HUNKY_DORY 0
+#define BAD_ARGS 1
+#define CANT_FORK_DQ 2
+#define CANT_FORK_RECEIVER 3
+#define CANT_FORK_SENDER 4
+#define DQ_FAILED 5
+#define ERROR_ON_CHILD 6
+#define HANGING 7
+#define ERROR_KILLING_BROKER 8
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+// If you want durability, use the "dir" option of "moduleOrDir" .
+int
+main ( int argc, char const ** argv )
+{
+ int brokerKills = 0;
+ if ( argc != 11 ) {
+ cerr << "Usage: "
+ << argv[0]
+ << "moduleOrDir declareQueuesPath senderPath receiverPath nMessages reportFrequency verbosity durable n_queues n_brokers"
+ << endl;
+ cerr << "\tverbosity is an integer, durable is 0 or 1\n";
+ return BAD_ARGS;
+ }
+ signal ( SIGCHLD, childExit );
+
+ int i = 1;
+ char const * moduleOrDir = argv[i++];
+ char const * declareQueuesPath = argv[i++];
+ char const * senderPath = argv[i++];
+ char const * receiverPath = argv[i++];
+ char const * nMessages = argv[i++];
+ char const * reportFrequency = argv[i++];
+ int verbosity = atoi(argv[i++]);
+ int durable = atoi(argv[i++]);
+ int n_queues = atoi(argv[i++]);
+ int n_brokers = atoi(argv[i++]);
+
+ char const * host = "127.0.0.1";
+
+ allMyChildren.verbosity = verbosity;
+
+ string clusterName;
+
+ srand ( getpid() );
+
+ makeClusterName ( clusterName );
+
+ brokerVector brokers;
+
+ if ( verbosity > 1 )
+ cout << "Starting initial cluster...\n";
+
+ for ( int i = 0; i < n_brokers; ++ i ) {
+ startNewBroker ( brokers,
+ moduleOrDir,
+ clusterName,
+ verbosity,
+ durable );
+ }
+
+
+ if ( verbosity > 0 )
+ printBrokers ( brokers );
+
+ // Get prefix for each queue name.
+ stringstream queue_prefix;
+ queue_prefix << "failover_soak_" << getpid();
+ string queue_prefix_str(queue_prefix.str());
+
+ // Run the declareQueues child.
+ int childStatus;
+ pid_t dqClientPid =
+ runDeclareQueuesClient ( brokers,
+ host,
+ declareQueuesPath,
+ verbosity,
+ durable,
+ queue_prefix_str.c_str(),
+ n_queues
+ );
+ if ( -1 == dqClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_DECLARE_1\n";
+ return CANT_FORK_DQ;
+ }
+
+ // Don't continue until declareQueues is finished.
+ pid_t retval = waitpid ( dqClientPid, & childStatus, 0);
+ if ( retval != dqClientPid) {
+ cerr << "END_OF_TEST ERROR_START_DECLARE_2\n";
+ return DQ_FAILED;
+ }
+ allMyChildren.exited ( dqClientPid, childStatus );
+
+
+ /*
+ Start one receiving and one sending client for each queue.
+ */
+ for ( int i = 0; i < n_queues; ++ i ) {
+
+ stringstream queue_name;
+ queue_name << queue_prefix.str() << '_' << i;
+ string queue_name_str(queue_name.str());
+
+ // Receiving client ---------------------------
+ pid_t receivingClientPid =
+ startReceivingClient ( brokers,
+ host,
+ receiverPath,
+ reportFrequency,
+ verbosity,
+ queue_name_str.c_str() );
+ if ( -1 == receivingClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_RECEIVER\n";
+ return CANT_FORK_RECEIVER;
+ }
+
+
+ // Sending client ---------------------------
+ pid_t sendingClientPid =
+ startSendingClient ( brokers,
+ host,
+ senderPath,
+ nMessages,
+ reportFrequency,
+ verbosity,
+ durable,
+ queue_name_str.c_str() );
+ if ( -1 == sendingClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_SENDER\n";
+ return CANT_FORK_SENDER;
+ }
+ }
+
+
+ int minSleep = 2,
+ maxSleep = 6;
+
+ int totalBrokers = n_brokers;
+
+ int loop = 0;
+
+ while ( 1 )
+ {
+ ++ loop;
+
+ /*
+ if ( verbosity > 1 )
+ std::cerr << "------- loop " << loop << " --------\n";
+
+ if ( verbosity > 0 )
+ cout << totalBrokers << " brokers have been added to the cluster.\n\n\n";
+ */
+
+ // Sleep for a while. -------------------------
+ int sleepyTime = mrand ( minSleep, maxSleep );
+ sleep ( sleepyTime );
+
+ int bullet = mrand ( 100 );
+ if ( bullet >= 95 )
+ {
+ fprintf ( stderr, "Killing oldest broker...\n" );
+
+ // Kill the oldest broker. --------------------------
+ if ( ! killFrontBroker ( brokers, verbosity ) )
+ {
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+ std::cerr << "END_OF_TEST ERROR_BROKER\n";
+ return ERROR_KILLING_BROKER;
+ }
+ ++ brokerKills;
+
+ // Start a new broker. --------------------------
+ if ( verbosity > 0 )
+ cout << "Starting new broker.\n\n";
+
+ startNewBroker ( brokers,
+ moduleOrDir,
+ clusterName,
+ verbosity,
+ durable );
+ ++ totalBrokers;
+ printBrokers ( brokers );
+ cerr << brokerKills << " brokers have been killed.\n\n\n";
+ }
+
+ int retval = allMyChildren.checkChildren();
+ if ( retval )
+ {
+ std::cerr << "END_OF_TEST ERROR_CLIENT\n";
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+ return ERROR_ON_CHILD;
+ }
+
+ // If all children have exited, quit.
+ int unfinished = allMyChildren.unfinished();
+ if ( unfinished == 0 ) {
+ killAllBrokers ( brokers, 5 );
+
+ if ( verbosity > 1 )
+ cout << "failoverSoak: all children have exited.\n";
+
+ std::cerr << "END_OF_TEST SUCCESSFUL\n";
+ return HUNKY_DORY;
+ }
+
+ }
+
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+
+ std::cerr << "END_OF_TEST SUCCESSFUL\n";
+
+ return HUNKY_DORY;
+}
+
+
+
diff --git a/qpid/cpp/src/tests/fanout_perftest b/qpid/cpp/src/tests/fanout_perftest
new file mode 100755
index 0000000000..d8a7661f49
--- /dev/null
+++ b/qpid/cpp/src/tests/fanout_perftest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode fanout --npubs 16 --nsubs 16 --size 64
diff --git a/qpid/cpp/src/tests/federated_cluster_test b/qpid/cpp/src/tests/federated_cluster_test
new file mode 100755
index 0000000000..70bec5e703
--- /dev/null
+++ b/qpid/cpp/src/tests/federated_cluster_test
@@ -0,0 +1,152 @@
+#!/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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+srcdir=`dirname $0`
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+fail() {
+ echo $1
+ exit 1
+}
+
+stop_brokers() {
+ if [[ $BROKER_A ]] ; then
+ ../qpidd --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [[ $NODE_1 ]] ; then
+ ../qpidd --no-module-dir -q --port $NODE_1
+ unset NODE_1
+ fi
+ if [[ $NODE_2 ]] ; then
+ ../qpidd --no-module-dir -q --port $NODE_2
+ unset NODE_2
+ fi
+ if [ -f cluster.ports ]; then
+ rm cluster.ports
+ fi
+}
+
+start_brokers() {
+ #start single node...
+ BROKER_A=`../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no --log-enable info+` || fail "BROKER_A failed to start"
+
+ #...and start cluster
+ $srcdir/start_cluster 2 || fail "Could not start cluster"
+ NODE_1=$(head -1 cluster.ports)
+ NODE_2=$(tail -1 cluster.ports)
+ test -n "$NODE_1" || fail "NODE_1 failed to start"
+ test -n "$NODE_2" || fail "NODE_2 failed to start"
+}
+
+setup() {
+ #create exchange on both cluster and single broker
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add exchange direct test-exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add exchange direct test-exchange
+
+ #create dynamic routes for test exchange
+ $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$NODE_2" "localhost:$BROKER_A" test-exchange
+ $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$BROKER_A" "localhost:$NODE_2" test-exchange
+
+ #create test queue on cluster and bind it to the test exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" bind test-exchange test-queue to-cluster
+
+ #create test queue on single broker and bind it to the test exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" bind test-exchange test-queue from-cluster
+}
+
+run_test_pull_to_cluster_two_consumers() {
+ #start consumers on each of the two nodes of the cluster
+ ./receiver --port $NODE_1 --queue test-queue --credit-window 1 > fed1.out.tmp &
+ ./receiver --port $NODE_2 --queue test-queue --credit-window 1 > fed2.out.tmp &
+
+ #send stream of messages to test exchange on single broker
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 2 < fed.in.tmp
+
+ #combine output of the two consumers, sort it and compare with the expected stream
+ wait
+ sort -g -k 2 fed1.out.tmp fed2.out.tmp > fed.out.tmp
+ diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+run_test_pull_to_cluster() {
+ #send stream of messages to test exchange on single broker
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 1 < fed.in.tmp
+
+ #consume from remaining node of the cluster
+ ./receiver --port $NODE_2 --queue test-queue > fed.out.tmp
+
+ #verify all messages are received
+ diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+run_test_pull_from_cluster() {
+ #start consumer on single broker
+ ./receiver --port $BROKER_A --queue test-queue --credit-window 1 > fed.out.tmp &
+
+ #send stream of messages to test exchange on cluster
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $NODE_2 --exchange test-exchange --routing-key from-cluster --send-eos 1 < fed.in.tmp
+
+ #verify all messages are received
+ wait
+ diff fed.in.tmp fed.out.tmp || fail "federated link from cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+
+if test -d ${PYTHON_DIR}; then
+ . $srcdir/ais_check
+
+ rm -f fed*.tmp #cleanup any files left from previous run
+ start_brokers
+ echo "brokers started"
+ setup
+ echo "setup completed"
+ run_test_pull_to_cluster_two_consumers
+ echo "federated link to cluster verified"
+ run_test_pull_from_cluster
+ echo "federated link from cluster verified"
+ if [[ $TEST_NODE_FAILURE ]] ; then
+ #kill first cluster node and retest
+ kill -9 $(../qpidd --check --port $NODE_1) && unset NODE_1
+ echo "killed first cluster node; waiting for links to re-establish themselves..."
+ sleep 5
+ echo "retesting..."
+ run_test_pull_to_cluster
+ echo "federated link to cluster verified"
+ run_test_pull_from_cluster
+ echo "federated link from cluster verified"
+ fi
+fi
diff --git a/qpid/cpp/src/tests/federated_cluster_test_with_node_failure b/qpid/cpp/src/tests/federated_cluster_test_with_node_failure
new file mode 100755
index 0000000000..f144a676de
--- /dev/null
+++ b/qpid/cpp/src/tests/federated_cluster_test_with_node_failure
@@ -0,0 +1,23 @@
+#!/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.
+#
+
+srcdir=`dirname $0`
+TEST_NODE_FAILURE=1 $srcdir/federated_cluster_test
diff --git a/qpid/cpp/src/tests/federated_topic_test b/qpid/cpp/src/tests/federated_topic_test
new file mode 100755
index 0000000000..b1063c7e8c
--- /dev/null
+++ b/qpid/cpp/src/tests/federated_topic_test
@@ -0,0 +1,129 @@
+#!/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.
+#
+
+# Run the topic test on a federated setup
+
+# Clean up old log files
+rm -f subscriber_*.log
+
+# Defaults values
+SUBSCRIBERS=2
+MESSAGES=1000
+BATCHES=1
+VERBOSE=1
+
+while getopts "s:m:b:" opt ; do
+ case $opt in
+ s) SUBSCRIBERS=$OPTARG ;;
+ m) MESSAGES=$OPTARG ;;
+ b) BATCHES=$OPTARG ;;
+ ?)
+ echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]"
+ exit 1
+ ;;
+ esac
+done
+
+MY_DIR=$(dirname $(which $0))
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+start_broker() {
+ ${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
+}
+
+start_brokers() {
+ start_broker
+ PORT_A=`cat qpidd.port`
+ start_broker
+ PORT_B=`cat qpidd.port`
+ start_broker
+ PORT_C=`cat qpidd.port`
+}
+
+stop_brokers() {
+ for p in $PORT_A $PORT_B $PORT_C; do
+ $QPIDD_EXEC --no-module-dir -q --port $p
+ done
+}
+
+subscribe() {
+ #which broker should we connect to?
+ if (( $1 % 2 )); then
+ MY_PORT=$PORT_C;
+ else
+ MY_PORT=$PORT_A;
+ fi
+
+ echo Subscriber $1 connecting on $MY_PORT
+ LOG="subscriber_$1.log"
+ ${MY_DIR}/topic_listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG
+}
+
+publish() {
+ ${MY_DIR}/topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A
+}
+
+setup_routes() {
+ BROKER_A="localhost:$PORT_A"
+ BROKER_B="localhost:$PORT_B"
+ BROKER_C="localhost:$PORT_C"
+ if (($VERBOSE)); then
+ echo "Establishing routes for topic..."
+ fi
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_A amq.topic topic_control B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_C $BROKER_B amq.topic topic_control C C
+ if (($VERBOSE)); then
+ echo "linked A->B->C"
+ fi
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A
+ if (($VERBOSE)); then
+ echo "linked C->B->A"
+ echo "Establishing routes for response queue..."
+ fi
+
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A
+ if (($VERBOSE)); then
+ echo "linked C->B->A"
+ for b in $BROKER_A $BROKER_B $BROKER_C; do
+ echo "Routes for $b"
+ $PYTHON_COMMANDS/qpid-route route list $b
+ done
+ fi
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ if (($VERBOSE)); then
+ echo "Running federated topic test against brokers on ports $PORT_A $PORT_B $PORT_C"
+ fi
+
+ for ((i=$SUBSCRIBERS ; i--; )); do
+ subscribe $i &
+ done
+
+ setup_routes
+
+ publish || exit 1
+fi
diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py
new file mode 100755
index 0000000000..201b06a4a2
--- /dev/null
+++ b/qpid/cpp/src/tests/federation.py
@@ -0,0 +1,2091 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+from qpid.testlib import TestBase010
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from qpid.util import URL
+from time import sleep, time
+
+
+class _FedBroker(object):
+ """
+ A proxy object for a remote broker. Contains connection and management
+ state.
+ """
+ def __init__(self, host, port,
+ conn=None, session=None, qmf_broker=None):
+ self.host = host
+ self.port = port
+ self.url = "%s:%d" % (host, port)
+ self.client_conn = None
+ self.client_session = None
+ self.qmf_broker = None
+ self.qmf_object = None
+ if conn is not None:
+ self.client_conn = conn
+ if session is not None:
+ self.client_session = session
+ if qmf_broker is not None:
+ self.qmf_broker = qmf_broker
+
+
+class FederationTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+
+ def _setup_brokers(self):
+ ports = [self.remote_port()]
+ extra = self.defines.get("extra-brokers")
+ if extra:
+ for p in extra.split():
+ ports.append(int(p))
+
+ # broker[0] has already been set up.
+ self._brokers = [_FedBroker(self.broker.host,
+ self.broker.port,
+ self.conn,
+ self.session,
+ self.qmf_broker)]
+ self._brokers[0].qmf_object = self.qmf.getObjects(_class="broker")[0]
+
+ # setup remaining brokers
+ for _p in ports:
+ _b = _FedBroker(self.remote_host(), _p)
+ _b.client_conn = self.connect(host=self.remote_host(), port=_p)
+ _b.client_session = _b.client_conn.session("Fed_client_session_" + str(_p))
+ _b.qmf_broker = self.qmf.addBroker(_b.url)
+ for _bo in self.qmf.getObjects(_class="broker"):
+ if _bo.getBroker().getUrl() == _b.qmf_broker.getUrl():
+ _b.qmf_object = _bo
+ break
+ self._brokers.append(_b)
+
+ def _teardown_brokers(self):
+ """ Un-does _setup_brokers()
+ """
+ # broker[0] is configured at test setup, so it must remain configured
+ for _b in self._brokers[1:]:
+ self.qmf.delBroker(_b.qmf_broker)
+ if not _b.client_session.error():
+ _b.client_session.close(timeout=10)
+ _b.client_conn.close(timeout=10)
+
+
+ def test_bridge_create_and_close(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.direct", "my-key", "", "", False, False, False, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_pull_from_exchange(self):
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, False, False, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from local broker
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+ sleep(6)
+
+ #send messages to remote broker and confirm it is routed to local broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_pull_from_exchange")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="my-key")
+ r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_push_to_exchange(self):
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, True, False, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from remote broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_push_to_exchange")
+ r_session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ r_session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(session=r_session, queue="fed1", destination="f1")
+ queue = r_session.incoming("f1")
+ sleep(6)
+
+ #send messages to local broker and confirm it is routed to remote broker
+ for i in range(1, 11):
+ dp = session.delivery_properties(routing_key="my-key")
+ session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_pull_from_queue(self):
+ session = self.session
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_pull_from_queue")
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
+ for i in range(1, 6):
+ dp = r_session.delivery_properties(routing_key="my-bridge-queue")
+ r_session.message_transfer(message=Message(dp, "Message %d" % i))
+
+ #setup queue to receive messages from local broker
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(3)
+
+ #add some more messages (i.e. after bridge was created)
+ for i in range(6, 11):
+ dp = r_session.delivery_properties(routing_key="my-bridge-queue")
+ r_session.message_transfer(message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ try:
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ except Empty:
+ self.fail("Failed to find expected message containing 'Message %d'" % i)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_tracing_automatic(self):
+ remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port())
+ self.startQmf()
+ l_broker = self.qmf_broker
+ r_broker = self.qmf.addBroker(remoteUrl)
+
+ l_brokerObj = self.qmf.getObjects(_class="broker", _broker=l_broker)[0]
+ r_brokerObj = self.qmf.getObjects(_class="broker", _broker=r_broker)[0]
+
+ l_res = l_brokerObj.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ r_res = r_brokerObj.connect(self.broker.host, self.broker.port, False, "PLAIN", "guest", "guest", "tcp")
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+
+ l_res = l_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0)
+ r_res = r_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0)
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ count = 0
+ while l_link.state != "Operational" or r_link.state != "Operational":
+ count += 1
+ if count > 10:
+ self.fail("Fed links didn't become operational after 10 seconds")
+ sleep(1)
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+ sleep(3)
+
+ #setup queue to receive messages from local broker
+ session = self.session
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.direct", binding_key="key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_trace")
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="key")
+ r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ try:
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ except Empty:
+ self.fail("Failed to find expected message containing 'Message %d'" % i)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ def test_tracing(self):
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "my-bridge-id",
+ "exclude-me,also-exclude-me", False, False, False, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from local broker
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+ sleep(6)
+
+ #send messages to remote broker and confirm it is routed to local broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_tracing")
+
+ trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"]
+ body = ["yes", "first-bad", "second-bad", "third-bad", "yes"]
+ for b, t in zip(body, trace):
+ headers = {}
+ if (t): headers["x-qpid.trace"]=t
+ dp = r_session.delivery_properties(routing_key="my-key", ttl=1000*60*5)
+ mp = r_session.message_properties(application_headers=headers)
+ r_session.message_transfer(destination="amq.direct", message=Message(dp, mp, b))
+
+ for e in ["my-bridge-id", "dont-exclude-me,my-bridge-id"]:
+ msg = queue.get(timeout=5)
+ self.assertEqual("yes", msg.body)
+ self.assertEqual(e, self.getAppHeader(msg, "x-qpid.trace"))
+ assert(msg.get("delivery_properties").ttl > 0)
+ assert(msg.get("delivery_properties").ttl < 1000*60*50)
+
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_fanout(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_fanout")
+
+ session.exchange_declare(exchange="fed.fanout", type="fanout")
+ r_session.exchange_declare(exchange="fed.fanout", type="fanout")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.fanout", "fed.fanout", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties()
+ r_session.message_transfer(destination="fed.fanout", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct")
+
+ session.exchange_declare(exchange="fed.direct", type="direct")
+ r_session.exchange_declare(exchange="fed.direct", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct", "fed.direct", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct", binding_key="fd-key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="fd-key")
+ r_session.message_transfer(destination="fed.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic")
+
+ session.exchange_declare(exchange="fed.topic", type="topic")
+ r_session.exchange_declare(exchange="fed.topic", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_reorigin")
+
+ session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+
+ session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.topic_reorigin_2", binding_key="ft-key.one.#")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic_reorigin", "fed.topic_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.topic_reorigin_2", "fed.topic_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic_reorigin", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = bridge2.close()
+ self.assertEqual(result.status, 0)
+
+ # extra check: verify we don't leak bridge objects - keep the link
+ # around and verify the bridge count has gone to zero
+
+ attempts = 0
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+ while bridgeCount > 0:
+ attempts += 1
+ if attempts >= 5:
+ self.fail("Bridges didn't clean up")
+ return
+ sleep(1)
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_direct_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct_reorigin")
+
+ session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+
+ session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.direct_reorigin_2", binding_key="ft-key.two")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct_reorigin", "fed.direct_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.direct_reorigin_2", "fed.direct_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct_reorigin", binding_key="ft-key.one")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one")
+ r_session.message_transfer(destination="fed.direct_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers")
+
+ session.exchange_declare(exchange="fed.headers", type="headers")
+ r_session.exchange_declare(exchange="fed.headers", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers", "fed.headers", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.headers", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.message_properties(application_headers={'class':'first'})
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.headers", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ content = msg.body
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_reorigin")
+
+ session.exchange_declare(exchange="fed.headers_reorigin", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_reorigin", type="headers")
+
+ session.exchange_declare(exchange="fed.headers_reorigin_2", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_reorigin_2", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.headers_reorigin_2", binding_key="key2", arguments={'x-match':'any', 'class':'second'})
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_reorigin", "fed.headers_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.headers_reorigin_2", "fed.headers_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.headers_reorigin", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.message_properties(application_headers={'class':'first'})
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.headers_reorigin", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_unbind(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_unbind")
+
+ session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_unbind", "fed.headers_unbind", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_headers_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_xml")
+
+ session.exchange_declare(exchange="fed.xml", type="xml")
+ r_session.exchange_declare(exchange="fed.xml", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml", "fed.xml", "", "", "", False, False, True, 0)
+
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.xml", binding_key="key1", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.delivery_properties(routing_key="key1")
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.xml", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ content = msg.body
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_reorigin_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_reorigin_xml")
+
+ session.exchange_declare(exchange="fed.xml_reorigin", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_reorigin", type="xml")
+
+ session.exchange_declare(exchange="fed.xml_reorigin_2", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_reorigin_2", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.xml_reorigin_2", binding_key="key2", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml_reorigin", "fed.xml_reorigin", "", "", "", False, False, True, 0)
+
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.xml_reorigin_2", "fed.xml_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ foo=qmf.getObjects(_class="link")
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.xml_reorigin", binding_key="key1", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.delivery_properties(routing_key="key1")
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.xml_reorigin", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_unbind_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_xml_unbind")
+
+ session.exchange_declare(exchange="fed.xml_unbind", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_unbind", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml_unbind", "fed.xml_unbind", "", "", "", False, False, True, 0)
+
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.xml_unbind", binding_key="key1", arguments={'xquery':'true()'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.xml_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_topic_nodup(self):
+ """Verify that a message whose routing key matches more than one
+ binding does not get duplicated to the same queue.
+ """
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_nodup")
+
+ session.exchange_declare(exchange="fed.topic", type="topic")
+ r_session.exchange_declare(exchange="fed.topic", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="red.*")
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="*.herring")
+
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="red.herring")
+ r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct_route_prop(self):
+ """ Set up a tree of uni-directional routes across the direct exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spudboy"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spudboy"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create direct exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.direct":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.direct" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.direct", # src
+ "fedX.direct", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active. Hopefully, this is long enough!
+ sleep(6)
+
+ # create a queue on B2, bound to "spudboy"
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+
+ # create a queue on B3, bound to "spudboy"
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # @todo - restore code when QPID-2499 fixed!!
+ sleep(6)
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic_route_prop(self):
+ """ Set up a tree of uni-directional routes across a topic exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spud.*"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spud.*"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.topic", type="topic")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.topic").type,
+ "topic", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.topic":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.topic" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.topic", # src
+ "fedX.topic", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+ sleep(6)
+
+ # create a queue on B2, bound to "spudboy"
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+
+ # create a queue on B3, bound to "spudboy"
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spud.boy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.topic", message=Message(dp, "Message_trp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spud.boy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.topic", message=Message(dp, "Message_trp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.topic")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_fanout_route_prop(self):
+ """ Set up a tree of uni-directional routes across a fanout exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spud.*"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spud.*"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create fanout exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.fanout", type="fanout")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.fanout").type,
+ "fanout", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.fanout":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.fanout" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.fanout", # src
+ "fedX.fanout", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+ sleep(6)
+
+ # create a queue on B2, bound to the exchange
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.fanout")
+
+ # create a queue on B3, bound to the exchange
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.fanout")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties()
+ self._brokers[0].client_session.message_transfer(destination="fedX.fanout", message=Message(dp, "Message_frp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.fanout")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties()
+ self._brokers[0].client_session.message_transfer(destination="fedX.fanout", message=Message(dp, "Message_frp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.fanout")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.fanout")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
+
+ def test_dynamic_topic_bounce(self):
+ """ Bounce the connection between federated Topic Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "topic"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud.*")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud.*")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud.boy")
+
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_direct_bounce(self):
+ """ Bounce the connection between federated Direct Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "direct"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename, binding_key="spud")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_fanout_bounce(self):
+ """ Bounce the connection between federated Fanout Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "fanout"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename)
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename)
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_headers_bounce(self):
+ """ Bounce the connection between federated Headers Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "headers"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud", arguments={'x-match':'any', 'class':'first'})
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.message_properties(application_headers={'class':'first'})
+ ## @todo KAG - re-enable once federation bugs with headers exchanges
+ ## are fixed.
+ #self.generic_dynamic_bounce_test(Params())
+ return
+
+
+ def generic_dynamic_bounce_test(self, params):
+ """ Verify that a federated broker can maintain a binding to a local
+ queue using the same key as a remote binding. Destroy and reconnect
+ the federation link, and verify routes are restored correctly.
+ See QPID-3170.
+ Topology:
+
+ Queue1 <---"Key"---B0<==[Federated Exchange]==>B1---"Key"--->Queue2
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_declare(exchange="fedX", type=params.exchange_type())
+ self.assertEqual(_b.client_session.exchange_query(name="fedX").type,
+ params.exchange_type(), "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ timeout = time() + 10
+ while my_exchange is None and time() <= timeout:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ self.fail("QMF failed to find new exchange!")
+ exchanges.append(my_exchange)
+
+ #
+ # on each broker, create a local queue bound to the exchange with the
+ # same key value.
+ #
+
+ self._brokers[0].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[0].client_session, queue="fedX1", destination="f1")
+ queue_0 = self._brokers[0].client_session.incoming("f1")
+
+ self._brokers[1].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[1].client_session, queue="fedX1", destination="f1")
+ queue_1 = self._brokers[1].client_session.incoming("f1")
+
+ # now federate the two brokers
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B0
+ result = self._brokers[0].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ # wait for all the inter-broker links to become operational
+ operational = False
+ timeout = time() + 10
+ while not operational and time() <= timeout:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ self.failUnless(operational, "inter-broker links failed to become operational.")
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+
+ # wait until the binding key has propagated to each broker - each
+ # broker should see 2 bindings (1 local, 1 remote)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount < binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ # send 10 msgs to B0
+ for i in range(1, 11):
+ # dp = self._brokers[0].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[0].client_session)
+ self._brokers[0].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's local queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+ #
+ # Tear down the bridges between the two exchanges, then wait
+ # for the bindings to be cleaned up
+ #
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # restore the bridges between the two exchanges, and wait for the
+ # bindings to propagate.
+ #
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # verify traffic flows correctly
+ #
+
+ for i in range(1, 11):
+ #dp = self._brokers[1].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[1].client_session)
+ self._brokers[1].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+
+ #
+ # cleanup
+ #
+ params.unbind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self._brokers[0].client_session.message_cancel(destination="f1")
+ self._brokers[0].client_session.queue_delete(queue="fedX1")
+
+ params.unbind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self._brokers[1].client_session.message_cancel(destination="f1")
+ self._brokers[1].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_delete(exchange="fedX")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
diff --git a/qpid/cpp/src/tests/find_prog.ps1 b/qpid/cpp/src/tests/find_prog.ps1
new file mode 100644
index 0000000000..5c482debbf
--- /dev/null
+++ b/qpid/cpp/src/tests/find_prog.ps1
@@ -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.
+#
+
+# Locate the subdirectory where the specified program resides; the program
+# must have a directory and a file name, even if the directory is .
+param(
+ [string] $prog # program to look for somewhere below cwd
+)
+
+$dir = Split-Path $prog
+$exe = Split-Path $prog -leaf
+$sub = ""
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($try in $subs) {
+ $prog = "$dir\$try\$exe"
+ if (Test-Path $prog) {
+ $sub = $try
+ break
+ }
+}
diff --git a/qpid/cpp/src/tests/header_test.cpp b/qpid/cpp/src/tests/header_test.cpp
new file mode 100644
index 0000000000..c36b4f3bc3
--- /dev/null
+++ b/qpid/cpp/src/tests/header_test.cpp
@@ -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.
+ *
+ */
+
+#include <iostream>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ TestOptions opts;
+ try {
+ opts.parse(argc, argv);
+ Connection connection;
+ connection.open(opts.con);
+ Session session = connection.newSession();
+ std::string q("header_interop_test_queue");
+ session.queueDeclare(arg::queue=q);
+ double pi = 3.14159265;
+ float e = 2.71828f;
+ Message msg("", q);
+ msg.getMessageProperties().getApplicationHeaders().setDouble("pi", pi);
+ msg.getMessageProperties().getApplicationHeaders().setFloat("e", e);
+ session.messageTransfer(arg::content=msg);
+
+ session.close();
+ connection.close();
+
+ return 0;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/header_test.py b/qpid/cpp/src/tests/header_test.py
new file mode 100755
index 0000000000..d5a2c16c01
--- /dev/null
+++ b/qpid/cpp/src/tests/header_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import qpid
+import sys
+import os
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import Message, RangedSet, uuid4
+from qpid.queue import Empty
+from math import fabs
+
+def getApplicationHeaders(msg):
+ for h in msg.headers:
+ if hasattr(h, 'application_headers'): return getattr(h, 'application_headers')
+ return None
+
+# Set parameters for login
+
+host="127.0.0.1"
+port=5672
+user="guest"
+password="guest"
+
+if len(sys.argv) > 1 :
+ host=sys.argv[1]
+if len(sys.argv) > 2 :
+ port=int(sys.argv[2])
+
+# Create a connection.
+socket = connect(host, port)
+connection = Connection (sock=socket)
+connection.start()
+session = connection.session(str(uuid4()))
+
+q = "header_interop_test_queue"
+session.queue_declare(queue=q)
+
+session.message_subscribe(queue=q, destination="received")
+queue = session.incoming("received")
+queue.start()
+
+msg = queue.get(timeout=10)
+pi = 3.14159265
+e = 2.71828
+
+headers = getApplicationHeaders(msg)
+pi_ = headers["pi"]
+e_ = headers["e"]
+session.close(timeout=10)
+
+failed = False
+
+if pi != pi_:
+ print "got incorrect value for pi: ", pi_, " expected:", pi
+ failed = True
+
+if fabs(e - e_) > 0.0001:
+ print "got incorrect value for e: ", e_, " expected:", e
+ failed = True
+
+if failed:
+ sys.exit(1)
+else:
+ print "Correct header values received."
+ sys.exit(0)
+
+
+
diff --git a/qpid/cpp/src/tests/headers_federation.py b/qpid/cpp/src/tests/headers_federation.py
new file mode 100644
index 0000000000..60cff1da54
--- /dev/null
+++ b/qpid/cpp/src/tests/headers_federation.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+from qpid.testlib import TestBase010
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+class HeadersFederationTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+
+ def test_dynamic_headers_unbind(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_unbind")
+
+ session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_unbind", "fed.headers_unbind", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
diff --git a/qpid/cpp/src/tests/install_env.sh.in b/qpid/cpp/src/tests/install_env.sh.in
new file mode 100644
index 0000000000..2231954cb8
--- /dev/null
+++ b/qpid/cpp/src/tests/install_env.sh.in
@@ -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.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+prefix=`absdir @prefix@`
+export QPID_INSTALL_PREFIX=$prefix
+export PATH=$prefix/bin:$prefix/sbin:$prefix/libexec/qpid/tests:$PATH
+export LD_LIBRARY_PATH=$prefix/lib:$LD_LIBRARY_PATH
+export PYTHONPATH=$prefix/lib/python2.4/site-packages:$PYTHONPATH
diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp
new file mode 100644
index 0000000000..fc55d642c3
--- /dev/null
+++ b/qpid/cpp/src/tests/logging.cpp
@@ -0,0 +1,385 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "test_tools.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/log/OstreamOutput.h"
+#include "qpid/memory.h"
+#include "qpid/Options.h"
+#if defined (_WIN32)
+# include "qpid/log/windows/SinkOptions.h"
+#else
+# include "qpid/log/posix/SinkOptions.h"
+#endif
+
+#include <boost/test/floating_point_comparison.hpp>
+#include <boost/format.hpp>
+#include "unit_test.h"
+
+#include <exception>
+#include <fstream>
+#include <time.h>
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(loggingTestSuite)
+
+using namespace std;
+using namespace boost;
+using namespace qpid::log;
+
+QPID_AUTO_TEST_CASE(testStatementInit) {
+ Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__;
+ BOOST_CHECK(!s.enabled);
+ BOOST_CHECK_EQUAL(string(__FILE__), s.file);
+ BOOST_CHECK_EQUAL(line, s.line);
+ BOOST_CHECK_EQUAL(debug, s.level);
+}
+
+
+QPID_AUTO_TEST_CASE(testSelector_enable) {
+ Selector s;
+ // Simple enable
+ s.enable(debug,"foo");
+ BOOST_CHECK(s.isEnabled(debug,"foo"));
+ BOOST_CHECK(!s.isEnabled(error,"foo"));
+ BOOST_CHECK(!s.isEnabled(error,"bar"));
+
+ // Substring match
+ BOOST_CHECK(s.isEnabled(debug, "bazfoobar"));
+ BOOST_CHECK(!s.isEnabled(debug, "bazbar"));
+
+ // Different levels for different substrings.
+ s.enable(info, "bar");
+ BOOST_CHECK(s.isEnabled(debug, "foobar"));
+ BOOST_CHECK(s.isEnabled(info, "foobar"));
+ BOOST_CHECK(!s.isEnabled(debug, "bar"));
+ BOOST_CHECK(!s.isEnabled(info, "foo"));
+
+ // Enable-strings
+ s.enable("notice:blob");
+ BOOST_CHECK(s.isEnabled(notice, "blob"));
+ s.enable("error+:oops");
+ BOOST_CHECK(s.isEnabled(error, "oops"));
+ BOOST_CHECK(s.isEnabled(critical, "oops"));
+}
+
+QPID_AUTO_TEST_CASE(testStatementEnabled) {
+ // Verify that the singleton enables and disables static
+ // log statements.
+ Logger& l = Logger::instance();
+ ScopedSuppressLogging ls(l);
+ l.select(Selector(debug));
+ static Statement s=QPID_LOG_STATEMENT_INIT(debug);
+ BOOST_CHECK(!s.enabled);
+ static Statement::Initializer init(s);
+ BOOST_CHECK(s.enabled);
+
+ static Statement s2=QPID_LOG_STATEMENT_INIT(warning);
+ static Statement::Initializer init2(s2);
+ BOOST_CHECK(!s2.enabled);
+
+ l.select(Selector(warning));
+ BOOST_CHECK(!s.enabled);
+ BOOST_CHECK(s2.enabled);
+}
+
+struct TestOutput : public Logger::Output {
+ vector<string> msg;
+ vector<Statement> stmt;
+
+ TestOutput(Logger& l) {
+ l.output(std::auto_ptr<Logger::Output>(this));
+ }
+
+ void log(const Statement& s, const string& m) {
+ msg.push_back(m);
+ stmt.push_back(s);
+ }
+ string last() { return msg.back(); }
+};
+
+using boost::assign::list_of;
+
+QPID_AUTO_TEST_CASE(testLoggerOutput) {
+ Logger l;
+ l.clear();
+ l.select(Selector(debug));
+ Statement s=QPID_LOG_STATEMENT_INIT(debug);
+
+ TestOutput* out=new TestOutput(l);
+
+ // Verify message is output.
+ l.log(s, "foo");
+ vector<string> expect=list_of("foo\n");
+ BOOST_CHECK_EQUAL(expect, out->msg);
+
+ // Verify multiple outputs
+ TestOutput* out2=new TestOutput(l);
+ l.log(Statement(), "baz");
+ expect.push_back("baz\n");
+ BOOST_CHECK_EQUAL(expect, out->msg);
+ expect.erase(expect.begin());
+ BOOST_CHECK_EQUAL(expect, out2->msg);
+}
+
+QPID_AUTO_TEST_CASE(testMacro) {
+ Logger& l=Logger::instance();
+ ScopedSuppressLogging ls(l);
+ l.select(Selector(info));
+ TestOutput* out=new TestOutput(l);
+ QPID_LOG(info, "foo");
+ vector<string> expect=list_of("foo\n");
+ BOOST_CHECK_EQUAL(expect, out->msg);
+ BOOST_CHECK_EQUAL(__FILE__, out->stmt.front().file);
+
+ // Not enabled:
+ QPID_LOG(debug, "bar");
+ BOOST_CHECK_EQUAL(expect, out->msg);
+
+ QPID_LOG(info, 42 << " bingo");
+ expect.push_back("42 bingo\n");
+ BOOST_CHECK_EQUAL(expect, out->msg);
+}
+
+QPID_AUTO_TEST_CASE(testLoggerFormat) {
+ Logger& l = Logger::instance();
+ ScopedSuppressLogging ls(l);
+ l.select(Selector(critical));
+ TestOutput* out=new TestOutput(l);
+
+ l.format(Logger::FILE);
+ QPID_LOG(critical, "foo");
+ BOOST_CHECK_EQUAL(out->last(), string(__FILE__)+": foo\n");
+
+ l.format(Logger::FILE|Logger::LINE);
+ QPID_LOG(critical, "foo");
+ BOOST_CHECK_EQUAL(out->last().find(__FILE__), 0u);
+
+ l.format(Logger::FUNCTION);
+ QPID_LOG(critical, "foo");
+ BOOST_CHECK_EQUAL(string(BOOST_CURRENT_FUNCTION) + ": foo\n", out->last());
+
+ l.format(Logger::LEVEL);
+ QPID_LOG(critical, "foo");
+ BOOST_CHECK_EQUAL("critical foo\n", out->last());
+}
+
+QPID_AUTO_TEST_CASE(testOstreamOutput) {
+ Logger& l=Logger::instance();
+ ScopedSuppressLogging ls(l);
+ l.select(Selector(error));
+ ostringstream os;
+ l.output(qpid::make_auto_ptr<Logger::Output>(new OstreamOutput(os)));
+ QPID_LOG(error, "foo");
+ QPID_LOG(error, "bar");
+ QPID_LOG(error, "baz");
+ BOOST_CHECK_EQUAL("foo\nbar\nbaz\n", os.str());
+}
+
+#if 0 // This test requires manual intervention. Normally disabled.
+QPID_AUTO_TEST_CASE(testSyslogOutput) {
+ Logger& l=Logger::instance();
+ Logger::StateSaver ls(l);
+ l.clear();
+ l.select(Selector(info));
+ l.syslog("qpid_test");
+ QPID_LOG(info, "Testing QPID");
+ BOOST_ERROR("Manually verify that /var/log/messages contains a recent line 'Testing QPID'");
+}
+#endif // 0
+
+int count() {
+ static int n = 0;
+ return n++;
+}
+
+int loggedCount() {
+ static int n = 0;
+ QPID_LOG(debug, "counting: " << n);
+ return n++;
+}
+
+
+using namespace qpid::sys;
+
+// Measure CPU time.
+clock_t timeLoop(int times, int (*fp)()) {
+ clock_t start=clock();
+ while (times-- > 0)
+ (*fp)();
+ return clock() - start;
+}
+
+// Overhead test disabled because it consumes a ton of CPU and takes
+// forever under valgrind. Not friendly for regular test runs.
+//
+#if 0
+QPID_AUTO_TEST_CASE(testOverhead) {
+ // Ensure that the ratio of CPU time for an incrementing loop
+ // with and without disabled log statements is in acceptable limits.
+ //
+ int times=100000000;
+ clock_t noLog=timeLoop(times, count);
+ clock_t withLog=timeLoop(times, loggedCount);
+ double ratio=double(withLog)/double(noLog);
+
+ // NB: in initial tests the ratio was consistently below 1.5,
+ // 2.5 is reasonable and should avoid spurios failures
+ // due to machine load.
+ //
+ BOOST_CHECK_SMALL(ratio, 2.5);
+}
+#endif // 0
+
+Statement statement(
+ Level level, const char* file="", int line=0, const char* fn=0)
+{
+ Statement s={0, file, line, fn, level};
+ return s;
+}
+
+
+#define ARGC(argv) (sizeof(argv)/sizeof(char*))
+
+QPID_AUTO_TEST_CASE(testOptionsParse) {
+ const char* argv[]={
+ 0,
+ "--log-enable", "error+:foo",
+ "--log-enable", "debug:bar",
+ "--log-enable", "info",
+ "--log-to-stderr", "no",
+ "--log-to-file", "logout",
+ "--log-level", "yes",
+ "--log-source", "1",
+ "--log-thread", "true",
+ "--log-function", "YES"
+ };
+ qpid::log::Options opts("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
+ opts.parse(ARGC(argv), const_cast<char**>(argv));
+ sinks = *opts.sinkOptions;
+ vector<string> expect=list_of("error+:foo")("debug:bar")("info");
+ BOOST_CHECK_EQUAL(expect, opts.selectors);
+ BOOST_CHECK(!sinks.logToStderr);
+ BOOST_CHECK(!sinks.logToStdout);
+ BOOST_CHECK(sinks.logFile == "logout");
+ BOOST_CHECK(opts.level);
+ BOOST_CHECK(opts.source);
+ BOOST_CHECK(opts.function);
+ BOOST_CHECK(opts.thread);
+}
+
+QPID_AUTO_TEST_CASE(testOptionsDefault) {
+ qpid::log::Options opts("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
+ sinks = *opts.sinkOptions;
+ BOOST_CHECK(sinks.logToStderr);
+ BOOST_CHECK(!sinks.logToStdout);
+ BOOST_CHECK(sinks.logFile.length() == 0);
+ vector<string> expect=list_of("notice+");
+ BOOST_CHECK_EQUAL(expect, opts.selectors);
+ BOOST_CHECK(opts.time && opts.level);
+ BOOST_CHECK(!(opts.source || opts.function || opts.thread));
+}
+
+QPID_AUTO_TEST_CASE(testSelectorFromOptions) {
+ const char* argv[]={
+ 0,
+ "--log-enable", "error+:foo",
+ "--log-enable", "debug:bar",
+ "--log-enable", "info"
+ };
+ qpid::log::Options opts("");
+ opts.parse(ARGC(argv), const_cast<char**>(argv));
+ vector<string> expect=list_of("error+:foo")("debug:bar")("info");
+ BOOST_CHECK_EQUAL(expect, opts.selectors);
+ Selector s(opts);
+ BOOST_CHECK(!s.isEnabled(warning, "x"));
+ BOOST_CHECK(!s.isEnabled(debug, "x"));
+ BOOST_CHECK(s.isEnabled(debug, "bar"));
+ BOOST_CHECK(s.isEnabled(error, "foo"));
+ BOOST_CHECK(s.isEnabled(critical, "foo"));
+}
+
+QPID_AUTO_TEST_CASE(testLoggerStateure) {
+ Logger& l=Logger::instance();
+ ScopedSuppressLogging ls(l);
+ qpid::log::Options opts("test");
+ const char* argv[]={
+ 0,
+ "--log-time", "no",
+ "--log-source", "yes",
+ "--log-to-stderr", "no",
+ "--log-to-file", "logging.tmp",
+ "--log-enable", "critical"
+ };
+ opts.parse(ARGC(argv), const_cast<char**>(argv));
+ l.configure(opts);
+ QPID_LOG(critical, "foo"); int srcline=__LINE__;
+ ifstream log("logging.tmp");
+ string line;
+ getline(log, line);
+ string expect=(format("critical %s:%d: foo")%__FILE__%srcline).str();
+ BOOST_CHECK_EQUAL(expect, line);
+ log.close();
+ unlink("logging.tmp");
+}
+
+QPID_AUTO_TEST_CASE(testQuoteNonPrintable) {
+ Logger& l=Logger::instance();
+ ScopedSuppressLogging ls(l);
+ qpid::log::Options opts("test");
+ opts.time=false;
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions *sinks =
+ dynamic_cast<qpid::log::windows::SinkOptions *>(opts.sinkOptions.get());
+#else
+ qpid::log::posix::SinkOptions *sinks =
+ dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get());
+#endif
+ sinks->logToStderr = false;
+ sinks->logFile = "logging.tmp";
+ l.configure(opts);
+
+ char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff";
+ string str(s, sizeof(s));
+ QPID_LOG(critical, str);
+ ifstream log("logging.tmp");
+ string line;
+ getline(log, line, '\0');
+ string expect="critical null\\x00tab\tspace newline\nret\r\\x80\\x99\\xFF\\x00\n";
+ BOOST_CHECK_EQUAL(expect, line);
+ log.close();
+ unlink("logging.tmp");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/long_cluster_tests.py b/qpid/cpp/src/tests/long_cluster_tests.py
new file mode 100755
index 0000000000..f77837f0c4
--- /dev/null
+++ b/qpid/cpp/src/tests/long_cluster_tests.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, unittest
+from testlib import TestBaseCluster
+
+class LongClusterTests(TestBaseCluster):
+ """Long/Soak cluster tests with async store ability"""
+
+
+ def test_LongCluster_01_DummyTest(self):
+ """Dummy test - a placeholder for the first of the long/soak python cluster tests"""
+ pass
+
+# Start the test here
+
+if __name__ == '__main__':
+ if os.getenv("STORE_LIB") != None:
+ print "NOTE: Store enabled for the following tests:"
+ if not unittest.main(): sys.exit(1)
+
diff --git a/qpid/cpp/src/tests/multiq_perftest b/qpid/cpp/src/tests/multiq_perftest
new file mode 100755
index 0000000000..10f9edd2a6
--- /dev/null
+++ b/qpid/cpp/src/tests/multiq_perftest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode shared --qt 16
diff --git a/qpid/cpp/src/tests/perfdist b/qpid/cpp/src/tests/perfdist
new file mode 100755
index 0000000000..59548b23f7
--- /dev/null
+++ b/qpid/cpp/src/tests/perfdist
@@ -0,0 +1,87 @@
+#!/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.
+#
+
+
+#
+# Distributed perftest.
+# Runs perftest clients on multiple hosts using ssh.
+#
+
+set -e
+usage() {
+cat <<EOF
+usage: $0 <perftest-args> -- <client-hosts ...> [ --- <broker hosts...> ]
+Client & broker hosts can also be set in env vars CLIENTS and BROKERS.
+
+Run perftest clients on the client hosts against brokers on the broker
+hosts Clients are assigned to client hosts round robin: publishers
+first, then subscribers. If there are multiple brokers (for cluster
+tests) clients connect to them round robin.
+
+Broker hosts can be listed with -b in perftest-args or after ---
+at the end of the arguments.
+
+Error: $*
+EOF
+exit 1
+}
+
+TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts.
+
+collect() { eval $COLLECT=\""\$$COLLECT $*"\"; }
+NPUBS=1
+NSUBS=1
+COLLECT=ARGS
+while test $# -gt 0; do
+ case $1 in
+ --publish|--subscribe|--setup|--control) usage "Don't pass perftest action flags: $1" ;;
+ --npubs) collect $1 $2; NPUBS=$2; shift 2 ;;
+ --nsubs) collect $1 $2; NSUBS=$2; shift 2 ;;
+ -s|--summary) collect $1; QUIET=yes; shift 1 ;;
+ -b|--broker) BROKERS="$BROKERS $2"; shift 2;;
+ --) COLLECT=CLIENTARG; shift ;;
+ ---) COLLECT=BROKERARG; shift;;
+ *) collect $1; shift ;;
+ esac
+done
+
+CLIENTS=${CLIENTARG:-$CLIENTS}
+if [ -z "$CLIENTS" ]; then usage "No client hosts listed after --"; fi
+BROKERS=${BROKERARG:-$BROKERS}
+if [ -z "$BROKERS" ]; then usage "No brokers specified"; fi
+
+PERFTEST="$TESTDIR/perftest $ARGS"
+
+CLIENTS=($CLIENTS)
+BROKERS=($BROKERS)
+start() {
+ CLIENT=${CLIENTS[i % ${#CLIENTS[*]}]}
+ BROKER=${BROKERS[i % ${#BROKERS[*]}]}
+ ARGS="$* --broker $BROKER"
+ cmd="ssh -n $CLIENT $PERFTEST $ARGS"
+ test -z "$QUIET" && echo "Client $i: $cmd"
+ $cmd &
+}
+
+$PERFTEST --setup -b ${BROKERS[0]}
+for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done
+for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done
+$PERFTEST --control -b ${BROKERS[0]}
diff --git a/qpid/cpp/src/tests/policy.acl b/qpid/cpp/src/tests/policy.acl
new file mode 100644
index 0000000000..ef46026555
--- /dev/null
+++ b/qpid/cpp/src/tests/policy.acl
@@ -0,0 +1 @@
+acl allow all all
diff --git a/qpid/cpp/src/tests/publish.cpp b/qpid/cpp/src/tests/publish.cpp
new file mode 100644
index 0000000000..3f456e7588
--- /dev/null
+++ b/qpid/cpp/src/tests/publish.cpp
@@ -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.
+ *
+ */
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+typedef vector<string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ uint size;
+ uint count;
+ bool durable;
+ string destination;
+ string routingKey;
+ bool summary;
+ bool id;
+
+ Args() : size(256), count(1000), durable(true), routingKey("publish-consume"), summary(false), id(false) {
+ addOptions()
+ ("size", optValue(size, "N"), "message size")
+ ("count", optValue(count, "N"), "number of messages to publish")
+ ("durable", optValue(durable, "yes|no"), "use durable messages")
+ ("destination", optValue(destination, "<exchange name>"), "destination to publish to")
+ ("routing-key", optValue(routingKey, "<key>"), "routing key to publish with")
+ ("summary,s", optValue(summary), "Output only the rate.")
+ ("id", optValue(id), "Add unique correlation ID");
+ }
+};
+
+Args opts;
+
+struct Client
+{
+ Connection connection;
+ AsyncSession session;
+
+ Client()
+ {
+ opts.open(connection);
+ session = connection.newSession();
+ }
+
+ // Cheap hex calculation, avoid expensive ostrstream and string
+ // creation to generate correlation ids in message loop.
+ char hex(char i) { return i<10 ? '0'+i : 'A'+i-10; }
+ void hex(char i, string& s) {
+ s[0]=hex(i>>24); s[1]=hex(i>>16); s[2]=hex(i>>8); s[3]=i;
+ }
+
+ void publish()
+ {
+ AbsTime begin=now();
+ Message msg(string(opts.size, 'X'), opts.routingKey);
+ string correlationId = "0000";
+ if (opts.durable)
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+
+ for (uint i = 0; i < opts.count; i++) {
+ if (opts.id) {
+ hex(i+1, correlationId);
+ msg.getMessageProperties().setCorrelationId(correlationId);
+ }
+ session.messageTransfer(arg::destination=opts.destination,
+ arg::content=msg,
+ arg::acceptMode=1);
+ }
+ session.sync();
+ AbsTime end=now();
+ double secs(double(Duration(begin,end))/TIME_SEC);
+ if (opts.summary) cout << opts.count/secs << endl;
+ else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl;
+ }
+
+ ~Client()
+ {
+ try{
+ session.close();
+ connection.close();
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Client client;
+ client.publish();
+ return 0;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests
new file mode 100755
index 0000000000..0216b5ca7b
--- /dev/null
+++ b/qpid/cpp/src/tests/python_tests
@@ -0,0 +1,29 @@
+#!/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.
+#
+
+# Run the python tests.
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
+QPID_PORT=${QPID_PORT:-5672}
+PYTHON_TESTS=${PYTHON_TESTS:-$*}
+FAILING=${FAILING:-/dev/null}
+
+python $QPID_PYTHON_TEST -m qpid_tests.broker_0_10 -m qpid.tests -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || exit 1
diff --git a/qpid/cpp/src/tests/python_tests.ps1 b/qpid/cpp/src/tests/python_tests.ps1
new file mode 100644
index 0000000000..9f8b9890c4
--- /dev/null
+++ b/qpid/cpp/src/tests/python_tests.ps1
@@ -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.
+#
+
+# Run the python tests; intended to be run by run_test.ps1 which sets up
+# QPID_PORT
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping python tests as python libs not found"
+ exit 1
+}
+
+$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py"
+$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py"
+
+if (Test-Path env:FAILING) {
+ $fails = "-I $env:FAILING"
+}
+if (Test-Path env:PYTHON_TESTS) {
+ $tests = "$env:PYTHON_TESTS"
+}
+else {
+ $tests = "$args"
+}
+
+#cd $PYTHON_DIR
+$env:PYTHONPATH="$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB"
+python $PYTHON_DIR/qpid-python-test -m qpid_tests.broker_0_10 -m qpid.tests -b localhost:$env:QPID_PORT $fails $tests
+exit $LASTEXITCODE
diff --git a/qpid/cpp/src/tests/qpid-build-rinstall b/qpid/cpp/src/tests/qpid-build-rinstall
new file mode 100755
index 0000000000..1a92f8750a
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-build-rinstall
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run "make install"" locally then copy the install tree to each of $HOSTS
+# Must be run in a configured qpid build directory.
+#
+test -f config.status || { echo "Not in a configured build directory."; usage; }
+. src/tests/install_env.sh
+set -ex
+make && make -j1 install
+rsynchosts $QPID_INSTALL_PREFIX
diff --git a/qpid/cpp/src/tests/qpid-client-test.cpp b/qpid/cpp/src/tests/qpid-client-test.cpp
new file mode 100644
index 0000000000..2f5e8e5afe
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-client-test.cpp
@@ -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.
+ *
+ */
+
+/**
+ * This file provides a simple test (and example) of basic
+ * functionality including declaring an exchange and a queue, binding
+ * these together, publishing a message and receiving that message
+ * asynchronously.
+ */
+
+#include <iostream>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public TestOptions {
+ uint msgSize;
+ bool verbose;
+
+ Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26)
+ {
+ addOptions()
+ ("size", optValue(msgSize, "N"), "message size")
+ ("verbose", optValue(verbose), "print out some status messages");
+ }
+};
+
+const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+std::string generateData(uint size)
+{
+ if (size < chars.length()) {
+ return chars.substr(0, size);
+ }
+ std::string data;
+ for (uint i = 0; i < (size / chars.length()); i++) {
+ data += chars;
+ }
+ data += chars.substr(0, size % chars.length());
+ return data;
+}
+
+void print(const std::string& text, const Message& msg)
+{
+ std::cout << text;
+ if (msg.getData().size() > 16) {
+ std::cout << msg.getData().substr(0, 16) << "...";
+ } else {
+ std::cout << msg.getData();
+ }
+ std::cout << std::endl;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ Args opts;
+ opts.parse(argc, argv);
+
+ //Connect to the broker:
+ Connection connection;
+ opts.open(connection);
+ if (opts.verbose) std::cout << "Opened connection." << std::endl;
+
+ //Create and open a session on the connection through which
+ //most functionality is exposed:
+ Session session = connection.newSession();
+ if (opts.verbose) std::cout << "Opened session." << std::endl;
+
+
+ //'declare' the exchange and the queue, which will create them
+ //as they don't exist
+ session.exchangeDeclare(arg::exchange="MyExchange", arg::type="direct");
+ if (opts.verbose) std::cout << "Declared exchange." << std::endl;
+ session.queueDeclare(arg::queue="MyQueue", arg::autoDelete=true, arg::exclusive=true);
+ if (opts.verbose) std::cout << "Declared queue." << std::endl;
+
+ //now bind the queue to the exchange
+ session.exchangeBind(arg::exchange="MyExchange", arg::queue="MyQueue", arg::bindingKey="MyKey");
+ if (opts.verbose) std::cout << "Bound queue to exchange." << std::endl;
+
+ //create and send a message to the exchange using the routing
+ //key we bound our queue with:
+ Message msgOut(generateData(opts.msgSize));
+ msgOut.getDeliveryProperties().setRoutingKey("MyKey");
+ session.messageTransfer(arg::destination="MyExchange", arg::content=msgOut, arg::acceptMode=1);
+ if (opts.verbose) print("Published message: ", msgOut);
+
+ // Using the SubscriptionManager, get the message from the queue.
+ SubscriptionManager subs(session);
+ Message msgIn = subs.get("MyQueue");
+ if (msgIn.getData() == msgOut.getData())
+ if (opts.verbose) std::cout << "Received the exepected message." << std::endl;
+
+ //close the session & connection
+ session.close();
+ if (opts.verbose) std::cout << "Closed session." << std::endl;
+ connection.close();
+ if (opts.verbose) std::cout << "Closed connection." << std::endl;
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/qpid-cluster-benchmark b/qpid/cpp/src/tests/qpid-cluster-benchmark
new file mode 100755
index 0000000000..ff787a46dd
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-cluster-benchmark
@@ -0,0 +1,58 @@
+#!/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.
+#
+
+# Benchmark script for comparing cluster performance.
+
+# Default values
+PORT="5672"
+COUNT=10000
+FLOW=100 # Flow control limit on queue depth for latency.
+REPEAT=10
+QUEUES=4
+CLIENTS=3
+
+while getopts "p:c:f:r:t:b:q:c" opt; do
+ case $opt in
+ p) PORT=$OPTARG;;
+ c) COUNT=$OPTARG;;
+ f) FLOW=$OPTARG;;
+ r) REPEAT=$OPTARG;;
+ s) SCALE=$OPTARG;;
+ b) BROKERS=$OPTARG;;
+ q) QUEUES=$OPTARG;;
+ c) CLIENTS=$OPTARG;;
+ *) echo "Unknown option"; exit 1;;
+ esac
+done
+
+BROKERS=${BROKERS:-$(echo $HOSTS | sed "s/\>/:$PORT/g;s/ /,/g")} # Broker URL list
+BROKER=`echo $BROKERS | awk -F, '{print $1}'` # First broker
+
+run_test() { echo $*; shift; "$@"; echo; echo; echo; }
+
+# Multiple pubs/subs connect via multiple brokers (active-active)
+run_test "multi-host-thruput" qpid-cpp-benchmark --repeat $REPEAT -b $BROKERS --no-timestamp --summarize -q$QUEUES -s$CLIENTS -r$CLIENTS -m $COUNT
+
+# Multiple pubs/subs connect via single broker (active-passive)
+run_test "single-host-thruput" qpid-cpp-benchmark --repeat $REPEAT -b $BROKER --no-timestamp --summarize -q$QUEUES -s$CLIENTS -r$CLIENTS -m $COUNT
+
+# Latency
+run_test "latency" qpid-cpp-benchmark --repeat $REPEAT -b $BROKER --connection-options '{tcp-nodelay:true}' -m $COUNT --flow-control $FLOW
+
diff --git a/qpid/cpp/src/tests/qpid-cluster-lag.py b/qpid/cpp/src/tests/qpid-cluster-lag.py
new file mode 100755
index 0000000000..5b24353241
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-cluster-lag.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""%prog [options] broker...
+Check for brokers that lag behind other brokers in a cluster."""
+
+import os, os.path, sys, socket, time, re
+from qpid.messaging import *
+from optparse import OptionParser
+from threading import Thread
+
+class Browser(Thread):
+ def __init__(self, broker, queue, timeout):
+ Thread.__init__(self)
+ self.broker = broker
+ self.queue = queue
+ self.timeout = timeout
+ self.error = None
+ self.time = None
+
+ def run(self):
+ try:
+ self.connection = Connection(self.broker)
+ self.connection.open()
+ self.session = self.connection.session()
+ self.receiver = self.session.receiver("%s;{mode:browse}"%self.queue)
+ self.msg = self.receiver.fetch(timeout=self.timeout)
+ self.time = time.time()
+ if (self.msg.content != self.queue):
+ raise Exception("Wrong message content, expected '%s' found '%s'"%
+ (self.queue, self.msg.content))
+ except Empty:
+ self.error = "No message on queue %s"%self.queue
+ except Exception, e:
+ self.error = "Error: %s"%e
+
+def main(argv):
+ op = OptionParser(usage=__doc__)
+ op.add_option("--timeout", type="float", default=None, metavar="TIMEOUT",
+ help="Give up after TIMEOUT milliseconds, default never timeout")
+ (opts, args) = op.parse_args(argv)
+ if (len(args) <= 1): op.error("No brokers were specified")
+ brokers = args[1:]
+
+ # Put a message on a uniquely named queue.
+ queue = "%s:%s:%s"%(os.path.basename(args[0]), socket.gethostname(), os.getpid())
+ connection = Connection(brokers[0])
+ connection.open()
+ session = connection.session()
+ sender = session.sender(
+ "%s;{create:always,delete:always,node:{durable:False}}"%queue)
+ sender.send(Message(content=queue))
+ start = time.time()
+ # Browse for the message on each broker
+ if opts.timeout: opts.timeout
+ threads = [Browser(b, queue, opts.timeout) for b in brokers]
+ for t in threads: t.start()
+ delays=[]
+
+ for t in threads:
+ t.join()
+ if t.error:
+ delay=t.error
+ else:
+ delay = t.time-start
+ delays.append([delay, t.broker])
+ print "%s: %s"%(t.broker,delay)
+ if delays:
+ delays.sort()
+ print "lag: %s (%s-%s)"%(delays[-1][0] - delays[0][0], delays[-1][1], delays[0][1])
+ # Clean up
+ sender.close()
+ session.close()
+ connection.close()
+
+if __name__ == "__main__": sys.exit(main(sys.argv))
diff --git a/qpid/cpp/src/tests/qpid-cpp-benchmark b/qpid/cpp/src/tests/qpid-cpp-benchmark
new file mode 100755
index 0000000000..6138108558
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-cpp-benchmark
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import optparse, time, qpid.messaging, re
+from threading import Thread
+from subprocess import Popen, PIPE, STDOUT
+
+op = optparse.OptionParser(usage="usage: %prog [options]",
+ description="simple performance benchmarks")
+op.add_option("-b", "--broker", default=[], action="append", type="str",
+ help="url of broker(s) to connect to, round robin on multiple brokers")
+op.add_option("-c", "--client-host", default=[], action="append", type="str",
+ help="host(s) to run clients on via ssh, round robin on mulple hosts")
+op.add_option("-q", "--queues", default=1, type="int", metavar="N",
+ help="create N queues (default %default)")
+op.add_option("-s", "--senders", default=1, type="int", metavar="N",
+ help="start N senders per queue (default %default)")
+op.add_option("-r", "--receivers", default=1, type="int", metavar="N",
+ help="start N receivers per queue (default %default)")
+op.add_option("-m", "--messages", default=100000, type="int", metavar="N",
+ help="send N messages per sender (default %default)")
+op.add_option("--queue-name", default="benchmark", metavar="NAME",
+ help="base name for queues (default %default)")
+op.add_option("--send-rate", default=0, metavar="N",
+ help="send rate limited to N messages/second, 0 means no limit (default %default)")
+op.add_option("--receive-rate", default=0, metavar="N",
+ help="receive rate limited to N messages/second, 0 means no limit (default %default)")
+op.add_option("--content-size", default=1024, type="int", metavar="BYTES",
+ help="message size in bytes (default %default)")
+op.add_option("--ack-frequency", default=100, metavar="N", type="int",
+ help="receiver ack's every N messages, 0 means unconfirmed (default %default)")
+op.add_option("--no-report-header", dest="report_header", default=True,
+ action="store_false", help="don't print header on report")
+op.add_option("--summarize", default=False, action="store_true",
+ help="print summary statistics for multiple senders/receivers: total throughput, average latency")
+op.add_option("--repeat", default=1, metavar="N", help="repeat N times", type="int")
+op.add_option("--send-option", default=[], action="append", type="str",
+ help="Additional option for sending addresses")
+op.add_option("--receive-option", default=[], action="append", type="str",
+ help="Additional option for receiving addresses")
+op.add_option("--send-arg", default=[], action="append", type="str",
+ help="Additional argument for qpid-send")
+op.add_option("--receive-arg", default=[], action="append", type="str",
+ help="Additional argument for qpid-receive")
+op.add_option("--no-timestamp", dest="timestamp", default=True,
+ action="store_false", help="don't add a timestamp, no latency results")
+op.add_option("--connection-options", type="str",
+ help="Connection options for senders & receivers")
+op.add_option("--flow-control", default=0, type="int", metavar="N",
+ help="Flow control each sender to limit queue depth to 2*N. 0 means no flow control.")
+op.add_option("--durable", default=False, action="store_true",
+ help="Use durable queues and messages")
+
+single_quote_re = re.compile("'")
+def posix_quote(string):
+ """ Quote a string for use as an argument in a posix shell"""
+ return "'" + single_quote_re.sub("\\'", string) + "'";
+
+def ssh_command(host, command):
+ """Convert command into an ssh command on host with quoting"""
+ return ["ssh", host] + [posix_quote(arg) for arg in command]
+
+class Clients:
+ def __init__(self): self.clients=[]
+
+ def add(self, client):
+ self.clients.append(client)
+ return client
+
+ def kill(self):
+ for c in self.clients:
+ try: c.kill()
+ except: pass
+
+clients = Clients()
+
+def start_receive(queue, index, opts, ready_queue, broker, host):
+ address_opts=["create:receiver"] + opts.receive_option
+ if opts.durable: address_opts += ["node:{durable:true}"]
+ address="%s;{%s}"%(queue,",".join(address_opts))
+ msg_total=opts.senders*opts.messages
+ messages = msg_total/opts.receivers;
+ if (index < msg_total%opts.receivers): messages += 1
+ if (messages == 0): return None
+ command = ["qpid-receive",
+ "-b", broker,
+ "-a", address,
+ "-m", str(messages),
+ "--forever",
+ "--print-content=no",
+ "--receive-rate", str(opts.receive_rate),
+ "--report-total",
+ "--ack-frequency", str(opts.ack_frequency),
+ "--ready-address", ready_queue,
+ "--report-header=no"
+ ]
+ command += opts.receive_arg
+ if opts.connection_options:
+ command += ["--connection-options",opts.connection_options]
+ if host: command = ssh_command(host, command)
+ return clients.add(Popen(command, stdout=PIPE))
+
+def start_send(queue, opts, broker, host):
+ address="%s;{%s}"%(queue,",".join(opts.send_option))
+ command = ["qpid-send",
+ "-b", broker,
+ "-a", address,
+ "--messages", str(opts.messages),
+ "--content-size", str(opts.content_size),
+ "--send-rate", str(opts.send_rate),
+ "--report-total",
+ "--report-header=no",
+ "--timestamp=%s"%(opts.timestamp and "yes" or "no"),
+ "--sequence=no",
+ "--flow-control", str(opts.flow_control),
+ "--durable", str(opts.durable)
+ ]
+ command += opts.send_arg
+ if opts.connection_options:
+ command += ["--connection-options",opts.connection_options]
+ if host: command = ssh_command(host, command)
+ return clients.add(Popen(command, stdout=PIPE))
+
+def first_line(p):
+ out,err=p.communicate()
+ if p.returncode != 0: raise Exception("Process failed: %s"%(out.strip()))
+ return out.split("\n")[0]
+
+def delete_queues(queues, broker):
+ c = qpid.messaging.Connection(broker)
+ c.open()
+ for q in queues:
+ try:
+ s = c.session()
+ snd = s.sender("%s;{delete:always}"%(q))
+ snd.close()
+ s.sync()
+ except qpid.messaging.exceptions.NotFound: pass # Ignore "no such queue"
+ c.close()
+
+def print_header(timestamp):
+ if timestamp: latency_header="\tl-min\tl-max\tl-avg"
+ else: latency_header=""
+ print "send-tp\t\trecv-tp%s"%latency_header
+
+def parse(parser, lines): # Parse sender/receiver output
+ for l in lines:
+ fn_val = zip(parser, l)
+ return [map(lambda p: p[0](p[1]), zip(parser,line.split())) for line in lines]
+
+def parse_senders(senders):
+ return parse([int],[first_line(p) for p in senders])
+
+def parse_receivers(receivers):
+ return parse([int,float,float,float],[first_line(p) for p in receivers if p])
+
+def print_data(send_stats, recv_stats):
+ for send,recv in map(None, send_stats, recv_stats):
+ line=""
+ if send: line += "%d"%send[0]
+ if recv:
+ line += "\t\t%d"%recv[0]
+ if len(recv) == 4: line += "\t%.2f\t%.2f\t%.2f"%tuple(recv[1:])
+ print line
+
+def print_summary(send_stats, recv_stats):
+ def avg(s): sum(s) / len(s)
+ send_tp = sum([l[0] for l in send_stats])
+ recv_tp = sum([l[0] for l in recv_stats])
+ summary = "%d\t\t%d"%(send_tp, recv_tp)
+ if recv_stats and len(recv_stats[0]) == 4:
+ l_min = sum(l[1] for l in recv_stats)/len(recv_stats)
+ l_max = sum(l[2] for l in recv_stats)/len(recv_stats)
+ l_avg = sum(l[3] for l in recv_stats)/len(recv_stats)
+ summary += "\t%.2f\t%.2f\t%.2f"%(l_min, l_max, l_avg)
+ print summary
+
+
+class ReadyReceiver:
+ """A receiver for ready messages"""
+ def __init__(self, queue, broker):
+ delete_queues([queue], broker)
+ self.connection = qpid.messaging.Connection(broker)
+ self.connection.open()
+ self.receiver = self.connection.session().receiver(
+ "%s;{create:receiver,delete:receiver,node:{durable:false}}"%(queue))
+ self.receiver.session.sync()
+ self.timeout=10
+
+ def wait(self, receivers):
+ try:
+ for i in receivers: self.receiver.fetch(self.timeout)
+ self.connection.close()
+ except qpid.messaging.Empty:
+ for r in receivers:
+ if (r.poll() is not None):
+ out,err=r.communicate()
+ raise Exception("Receiver error: %s"%(out))
+ raise Exception("Timed out waiting for receivers to be ready")
+
+def flatten(l): return sum(map(lambda s: s.split(","), l),[])
+
+class RoundRobin:
+ def __init__(self,items):
+ self.items = items
+ self.index = 0
+
+ def next(self):
+ if not self.items: return None
+ ret = self.items[self.index]
+ self.index = (self.index+1)%len(self.items)
+ return ret
+
+def main():
+ opts, args = op.parse_args()
+ if not opts.broker: opts.broker = ["127.0.0.1"] # Deafult to local broker
+ opts.broker = flatten(opts.broker)
+ opts.client_host = flatten(opts.client_host)
+ brokers = RoundRobin(opts.broker)
+ client_hosts = RoundRobin(opts.client_host)
+ send_out = ""
+ receive_out = ""
+ ready_queue="%s-ready"%(opts.queue_name)
+ queues = ["%s-%s"%(opts.queue_name, i) for i in xrange(opts.queues)]
+ try:
+ for i in xrange(opts.repeat):
+ delete_queues(queues, opts.broker[0])
+ ready_receiver = ReadyReceiver(ready_queue, opts.broker[0])
+ receivers = [start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.receivers)]
+ ready_receiver.wait(filter(None, receivers)) # Wait for receivers to be ready.
+ senders = [start_send(q, opts,brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.senders)]
+ if opts.report_header and i == 0: print_header(opts.timestamp)
+ send_stats=parse_senders(senders)
+ recv_stats=parse_receivers(receivers)
+ if opts.summarize: print_summary(send_stats, recv_stats)
+ else: print_data(send_stats, recv_stats)
+ delete_queues(queues, opts.broker[0])
+ finally: clients.kill() # No strays
+
+if __name__ == "__main__": main()
+
diff --git a/qpid/cpp/src/tests/qpid-ctrl b/qpid/cpp/src/tests/qpid-ctrl
new file mode 100755
index 0000000000..4246c57898
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-ctrl
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import optparse
+from qpid.messaging import *
+from qpid.util import URL
+from qpid.log import enable, DEBUG, WARN
+
+def nameval(st):
+ idx = st.find("=")
+ if idx >= 0:
+ name = st[0:idx]
+ value = st[idx+1:]
+ else:
+ name = st
+ value = None
+ return name, value
+
+def list_map_entries(m):
+ r = ""
+ for t in m:
+ r += "%s=%s " % (t, m[t])
+ return r
+
+def get_qmfv2_result(m):
+ if m.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if m.properties['qmf.opcode'] == '_method_response':
+ return m.content['_arguments']
+ elif m.properties['qmf.opcode'] == '_exception':
+ raise Exception("Error: %s" % list_map_entries(m.content['_values']))
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % m)
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % m)
+
+
+parser = optparse.OptionParser(usage="usage: %prog [options] COMMAND ...",
+ description="Invoke the specified command.")
+parser.add_option("-b", "--broker", default="localhost",
+ help="connect to specified BROKER (default %default)")
+parser.add_option("-c", "--class", dest="qmfclass", default="broker",
+ help="class of object on which command is being invoked (default %default)")
+parser.add_option("-p", "--package", default="org.apache.qpid.broker",
+ help="package of object on which command is being invoked (default %default)")
+parser.add_option("-i", "--id", default="amqp-broker",
+ help="identifier of object on which command is being invoked (default %default)")
+parser.add_option("-a", "--address", default="qmf.default.direct/broker",
+ help="address to send commands to (default %default)")
+parser.add_option("-t", "--timeout", type="float", default=5,
+ help="timeout in seconds to wait for response before exiting (default %default)")
+parser.add_option("-v", dest="verbose", action="store_true",
+ help="enable logging")
+
+opts, args = parser.parse_args()
+
+if opts.verbose:
+ enable("qpid", DEBUG)
+else:
+ enable("qpid", WARN)
+
+if args:
+ command = args.pop(0)
+else:
+ parser.error("command is required")
+
+
+conn = Connection(opts.broker)
+try:
+ conn.open()
+ ssn = conn.session()
+ snd = ssn.sender(opts.address)
+ reply_to = "qmf.default.direct/%s; {node: {type: topic}}" % str(uuid4())
+ rcv = ssn.receiver(reply_to)
+
+ object_name = "%s:%s:%s" % (opts.package, opts.qmfclass, opts.id)
+ method_name = command
+ arguments = {}
+ for a in args:
+ name, val = nameval(a)
+ if val[0] == '{' or val[0] == '[':
+ arguments[name] = eval(val)
+ else:
+ arguments[name] = val
+ content = {
+ "_object_id": {"_object_name": object_name},
+ "_method_name": method_name,
+ "_arguments": arguments
+ }
+ msg = Message(reply_to=reply_to, content=content)
+ msg.properties["x-amqp-0-10.app-id"] = "qmf2"
+ msg.properties["qmf.opcode"] = "_method_request"
+ snd.send(msg)
+
+ try:
+ print list_map_entries(get_qmfv2_result(rcv.fetch(timeout=opts.timeout)))
+ except Empty:
+ print "No response received!"
+ except Exception, e:
+ print e
+except ReceiverError, e:
+ print e
+except KeyboardInterrupt:
+ pass
+
+conn.close()
diff --git a/qpid/cpp/src/tests/qpid-latency-test.cpp b/qpid/cpp/src/tests/qpid-latency-test.cpp
new file mode 100644
index 0000000000..20eb4568f3
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-latency-test.cpp
@@ -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.
+ *
+ */
+
+
+#include <algorithm>
+#include <limits>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include "TestOptions.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Time.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ uint size;
+ uint count;
+ uint rate;
+ bool sync;
+ uint reportFrequency;
+ uint timeLimit;
+ uint concurrentConnections;
+ uint prefetch;
+ uint ack;
+ bool cumulative;
+ bool csv;
+ bool durable;
+ string base;
+ bool singleConnect;
+
+ Args() : size(256), count(1000), rate(0), reportFrequency(1000),
+ timeLimit(0), concurrentConnections(1),
+ prefetch(100), ack(0),
+ durable(false), base("latency-test"), singleConnect(false)
+
+ {
+ addOptions()
+
+ ("size", optValue(size, "N"), "message size")
+ ("concurrentTests", optValue(concurrentConnections, "N"), "number of concurrent test setups, will create another publisher,\
+ subcriber, queue, and connections")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
+ ("count", optValue(count, "N"), "number of messages to send")
+ ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)")
+ ("sync", optValue(sync), "send messages synchronously")
+ ("report-frequency", optValue(reportFrequency, "N"),
+ "number of milliseconds to wait between reports (ignored unless rate specified)")
+ ("time-limit", optValue(timeLimit, "N"),
+ "test duration, in seconds")
+ ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)")
+ ("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)")
+ ("durable", optValue(durable, "yes|no"), "use durable messages")
+ ("csv", optValue(csv), "print stats in csv format (rate,min,max,avg)")
+ ("cumulative", optValue(cumulative), "cumulative stats in csv format")
+ ("queue-base-name", optValue(base, "<name>"), "base name for queues");
+ }
+};
+
+const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+Args opts;
+double c_min, c_avg, c_max;
+Connection globalConnection;
+
+uint64_t current_time()
+{
+ Duration t(EPOCH, now());
+ return t;
+}
+
+struct Stats
+{
+ Mutex lock;
+ uint count;
+ double minLatency;
+ double maxLatency;
+ double totalLatency;
+
+ Stats();
+ void update(double l);
+ void print();
+ void reset();
+};
+
+class Client : public Runnable
+{
+protected:
+ Connection* connection;
+ Connection localConnection;
+ AsyncSession session;
+ Thread thread;
+ string queue;
+
+public:
+ Client(const string& q);
+ virtual ~Client();
+
+ void start();
+ void join();
+ void run();
+ virtual void test() = 0;
+};
+
+class Receiver : public Client, public MessageListener
+{
+ SubscriptionManager mgr;
+ uint count;
+ Stats& stats;
+
+public:
+ Receiver(const string& queue, Stats& stats);
+ void test();
+ void received(Message& msg);
+ Stats getStats();
+ uint getCount() { return count; }
+ void stop() { mgr.stop(); mgr.cancel(queue); }
+};
+
+
+class Sender : public Client
+{
+ string generateData(uint size);
+ void sendByRate();
+ void sendByCount();
+ Receiver& receiver;
+ const string data;
+
+public:
+ Sender(const string& queue, Receiver& receiver);
+ void test();
+};
+
+
+class Test
+{
+ const string queue;
+ Stats stats;
+ Receiver receiver;
+ Sender sender;
+ AbsTime begin;
+
+public:
+ Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {}
+ void start();
+ void join();
+ void report();
+};
+
+
+Client::Client(const string& q) : queue(q)
+{
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
+}
+
+void Client::start()
+{
+ thread = Thread(this);
+}
+
+void Client::join()
+{
+ thread.join();
+}
+
+void Client::run()
+{
+ try{
+ test();
+ } catch(const std::exception& e) {
+ std::cout << "Error in receiver: " << e.what() << std::endl;
+ }
+}
+
+Client::~Client()
+{
+ try{
+ session.close();
+ connection->close();
+ } catch(const std::exception& e) {
+ std::cout << "Error in receiver: " << e.what() << std::endl;
+ }
+}
+
+Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0), stats(s)
+{
+ session.queueDeclare(arg::queue=queue, arg::durable=opts.durable, arg::autoDelete=true);
+ uint msgCount = session.queueQuery(arg::queue=queue).get().getMessageCount();
+ if (msgCount) {
+ std::cout << "Warning: found " << msgCount << " msgs on " << queue << ". Purging..." << std::endl;
+ session.queuePurge(arg::queue=queue);
+ session.sync();
+ }
+ SubscriptionSettings settings;
+ if (opts.prefetch) {
+ settings.autoAck = (opts.ack ? opts.ack : (opts.prefetch / 2));
+ settings.flowControl = FlowControl::messageWindow(opts.prefetch);
+ } else {
+ settings.acceptMode = ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl::unlimited();
+ }
+ mgr.subscribe(*this, queue, settings);
+}
+
+void Receiver::test()
+{
+ mgr.run();
+ mgr.cancel(queue);
+}
+
+void Receiver::received(Message& msg)
+{
+ ++count;
+ uint64_t receivedAt = current_time();
+ uint64_t sentAt = msg.getDeliveryProperties().getTimestamp();
+
+ stats.update(((double) (receivedAt - sentAt)) / TIME_MSEC);
+
+ if (!opts.rate && count >= opts.count) {
+ mgr.stop();
+ }
+}
+
+void Stats::update(double latency)
+{
+ Mutex::ScopedLock l(lock);
+ count++;
+ minLatency = std::min(minLatency, latency);
+ maxLatency = std::max(maxLatency, latency);
+ totalLatency += latency;
+}
+
+Stats::Stats() : count(0), minLatency(std::numeric_limits<double>::max()), maxLatency(0), totalLatency(0) {}
+
+void Stats::print()
+{
+ static bool already_have_stats = false;
+ uint value;
+
+ if (opts.rate)
+ value = opts.rate;
+ else
+ value = opts.count;
+ Mutex::ScopedLock l(lock);
+ double aux_avg = (totalLatency / count);
+ if (!opts.cumulative) {
+ if (!opts.csv) {
+ if (count) {
+ std::cout << "Latency(ms): min=" << minLatency << ", max=" <<
+ maxLatency << ", avg=" << aux_avg;
+ } else {
+ std::cout << "Stalled: no samples for interval";
+ }
+ } else {
+ if (count) {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
+ "," << aux_avg;
+ } else {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
+ ", Stalled";
+ }
+ }
+ } else {
+ if (count) {
+ if (already_have_stats) {
+ c_avg = (c_min + aux_avg) / 2;
+ if (c_min > minLatency) c_min = minLatency;
+ if (c_max < maxLatency) c_max = maxLatency;
+ } else {
+ c_avg = aux_avg;
+ c_min = minLatency;
+ c_max = maxLatency;
+ already_have_stats = true;
+ }
+ std::cout << value << "," << c_min << "," << c_max <<
+ "," << c_avg;
+ } else {
+ std::cout << "Stalled: no samples for interval";
+ }
+ }
+}
+
+void Stats::reset()
+{
+ Mutex::ScopedLock l(lock);
+ count = 0;
+ totalLatency = maxLatency = 0;
+ minLatency = std::numeric_limits<double>::max();
+}
+
+Sender::Sender(const string& q, Receiver& receiver) : Client(q), receiver(receiver), data(generateData(opts.size)) {}
+
+void Sender::test()
+{
+ if (opts.rate) sendByRate();
+ else sendByCount();
+}
+
+void Sender::sendByCount()
+{
+ Message msg(data, queue);
+ if (opts.durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+
+ for (uint i = 0; i < opts.count; i++) {
+ uint64_t sentAt(current_time());
+ msg.getDeliveryProperties().setTimestamp(sentAt);
+ async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
+ if (opts.sync) session.sync();
+ }
+ session.sync();
+}
+
+void Sender::sendByRate()
+{
+ Message msg(data, queue);
+ if (opts.durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+ uint64_t interval = TIME_SEC/opts.rate;
+ int64_t timeLimit = opts.timeLimit * TIME_SEC;
+ uint64_t sent = 0, missedRate = 0;
+ AbsTime start = now();
+ while (true) {
+ AbsTime sentAt=now();
+ msg.getDeliveryProperties().setTimestamp(Duration(EPOCH, sentAt));
+ async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
+ if (opts.sync) session.sync();
+ ++sent;
+ AbsTime waitTill(start, sent*interval);
+ Duration delay(sentAt, waitTill);
+ if (delay < 0)
+ ++missedRate;
+ else
+ sys::usleep(delay / TIME_USEC);
+ if (timeLimit != 0 && Duration(start, now()) > timeLimit) {
+ session.sync();
+ receiver.stop();
+ break;
+ }
+ }
+}
+
+string Sender::generateData(uint size)
+{
+ if (size < chars.length()) {
+ return chars.substr(0, size);
+ }
+ std::string data;
+ for (uint i = 0; i < (size / chars.length()); i++) {
+ data += chars;
+ }
+ data += chars.substr(0, size % chars.length());
+ return data;
+}
+
+
+void Test::start()
+{
+ receiver.start();
+ begin = AbsTime(now());
+ sender.start();
+}
+
+void Test::join()
+{
+ sender.join();
+ receiver.join();
+ AbsTime end = now();
+ Duration time(begin, end);
+ double msecs(time / TIME_MSEC);
+ if (!opts.csv) {
+ std::cout << "Sent " << receiver.getCount() << " msgs through " << queue
+ << " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) ";
+ }
+ stats.print();
+ std::cout << std::endl;
+}
+
+void Test::report()
+{
+ stats.print();
+ std::cout << std::endl;
+ stats.reset();
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ if (opts.cumulative)
+ opts.csv = true;
+
+ Connection localConnection;
+ AsyncSession session;
+
+ boost::ptr_vector<Test> tests(opts.concurrentConnections);
+ for (uint i = 0; i < opts.concurrentConnections; i++) {
+ std::ostringstream out;
+ out << opts.base << "-" << (i+1);
+ tests.push_back(new Test(out.str()));
+ }
+ for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) {
+ i->start();
+ }
+ if (opts.rate && !opts.timeLimit) {
+ while (true) {
+ qpid::sys::usleep(opts.reportFrequency * 1000);
+ //print latency report:
+ for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) {
+ i->report();
+ }
+ }
+ } else {
+ for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) {
+ i->join();
+ }
+ }
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/qpid-perftest.cpp b/qpid/cpp/src/tests/qpid-perftest.cpp
new file mode 100644
index 0000000000..8a5cf05775
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-perftest.cpp
@@ -0,0 +1,746 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "TestOptions.h"
+
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Completion.h"
+#include "qpid/client/Message.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include <iostream>
+#include <sstream>
+#include <numeric>
+#include <algorithm>
+#include <math.h>
+
+
+using namespace std;
+using namespace qpid;
+using namespace client;
+using namespace sys;
+using boost::lexical_cast;
+using boost::bind;
+
+namespace qpid {
+namespace tests {
+
+enum Mode { SHARED, FANOUT, TOPIC };
+const char* modeNames[] = { "shared", "fanout", "topic" };
+
+// istream/ostream ops so Options can read/display Mode.
+istream& operator>>(istream& in, Mode& mode) {
+ string s;
+ in >> s;
+ int i = find(modeNames, modeNames+3, s) - modeNames;
+ if (i >= 3) throw Exception("Invalid mode: "+s);
+ mode = Mode(i);
+ return in;
+}
+
+ostream& operator<<(ostream& out, Mode mode) {
+ return out << modeNames[mode];
+}
+
+
+struct Opts : public TestOptions {
+
+ // Actions
+ bool setup, control, publish, subscribe;
+
+ // Queue policy
+ uint32_t queueMaxCount;
+ uint64_t queueMaxSize;
+ std::string baseName;
+ bool queueDurable;
+
+ // Publisher
+ size_t pubs;
+ size_t count ;
+ size_t size;
+ bool confirm;
+ bool durable;
+ bool uniqueData;
+ bool syncPub;
+
+ // Subscriber
+ size_t subs;
+ size_t ack;
+
+ // General
+ size_t qt;
+ bool singleConnect;
+ size_t iterations;
+ Mode mode;
+ bool summary;
+ uint32_t intervalSub;
+ uint32_t intervalPub;
+ size_t tx;
+ size_t txPub;
+ size_t txSub;
+ bool commitAsync;
+
+ static const std::string helpText;
+
+ Opts() :
+ TestOptions(helpText),
+ setup(false), control(false), publish(false), subscribe(false), baseName("qpid-perftest"),
+ pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false),
+ subs(1), ack(0),
+ qt(1),singleConnect(false), iterations(1), mode(SHARED), summary(false),
+ intervalSub(0), intervalPub(0), tx(0), txPub(0), txSub(0), commitAsync(false)
+ {
+ addOptions()
+ ("setup", optValue(setup), "Create shared queues.")
+ ("control", optValue(control), "Run test, print report.")
+ ("publish", optValue(publish), "Publish messages.")
+ ("subscribe", optValue(subscribe), "Subscribe for messages.")
+
+ ("mode", optValue(mode, "shared|fanout|topic"), "Test mode."
+ "\nshared: --qt queues, --npubs publishers and --nsubs subscribers per queue.\n"
+ "\nfanout: --npubs publishers, --nsubs subscribers, fanout exchange."
+ "\ntopic: --qt topics, --npubs publishers and --nsubs subscribers per topic.\n")
+
+ ("npubs", optValue(pubs, "N"), "Create N publishers.")
+ ("count", optValue(count, "N"), "Each publisher sends N messages.")
+ ("size", optValue(size, "BYTES"), "Size of messages in bytes.")
+ ("pub-confirm", optValue(confirm, "yes|no"), "Publisher use confirm-mode.")
+ ("durable", optValue(durable, "yes|no"), "Publish messages as durable.")
+ ("unique-data", optValue(uniqueData, "yes|no"), "Make data for each message unique.")
+ ("sync-publish", optValue(syncPub, "yes|no"), "Wait for confirmation of each message before sending the next one.")
+
+ ("nsubs", optValue(subs, "N"), "Create N subscribers.")
+ ("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n"
+ "N==0: Subscriber uses unconfirmed mode")
+
+ ("qt", optValue(qt, "N"), "Create N queues or topics.")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
+
+ ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.")
+ ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec")
+
+ ("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'")
+ ("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'")
+ ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics")
+ ("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)")
+
+ ("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume")
+ ("interval_pub", optValue(intervalPub, "ms"), ">=0 delay between msg publish")
+
+ ("tx", optValue(tx, "N"), "if non-zero, the transaction batch size for publishing and consuming")
+ ("pub-tx", optValue(txPub, "N"), "if non-zero, the transaction batch size for publishing")
+ ("async-commit", optValue(commitAsync, "yes|no"), "Don't wait for completion of commit")
+ ("sub-tx", optValue(txSub, "N"), "if non-zero, the transaction batch size for consuming");
+ }
+
+ // Computed values
+ size_t totalPubs;
+ size_t totalSubs;
+ size_t transfers;
+ size_t subQuota;
+
+ void parse(int argc, char** argv) {
+ TestOptions::parse(argc, argv);
+ switch (mode) {
+ case SHARED:
+ if (count % subs) {
+ count += subs - (count % subs);
+ cout << "WARNING: Adjusted --count to " << count
+ << " the nearest multiple of --nsubs" << endl;
+ }
+ totalPubs = pubs*qt;
+ totalSubs = subs*qt;
+ subQuota = (pubs*count)/subs;
+ break;
+ case FANOUT:
+ if (qt != 1) cerr << "WARNING: Fanout mode, ignoring --qt="
+ << qt << endl;
+ qt=1;
+ totalPubs = pubs;
+ totalSubs = subs;
+ subQuota = totalPubs*count;
+ break;
+ case TOPIC:
+ totalPubs = pubs*qt;
+ totalSubs = subs*qt;
+ subQuota = pubs*count;
+ break;
+ }
+ transfers=(totalPubs*count) + (totalSubs*subQuota);
+ if (tx) {
+ if (txPub) {
+ cerr << "WARNING: Using overriden tx value for publishers: " << txPub << std::endl;
+ } else {
+ txPub = tx;
+ }
+ if (txSub) {
+ cerr << "WARNING: Using overriden tx value for subscribers: " << txSub << std::endl;
+ } else {
+ txSub = tx;
+ }
+ }
+ }
+};
+
+const std::string Opts::helpText=
+"There are two ways to use qpid-perftest: single process or multi-process.\n\n"
+"If none of the --setup, --publish, --subscribe or --control options\n"
+"are given qpid-perftest will run a single-process test.\n"
+"For a multi-process test first run:\n"
+" qpid-perftest --setup <other options>\n"
+"and wait for it to complete. The remaining process should run concurrently::\n"
+"Run --npubs times: qpid-perftest --publish <other options>\n"
+"Run --nsubs times: qpid-perftest --subscribe <other options>\n"
+"Run once: qpid-perftest --control <other options>\n"
+"Note the <other options> must be identical for all processes.\n";
+
+Opts opts;
+Connection globalConnection;
+
+std::string fqn(const std::string& name)
+{
+ ostringstream fqn;
+ fqn << opts.baseName << "_" << name;
+ return fqn.str();
+}
+
+struct Client : public Runnable {
+ Connection* connection;
+ Connection localConnection;
+ AsyncSession session;
+ Thread thread;
+
+ Client() {
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
+ }
+
+ ~Client() {
+ try {
+ if (connection->isOpen()) {
+ session.close();
+ connection->close();
+ }
+ } catch (const std::exception& e) {
+ std::cerr << "Error in shutdown: " << e.what() << std::endl;
+ }
+ }
+};
+
+struct Setup : public Client {
+
+ void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) {
+ session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings);
+ session.queuePurge(arg::queue=name);
+ session.sync();
+ }
+
+ void run() {
+ queueInit(fqn("pub_start"));
+ queueInit(fqn("pub_done"));
+ queueInit(fqn("sub_ready"));
+ queueInit(fqn("sub_done"));
+ if (opts.iterations > 1) queueInit(fqn("sub_iteration"));
+ if (opts.mode==SHARED) {
+ framing::FieldTable settings;//queue policy settings
+ settings.setInt("qpid.max_count", opts.queueMaxCount);
+ settings.setInt("qpid.max_size", opts.queueMaxSize);
+ for (size_t i = 0; i < opts.qt; ++i) {
+ ostringstream qname;
+ qname << opts.baseName << i;
+ queueInit(qname.str(), opts.durable || opts.queueDurable, settings);
+ }
+ }
+ }
+};
+
+void expect(string actual, string expect) {
+ if (expect != actual)
+ throw Exception("Expecting "+expect+" but received "+actual);
+
+}
+
+double secs(Duration d) { return double(d)/TIME_SEC; }
+double secs(AbsTime start, AbsTime finish) {
+ return secs(Duration(start,finish));
+}
+
+
+// Collect rates & print stats.
+class Stats {
+ vector<double> values;
+ double sum;
+
+ public:
+ Stats() : sum(0) {}
+
+ // Functor to collect rates.
+ void operator()(const string& data) {
+ try {
+ double d=lexical_cast<double>(data);
+ values.push_back(d);
+ sum += d;
+ } catch (const std::exception&) {
+ throw Exception("Bad report: "+data);
+ }
+ }
+
+ double mean() const {
+ return sum/values.size();
+ }
+
+ double stdev() const {
+ if (values.size() <= 1) return 0;
+ double avg = mean();
+ double ssq = 0;
+ for (vector<double>::const_iterator i = values.begin();
+ i != values.end(); ++i) {
+ double x=*i;
+ x -= avg;
+ ssq += x*x;
+ }
+ return sqrt(ssq/(values.size()-1));
+ }
+
+ ostream& print(ostream& out) {
+ ostream_iterator<double> o(out, "\n");
+ copy(values.begin(), values.end(), o);
+ out << "Average: " << mean();
+ if (values.size() > 1)
+ out << " (std.dev. " << stdev() << ")";
+ return out << endl;
+ }
+};
+
+
+// Manage control queues, collect and print reports.
+struct Controller : public Client {
+
+ SubscriptionManager subs;
+
+ Controller() : subs(session) {}
+
+ /** Process messages from queue by applying a functor. */
+ void process(size_t n, string queue,
+ boost::function<void (const string&)> msgFn)
+ {
+ if (!opts.summary)
+ cout << "Processing " << n << " messages from "
+ << queue << " " << flush;
+ LocalQueue lq;
+ subs.setFlowControl(n, SubscriptionManager::UNLIMITED, false);
+ subs.subscribe(lq, queue);
+ for (size_t i = 0; i < n; ++i) {
+ if (!opts.summary) cout << "." << flush;
+ msgFn(lq.pop().getData());
+ }
+ if (!opts.summary) cout << " done." << endl;
+ }
+
+ void process(size_t n, LocalQueue lq, string queue,
+ boost::function<void (const string&)> msgFn)
+ {
+ session.messageFlow(queue, 0, n);
+ if (!opts.summary)
+ cout << "Processing " << n << " messages from "
+ << queue << " " << flush;
+ for (size_t i = 0; i < n; ++i) {
+ if (!opts.summary) cout << "." << flush;
+ msgFn(lq.pop().getData());
+ }
+ if (!opts.summary) cout << " done." << endl;
+ }
+
+ void send(size_t n, string queue, string data) {
+ if (!opts.summary)
+ cout << "Sending " << data << " " << n << " times to " << queue
+ << endl;
+ Message msg(data, queue);
+ for (size_t i = 0; i < n; ++i)
+ session.messageTransfer(arg::content=msg, arg::acceptMode=1);
+ }
+
+ void run() { // Controller
+ try {
+ // Wait for subscribers to be ready.
+ process(opts.totalSubs, fqn("sub_ready"), bind(expect, _1, "ready"));
+
+ LocalQueue pubDone;
+ LocalQueue subDone;
+ subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false);
+ subs.subscribe(pubDone, fqn("pub_done"));
+ subs.subscribe(subDone, fqn("sub_done"));
+
+ double txrateTotal(0);
+ double mbytesTotal(0);
+ double pubRateTotal(0);
+ double subRateTotal(0);
+
+ for (size_t j = 0; j < opts.iterations; ++j) {
+ AbsTime start=now();
+ send(opts.totalPubs, fqn("pub_start"), "start"); // Start publishers
+ if (j) {
+ send(opts.totalPubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
+ }
+
+ Stats pubRates;
+ Stats subRates;
+
+ process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates));
+ process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates));
+
+ AbsTime end=now();
+ double time=secs(start, end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
+ double txrate=opts.transfers/time;
+ double mbytes=(txrate*opts.size)/(1024*1024);
+
+ if (!opts.summary) {
+ cout << endl << "Total " << opts.transfers << " transfers of "
+ << opts.size << " bytes in "
+ << time << " seconds." << endl;
+ cout << endl << "Publish transfers/sec: " << endl;
+ pubRates.print(cout);
+ cout << endl << "Subscribe transfers/sec: " << endl;
+ subRates.print(cout);
+ cout << endl
+ << "Total transfers/sec: " << txrate << endl
+ << "Total Mbytes/sec: " << mbytes << endl;
+ }
+ else {
+ cout << pubRates.mean() << "\t"
+ << subRates.mean() << "\t"
+ << txrate << "\t"
+ << mbytes << endl;
+ }
+
+ txrateTotal += txrate;
+ mbytesTotal += mbytes;
+ pubRateTotal += pubRates.mean();
+ subRateTotal += subRates.mean();
+ }
+ if (opts.iterations > 1) {
+ cout << "Averages: "<< endl
+ << (pubRateTotal / opts.iterations) << "\t"
+ << (subRateTotal / opts.iterations) << "\t"
+ << (txrateTotal / opts.iterations) << "\t"
+ << (mbytesTotal / opts.iterations) << endl;
+ }
+ }
+ catch (const std::exception& e) {
+ cout << "Controller exception: " << e.what() << endl;
+ }
+ }
+};
+
+
+struct PublishThread : public Client {
+ string destination;
+ string routingKey;
+
+ PublishThread() {};
+
+ PublishThread(string key, string dest=string()) {
+ destination=dest;
+ routingKey=key;
+ }
+
+ void run() { // Publisher
+ try {
+ string data;
+ size_t offset(0);
+ if (opts.uniqueData) {
+ offset = 5;
+ data += "data:";//marker (requested for latency testing tool scripts)
+ data += string(sizeof(size_t), 'X');//space for seq no
+ data += session.getId().str();
+ if (opts.size > data.size()) {
+ data += string(opts.size - data.size(), 'X');
+ } else if(opts.size < data.size()) {
+ cout << "WARNING: Increased --size to " << data.size()
+ << " to honour --unique-data" << endl;
+ }
+ } else {
+ size_t msgSize=max(opts.size, sizeof(size_t));
+ data = string(msgSize, 'X');
+ }
+
+ Message msg(data, routingKey);
+ if (opts.durable)
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+
+
+ if (opts.txPub){
+ session.txSelect();
+ }
+ SubscriptionManager subs(session);
+ LocalQueue lq;
+ subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true);
+ subs.subscribe(lq, fqn("pub_start"));
+
+ for (size_t j = 0; j < opts.iterations; ++j) {
+ expect(lq.pop().getData(), "start");
+ AbsTime start=now();
+ for (size_t i=0; i<opts.count; i++) {
+ // Stamp the iteration into the message data, avoid
+ // any heap allocation.
+ const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t),
+ reinterpret_cast<const char*>(&i), sizeof(size_t));
+ if (opts.syncPub) {
+ sync(session).messageTransfer(
+ arg::destination=destination,
+ arg::content=msg,
+ arg::acceptMode=1);
+ } else {
+ session.messageTransfer(
+ arg::destination=destination,
+ arg::content=msg,
+ arg::acceptMode=1);
+ }
+ if (opts.txPub && ((i+1) % opts.txPub == 0)){
+ if (opts.commitAsync){
+ session.txCommit();
+ } else {
+ sync(session).txCommit();
+ }
+ }
+ if (opts.intervalPub)
+ qpid::sys::usleep(opts.intervalPub*1000);
+ }
+ if (opts.confirm) session.sync();
+ AbsTime end=now();
+ double time=secs(start,end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
+
+ // Send result to controller.
+ Message report(lexical_cast<string>(opts.count/time), fqn("pub_done"));
+ session.messageTransfer(arg::content=report, arg::acceptMode=1);
+ if (opts.txPub){
+ sync(session).txCommit();
+ }
+ }
+ session.close();
+ }
+ catch (const std::exception& e) {
+ cout << "PublishThread exception: " << e.what() << endl;
+ }
+ }
+};
+
+struct SubscribeThread : public Client {
+
+ string queue;
+
+ SubscribeThread() {}
+
+ SubscribeThread(string q) { queue = q; }
+
+ SubscribeThread(string key, string ex) {
+ queue=session.getId().str(); // Unique name.
+ session.queueDeclare(arg::queue=queue,
+ arg::exclusive=true,
+ arg::autoDelete=true,
+ arg::durable=opts.durable);
+ session.exchangeBind(arg::queue=queue,
+ arg::exchange=ex,
+ arg::bindingKey=key);
+ }
+
+ void verify(bool cond, const char* test, uint32_t expect, uint32_t actual) {
+ if (!cond) {
+ Message error(
+ QPID_MSG("Sequence error: expected n" << test << expect << " but got " << actual),
+ "sub_done");
+ session.messageTransfer(arg::content=error, arg::acceptMode=1);
+ throw Exception(error.getData());
+ }
+ }
+
+ void run() { // Subscribe
+ try {
+ if (opts.txSub) sync(session).txSelect();
+ SubscriptionManager subs(session);
+ SubscriptionSettings settings;
+ settings.autoAck = opts.txSub ? opts.txSub : opts.ack;
+ settings.acceptMode = (opts.txSub || opts.ack ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE);
+ settings.flowControl = FlowControl::messageCredit(opts.subQuota);
+ LocalQueue lq;
+ Subscription subscription = subs.subscribe(lq, queue, settings);
+ // Notify controller we are ready.
+ session.messageTransfer(arg::content=Message("ready", fqn("sub_ready")), arg::acceptMode=1);
+ if (opts.txSub) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+
+ LocalQueue iterationControl;
+ if (opts.iterations > 1) {
+ subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0)));
+ }
+
+ for (size_t j = 0; j < opts.iterations; ++j) {
+ if (j > 0) {
+ //need to wait here until all subs are done
+ session.messageFlow(fqn("sub_iteration"), 0, 1);
+ iterationControl.pop();
+
+ //need to allocate some more credit for subscription
+ session.messageFlow(queue, 0, opts.subQuota);
+ }
+ Message msg;
+ AbsTime start=now();
+ size_t expect=0;
+ for (size_t i = 0; i < opts.subQuota; ++i) {
+ msg=lq.pop();
+ if (opts.txSub && ((i+1) % opts.txSub == 0)) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+ if (opts.intervalSub)
+ qpid::sys::usleep(opts.intervalSub*1000);
+ // TODO aconway 2007-11-23: check message order for.
+ // multiple publishers. Need an array of counters,
+ // one per publisher and a publisher ID in the
+ // message. Careful not to introduce a lot of overhead
+ // here, e.g. no std::map, std::string etc.
+ //
+ // For now verify order only for a single publisher.
+ size_t offset = opts.uniqueData ? 5 /*marker is 'data:'*/ : 0;
+ size_t n = *reinterpret_cast<const size_t*>(msg.getData().data() + offset);
+ if (opts.pubs == 1) {
+ if (opts.subs == 1 || opts.mode == FANOUT) verify(n==expect, "==", expect, n);
+ else verify(n>=expect, ">=", expect, n);
+ expect = n+1;
+ }
+ }
+ if (opts.txSub || opts.ack)
+ subscription.accept(subscription.getUnaccepted());
+ if (opts.txSub) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+ AbsTime end=now();
+
+ // Report to publisher.
+ Message result(lexical_cast<string>(opts.subQuota/secs(start,end)),
+ fqn("sub_done"));
+ session.messageTransfer(arg::content=result, arg::acceptMode=1);
+ if (opts.txSub) sync(session).txCommit();
+ }
+ session.close();
+ }
+ catch (const std::exception& e) {
+ cout << "SubscribeThread exception: " << e.what() << endl;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv) {
+ int exitCode = 0;
+ boost::ptr_vector<Client> subs(opts.subs);
+ boost::ptr_vector<Client> pubs(opts.pubs);
+
+ try {
+ opts.parse(argc, argv);
+
+ string exchange;
+ switch (opts.mode) {
+ case FANOUT: exchange="amq.fanout"; break;
+ case TOPIC: exchange="amq.topic"; break;
+ case SHARED: break;
+ }
+
+ bool singleProcess=
+ (!opts.setup && !opts.control && !opts.publish && !opts.subscribe);
+ if (singleProcess)
+ opts.setup = opts.control = opts.publish = opts.subscribe = true;
+
+ if (opts.setup) Setup().run(); // Set up queues
+
+ // Start pubs/subs for each queue/topic.
+ for (size_t i = 0; i < opts.qt; ++i) {
+ ostringstream key;
+ key << opts.baseName << i; // Queue or topic name.
+ if (opts.publish) {
+ size_t n = singleProcess ? opts.pubs : 1;
+ for (size_t j = 0; j < n; ++j) {
+ pubs.push_back(new PublishThread(key.str(), exchange));
+ pubs.back().thread=Thread(pubs.back());
+ }
+ }
+ if (opts.subscribe) {
+ size_t n = singleProcess ? opts.subs : 1;
+ for (size_t j = 0; j < n; ++j) {
+ if (opts.mode==SHARED)
+ subs.push_back(new SubscribeThread(key.str()));
+ else
+ subs.push_back(new SubscribeThread(key.str(),exchange));
+ subs.back().thread=Thread(subs.back());
+ }
+ }
+ }
+
+ if (opts.control) Controller().run();
+ }
+ catch (const std::exception& e) {
+ cout << endl << e.what() << endl;
+ exitCode = 1;
+ }
+
+ // Wait for started threads.
+ if (opts.publish) {
+ for (boost::ptr_vector<Client>::iterator i=pubs.begin();
+ i != pubs.end();
+ ++i)
+ i->thread.join();
+ }
+
+ if (opts.subscribe) {
+ for (boost::ptr_vector<Client>::iterator i=subs.begin();
+ i != subs.end();
+ ++i)
+ i->thread.join();
+ }
+ return exitCode;
+}
diff --git a/qpid/cpp/src/tests/qpid-ping.cpp b/qpid/cpp/src/tests/qpid-ping.cpp
new file mode 100644
index 0000000000..0cb4afa0ee
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-ping.cpp
@@ -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.
+ *
+
+ */
+
+#include "TestOptions.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/framing/Uuid.h"
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qpid::sys;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid;
+
+struct PingOptions : public qpid::TestOptions {
+ int timeout; // Timeout in seconds.
+ bool quiet; // No output
+ PingOptions() : timeout(1), quiet(false) {
+ addOptions()
+ ("timeout,t", optValue(timeout, "SECONDS"), "Max time to wait.")
+ ("quiet,q", optValue(quiet), "Don't print anything to stderr/stdout.");
+ }
+};
+
+int main(int argc, char** argv) {
+ try {
+ PingOptions opts;
+ opts.parse(argc, argv);
+ opts.con.heartbeat = (opts.timeout+1)/2;
+ Connection connection;
+ opts.open(connection);
+ if (!opts.quiet) cout << "Opened connection." << endl;
+ AsyncSession s = connection.newSession();
+ string qname(Uuid(true).str());
+ s.queueDeclare(arg::queue=qname,arg::autoDelete=true,arg::exclusive=true);
+ s.messageTransfer(arg::content=Message("hello", qname));
+ if (!opts.quiet) cout << "Sent message." << endl;
+ SubscriptionManager subs(s);
+ subs.get(qname);
+ if (!opts.quiet) cout << "Received message." << endl;
+ s.sync();
+ s.close();
+ connection.close();
+ if (!opts.quiet) cout << "Success." << endl;
+ return 0;
+ } catch (const exception& e) {
+ cerr << "Error: " << e.what() << endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-receive.cpp b/qpid/cpp/src/tests/qpid-receive.cpp
new file mode 100644
index 0000000000..9c713e872a
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-receive.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/Options.h>
+#include <qpid/log/Logger.h>
+#include <qpid/log/Options.h>
+#include "qpid/sys/Time.h"
+#include "TestOptions.h"
+#include "Statistics.h"
+
+#include <iostream>
+#include <memory>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string address;
+ std::string connectionOptions;
+ int64_t timeout;
+ bool forever;
+ uint messages;
+ bool ignoreDuplicates;
+ bool checkRedelivered;
+ uint capacity;
+ uint ackFrequency;
+ uint tx;
+ uint rollbackFrequency;
+ bool printContent;
+ bool printHeaders;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ bool reportTotal;
+ uint reportEvery;
+ bool reportHeader;
+ string readyAddress;
+ uint receiveRate;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("amqp:tcp:127.0.0.1"),
+ timeout(0),
+ forever(false),
+ messages(0),
+ ignoreDuplicates(false),
+ checkRedelivered(false),
+ capacity(1000),
+ ackFrequency(100),
+ tx(0),
+ rollbackFrequency(0),
+ printContent(true),
+ printHeaders(false),
+ failoverUpdates(false),
+ log(argv0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true),
+ receiveRate(0)
+ {
+ addOptions()
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to receive from")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("timeout", qpid::optValue(timeout, "TIMEOUT"), "timeout in seconds to wait before exiting")
+ ("forever,f", qpid::optValue(forever), "ignore timeout and wait forever")
+ ("messages,m", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely")
+ ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)")
+ ("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)")
+ ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ ("print-content", qpid::optValue(printContent, "yes|no"), "print out message content")
+ ("print-headers", qpid::optValue(printHeaders, "yes|no"), "print out message headers")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput and latency statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput and latency statistics every N messages.")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ("ready-address", qpid::optValue(readyAddress, "ADDRESS"), "send a message to this address when ready to receive")
+ ("receive-rate", qpid::optValue(receiveRate,"N"), "Receive at rate of N messages/second. 0 means receive as fast as possible.")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ }
+
+ Duration getTimeout()
+ {
+ if (forever) return Duration::FOREVER;
+ else return Duration::SECOND*timeout;
+
+ }
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::ostringstream msg;
+ std::cout << msg << *this << std::endl << std::endl
+ << "Drains messages from the specified address" << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+class SequenceTracker
+{
+ uint lastSn;
+ public:
+ SequenceTracker() : lastSn(0) {}
+
+ bool isDuplicate(Message& message)
+ {
+ uint sn = message.getProperties()[SN];
+ if (lastSn < sn) {
+ lastSn = sn;
+ return false;
+ } else {
+ return true;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Connection connection;
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = opts.tx ? connection.createTransactionalSession() : connection.createSession();
+ Receiver receiver = session.createReceiver(opts.address);
+ receiver.setCapacity(opts.capacity);
+ Message msg;
+ uint count = 0;
+ uint txCount = 0;
+ SequenceTracker sequenceTracker;
+ Duration timeout = opts.getTimeout();
+ bool done = false;
+ Reporter<ThroughputAndLatency> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+ if (!opts.readyAddress.empty())
+ session.createSender(opts.readyAddress).send(msg);
+
+ // For receive rate calculation
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.receiveRate) interval = qpid::sys::TIME_SEC/opts.receiveRate;
+
+ std::map<std::string,Sender> replyTo;
+
+ while (!done && receiver.fetch(msg, timeout)) {
+ reporter.message(msg);
+ if (!opts.ignoreDuplicates || !sequenceTracker.isDuplicate(msg)) {
+ if (msg.getContent() == EOS) {
+ done = true;
+ } else {
+ ++count;
+ if (opts.printHeaders) {
+ if (msg.getSubject().size()) std::cout << "Subject: " << msg.getSubject() << std::endl;
+ if (msg.getReplyTo()) std::cout << "ReplyTo: " << msg.getReplyTo() << std::endl;
+ if (msg.getCorrelationId().size()) std::cout << "CorrelationId: " << msg.getCorrelationId() << std::endl;
+ if (msg.getUserId().size()) std::cout << "UserId: " << msg.getUserId() << std::endl;
+ if (msg.getTtl().getMilliseconds()) std::cout << "TTL: " << msg.getTtl().getMilliseconds() << std::endl;
+ if (msg.getPriority()) std::cout << "Priority: " << msg.getPriority() << std::endl;
+ if (msg.getDurable()) std::cout << "Durable: true" << std::endl;
+ if (msg.getRedelivered()) std::cout << "Redelivered: true" << std::endl;
+ std::cout << "Properties: " << msg.getProperties() << std::endl;
+ std::cout << std::endl;
+ }
+ if (opts.printContent)
+ std::cout << msg.getContent() << std::endl;//TODO: handle map or list messages
+ if (opts.messages && count >= opts.messages) done = true;
+ }
+ } else if (opts.checkRedelivered && !msg.getRedelivered()) {
+ throw qpid::Exception("duplicate sequence number received, message not marked as redelivered!");
+ }
+ if (opts.tx && (count % opts.tx == 0)) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ } else if (opts.ackFrequency && (count % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ if (msg.getReplyTo()) { // Echo message back to reply-to address.
+ Sender& s = replyTo[msg.getReplyTo().str()];
+ if (s.isNull()) {
+ s = session.createSender(msg.getReplyTo());
+ s.setCapacity(opts.capacity);
+ }
+ s.send(msg);
+ }
+ if (opts.receiveRate) {
+ qpid::sys::AbsTime waitTill(start, count*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ // Clear out message properties & content for next iteration.
+ msg = Message(); // TODO aconway 2010-12-01: should be done by fetch
+ }
+ if (opts.reportTotal) reporter.report();
+ if (opts.tx) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ } else {
+ session.acknowledge();
+ }
+ session.close();
+ connection.close();
+ return 0;
+ }
+ } catch(const std::exception& error) {
+ std::cerr << "qpid-receive: " << error.what() << std::endl;
+ connection.close();
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-send.cpp b/qpid/cpp/src/tests/qpid-send.cpp
new file mode 100644
index 0000000000..ef5e98e2a0
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-send.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/sys/Time.h>
+#include <qpid/sys/Monitor.h>
+#include "TestOptions.h"
+#include "Statistics.h"
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+using namespace std;
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+typedef std::vector<std::string> string_vector;
+
+namespace qpid {
+namespace tests {
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string connectionOptions;
+ std::string address;
+ uint messages;
+ std::string id;
+ std::string replyto;
+ uint sendEos;
+ bool durable;
+ uint ttl;
+ uint priority;
+ std::string userid;
+ std::string correlationid;
+ string_vector properties;
+ string_vector entries;
+ std::string contentString;
+ uint contentSize;
+ bool contentStdin;
+ uint tx;
+ uint rollbackFrequency;
+ uint capacity;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ bool reportTotal;
+ uint reportEvery;
+ bool reportHeader;
+ uint sendRate;
+ uint flowControl;
+ bool sequence;
+ bool timestamp;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("amqp:tcp:127.0.0.1"),
+ messages(1),
+ sendEos(0),
+ durable(false),
+ ttl(0),
+ priority(0),
+ contentString(),
+ contentSize(0),
+ contentStdin(false),
+ tx(0),
+ rollbackFrequency(0),
+ capacity(1000),
+ failoverUpdates(false),
+ log(argv0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true),
+ sendRate(0),
+ flowControl(0),
+ sequence(true),
+ timestamp(true)
+ {
+ addOptions()
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to send to")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("messages,m", qpid::optValue(messages, "N"), "stop after N messages have been sent, 0 means no limit")
+ ("id,i", qpid::optValue(id, "ID"), "use the supplied id instead of generating one")
+ ("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address")
+ ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
+ ("durable", qpid::optValue(durable, "yes|no"), "Mark messages as durable.")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("priority", qpid::optValue(priority, "PRIORITY"), "Priority for messages (higher value implies higher priority)")
+ ("property,P", qpid::optValue(properties, "NAME=VALUE"), "specify message property")
+ ("correlation-id", qpid::optValue(correlationid, "ID"), "correlation-id for message")
+ ("user-id", qpid::optValue(userid, "USERID"), "userid for message")
+ ("content-string", qpid::optValue(contentString, "CONTENT"), "use CONTENT as message content")
+ ("content-size", qpid::optValue(contentSize, "N"), "create an N-byte message content")
+ ("content-map,M", qpid::optValue(entries, "NAME=VALUE"), "specify entry for map content")
+ ("content-stdin", qpid::optValue(contentStdin), "read message content from stdin, one line per message")
+ ("capacity", qpid::optValue(capacity, "N"), "size of the senders outgoing message queue")
+ ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.")
+ ("flow-control", qpid::optValue(flowControl,"N"), "Do end to end flow control to limit queue depth to 2*N. 0 means no flow control.")
+ ("sequence", qpid::optValue(sequence, "yes|no"), "Add a sequence number messages property (required for duplicate/lost message detection)")
+ ("timestamp", qpid::optValue(timestamp, "yes|no"), "Add a time stamp messages property (required for latency measurement)")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ }
+
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::ostringstream msg;
+ std::cout << msg << *this << std::endl << std::endl
+ << "Drains messages from the specified address" << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+
+ static bool nameval(const std::string& in, std::string& name, std::string& value)
+ {
+ std::string::size_type i = in.find("=");
+ if (i == std::string::npos) {
+ name = in;
+ return false;
+ } else {
+ name = in.substr(0, i);
+ if (i+1 < in.size()) {
+ value = in.substr(i+1);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ static void setProperty(Message& message, const std::string& property)
+ {
+ std::string name;
+ std::string value;
+ if (nameval(property, name, value)) {
+ message.getProperties()[name] = value;
+ } else {
+ message.getProperties()[name] = Variant();
+ }
+ }
+
+ void setProperties(Message& message) const
+ {
+ for (string_vector::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ setProperty(message, *i);
+ }
+ }
+
+ void setEntries(Variant::Map& content) const
+ {
+ for (string_vector::const_iterator i = entries.begin(); i != entries.end(); ++i) {
+ std::string name;
+ std::string value;
+ if (nameval(*i, name, value)) {
+ content[name] = value;
+ } else {
+ content[name] = Variant();
+ }
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+const string TS("ts");
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+class ContentGenerator {
+ public:
+ virtual ~ContentGenerator() {}
+ virtual bool setContent(Message& msg) = 0;
+};
+
+class GetlineContentGenerator : public ContentGenerator {
+ public:
+ virtual bool setContent(Message& msg) {
+ string content;
+ bool got = getline(std::cin, content);
+ if (got) msg.setContent(content);
+ return got;
+ }
+};
+
+class FixedContentGenerator : public ContentGenerator {
+ public:
+ FixedContentGenerator(const string& s) : content(s) {}
+ virtual bool setContent(Message& msg) {
+ msg.setContent(content);
+ return true;
+ }
+ private:
+ std::string content;
+};
+
+class MapContentGenerator : public ContentGenerator {
+ public:
+ MapContentGenerator(const Options& opt) : opts(opt) {}
+ virtual bool setContent(Message& msg) {
+ Variant::Map map;
+ opts.setEntries(map);
+ encode(map, msg);
+ return true;
+ }
+ private:
+ const Options& opts;
+};
+
+int main(int argc, char ** argv)
+{
+ Connection connection;
+ Options opts;
+ try {
+ if (opts.parse(argc, argv)) {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = opts.tx ? connection.createTransactionalSession() : connection.createSession();
+ Sender sender = session.createSender(opts.address);
+ if (opts.capacity) sender.setCapacity(opts.capacity);
+ Message msg;
+ msg.setDurable(opts.durable);
+ if (opts.ttl) {
+ msg.setTtl(Duration(opts.ttl));
+ }
+ if (opts.priority) {
+ msg.setPriority(opts.priority);
+ }
+ if (!opts.replyto.empty()) {
+ if (opts.flowControl)
+ throw Exception("Can't use reply-to and flow-control together");
+ msg.setReplyTo(Address(opts.replyto));
+ }
+ if (!opts.userid.empty()) msg.setUserId(opts.userid);
+ if (!opts.correlationid.empty()) msg.setCorrelationId(opts.correlationid);
+ opts.setProperties(msg);
+ uint sent = 0;
+ uint txCount = 0;
+ Reporter<Throughput> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+
+ std::auto_ptr<ContentGenerator> contentGen;
+ if (opts.contentStdin) {
+ opts.messages = 0; // Don't limit # messages sent.
+ contentGen.reset(new GetlineContentGenerator);
+ }
+ else if (opts.entries.size() > 0)
+ contentGen.reset(new MapContentGenerator(opts));
+ else if (opts.contentSize > 0)
+ contentGen.reset(new FixedContentGenerator(string(opts.contentSize, 'X')));
+ else
+ contentGen.reset(new FixedContentGenerator(opts.contentString));
+
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
+
+ Receiver flowControlReceiver;
+ Address flowControlAddress("flow-"+Uuid(true).str()+";{create:always,delete:always}");
+ uint flowSent = 0;
+ if (opts.flowControl) {
+ flowControlReceiver = session.createReceiver(flowControlAddress);
+ flowControlReceiver.setCapacity(2);
+ }
+
+ while (contentGen->setContent(msg)) {
+ ++sent;
+ if (opts.sequence)
+ msg.getProperties()[SN] = sent;
+ if (opts.timestamp)
+ msg.getProperties()[TS] = int64_t(
+ qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
+ if (opts.flowControl) {
+ if ((sent % opts.flowControl) == 0) {
+ msg.setReplyTo(flowControlAddress);
+ ++flowSent;
+ }
+ else
+ msg.setReplyTo(Address()); // Clear the reply address.
+ }
+ sender.send(msg);
+ reporter.message(msg);
+
+ if (opts.tx && (sent % opts.tx == 0)) {
+ if (opts.rollbackFrequency &&
+ (++txCount % opts.rollbackFrequency == 0))
+ session.rollback();
+ else
+ session.commit();
+ }
+ if (opts.messages && sent >= opts.messages) break;
+
+ if (opts.flowControl && flowSent == 2) {
+ flowControlReceiver.get(Duration::SECOND);
+ --flowSent;
+ }
+
+ if (opts.sendRate) {
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ }
+ for ( ; flowSent>0; --flowSent)
+ flowControlReceiver.get(Duration::SECOND);
+ if (opts.reportTotal) reporter.report();
+ for (uint i = opts.sendEos; i > 0; --i) {
+ if (opts.sequence)
+ msg.getProperties()[SN] = ++sent;
+ msg.setContent(EOS); //TODO: add in ability to send digest or similar
+ sender.send(msg);
+ }
+ if (opts.tx) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ }
+ session.sync();
+ session.close();
+ connection.close();
+ return 0;
+ }
+ } catch(const std::exception& error) {
+ std::cerr << "qpid-send: " << error.what() << std::endl;
+ connection.close();
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-src-rinstall b/qpid/cpp/src/tests/qpid-src-rinstall
new file mode 100755
index 0000000000..5e69e0ade1
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-src-rinstall
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Copy the source tree and run "make install" on each of $HOSTS
+# Must be run in a configured qpid build directory.
+
+absdir() { echo `cd $1 && pwd`; }
+
+test -f config.status || { echo "Not in a configured build directory."; }
+CONFIGURE=`./config.status -V | grep '^configured by' | sed 's/^configured by \([^,]*\),.*$/\1/'`
+CONFIG_OPTIONS=`./config.status -V | grep 'with options' | sed 's/^.*with options "\([^"]*\)".*$/\1/'`
+set -ex
+rsynchosts `absdir $(dirname $CONFIGURE)/..` # Copy cpp srcdir and siblings.
+allhosts -bo rbuild.log "mkdir -p $PWD && cd $PWD && { test -f config.status || $CONFIGURE $CONFIG_OPTIONS; } && make && make -j1 install"
diff --git a/qpid/cpp/src/tests/qpid-stream.cpp b/qpid/cpp/src/tests/qpid-stream.cpp
new file mode 100644
index 0000000000..f02a484750
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-stream.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/sys/Runnable.h>
+#include <qpid/sys/Thread.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Options.h>
+#include <iostream>
+#include <string>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ std::string url;
+ std::string address;
+ uint size;
+ uint rate;
+ bool durable;
+ uint receiverCapacity;
+ uint senderCapacity;
+ uint ackFrequency;
+
+ Args() :
+ url("amqp:tcp:127.0.0.1:5672"),
+ address("test-queue"),
+ size(512),
+ rate(1000),
+ durable(false),
+ receiverCapacity(0),
+ senderCapacity(0),
+ ackFrequency(1)
+ {
+ addOptions()
+ ("url", qpid::optValue(url, "URL"), "Url to connect to.")
+ ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.")
+ ("size", qpid::optValue(size, "bytes"), "Message size in bytes (content only, not headers).")
+ ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.")
+ ("sender-capacity", qpid::optValue(senderCapacity, "N"), "Credit window (0 implies infinite window)")
+ ("receiver-capacity", qpid::optValue(receiverCapacity, "N"), "Credit window (0 implies infinite window)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"),
+ "Ack frequency (0 implies none of the messages will get accepted)");
+ }
+};
+
+Args opts;
+
+const std::string TS = "ts";
+
+uint64_t timestamp(const qpid::sys::AbsTime& time)
+{
+ qpid::sys::Duration t(qpid::sys::EPOCH, time);
+ return t;
+}
+
+struct Client : qpid::sys::Runnable
+{
+ virtual ~Client() {}
+ virtual void doWork(Session&) = 0;
+
+ void run()
+ {
+ Connection connection(opts.url);
+ try {
+ connection.open();
+ Session session = connection.createSession();
+ doWork(session);
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ connection.close();
+ }
+ }
+
+ qpid::sys::Thread thread;
+
+ void start() { thread = qpid::sys::Thread(this); }
+ void join() { thread.join(); }
+};
+
+struct Publish : Client
+{
+ void doWork(Session& session)
+ {
+ Sender sender = session.createSender(opts.address);
+ if (opts.senderCapacity) sender.setCapacity(opts.senderCapacity);
+ Message msg(std::string(opts.size, 'X'));
+ uint64_t interval = qpid::sys::TIME_SEC / opts.rate;
+ uint64_t sent = 0, missedRate = 0;
+ qpid::sys::AbsTime start = qpid::sys::now();
+ while (true) {
+ qpid::sys::AbsTime sentAt = qpid::sys::now();
+ msg.getProperties()[TS] = timestamp(sentAt);
+ sender.send(msg);
+ ++sent;
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ qpid::sys::Duration delay(sentAt, waitTill);
+ if (delay < 0) {
+ ++missedRate;
+ } else {
+ qpid::sys::usleep(delay / qpid::sys::TIME_USEC);
+ }
+ }
+ }
+};
+
+struct Consume : Client
+{
+ void doWork(Session& session)
+ {
+ Message msg;
+ uint64_t received = 0;
+ double minLatency = std::numeric_limits<double>::max();
+ double maxLatency = 0;
+ double totalLatency = 0;
+ Receiver receiver = session.createReceiver(opts.address);
+ if (opts.receiverCapacity) receiver.setCapacity(opts.receiverCapacity);
+ while (receiver.fetch(msg)) {
+ ++received;
+ if (opts.ackFrequency && (received % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ //calculate latency
+ uint64_t receivedAt = timestamp(qpid::sys::now());
+ uint64_t sentAt = msg.getProperties()[TS].asUint64();
+ double latency = ((double) (receivedAt - sentAt)) / qpid::sys::TIME_MSEC;
+
+ //update avg, min & max
+ minLatency = std::min(minLatency, latency);
+ maxLatency = std::max(maxLatency, latency);
+ totalLatency += latency;
+
+ if (received % opts.rate == 0) {
+ std::cout << "count=" << received
+ << ", avg=" << (totalLatency/received)
+ << ", min=" << minLatency
+ << ", max=" << maxLatency << std::endl;
+ }
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Publish publish;
+ Consume consume;
+ publish.start();
+ consume.start();
+ consume.join();
+ publish.join();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/src/tests/qpid-test-cluster b/qpid/cpp/src/tests/qpid-test-cluster
new file mode 100755
index 0000000000..9887406ef9
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-test-cluster
@@ -0,0 +1,109 @@
+#!/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.
+#
+
+usage() {
+ echo "Usage: `basename $0` [options] start|stop|restart|check [hosts]
+Start/stop/restart a cluster on specified hosts or on \$HOSTS via ssh.
+
+Options:
+ -l USER Run qpidd and copy files as USER.
+ -e SCRIPT Source SCRIPT for environment settings. Copies SCRIPT to each host.
+ Default is $DEFAULT_ENV.
+ -c CONFIG Use CONFIG as qpidd config file. Copies CONFIG to each host.
+ Default is $DEFAULT_CONF
+ -d Delete data-dir and log file before starting broker.
+"
+ exit 1
+}
+
+DEFAULT_CONF=~/qpid-test-qpidd.conf
+DEFAULT_ENV=~/qpid-test-env.sh
+
+test -f $DEFAULT_CONF && CONF_FILE=$DEFAULT_CONF
+test -f $DEFAULT_ENV && ENV_FILE=$DEFAULT_ENV
+
+while getopts "l:e:c:d" opt; do
+ case $opt in
+ l) SSHOPTS="-l$OPTARG $SSHOPTS" ; RSYNC_USER="$OPTARG@" ;;
+ e) ENV_FILE=$OPTARG ;;
+ c) CONF_FILE=$OPTARG ;;
+ d) DO_DELETE=1 ;;
+ *) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+test "$*" || usage
+CMD=$1; shift
+HOSTS=${*:-$HOSTS}
+
+conf_value() { test -f "$CONF_FILE" && awk -F= "/^$1=/ {print \$2}" $CONF_FILE; }
+
+if test -n "$CONF_FILE"; then
+ test -f "$CONF_FILE" || { echo Config file not found: $CONF_FILE; exit 1; }
+ RSYNCFILES="$RSYNCFILES $CONF_FILE"
+ QPIDD_ARGS="$QPIDD_ARGS --config $CONF_FILE"
+ CONF_PORT=`conf_value port`
+ CONF_DATA_DIR=`conf_value data-dir`
+ CONF_LOG_FILE=`conf_value log-to-file`
+fi
+
+if test -n "$ENV_FILE"; then
+ test -f "$ENV_FILE" || { echo Environment file not found: $ENV_FILE; exit 1; }
+ RSYNCFILES="$RSYNCFILES $ENV_FILE"
+ SOURCE_ENV="source $ENV_FILE ; "
+fi
+
+test -n "$RSYNCFILES" && rsynchosts $RSYNCFILES # Copy conf/env files to all hosts
+
+do_start() {
+ for h in $HOSTS; do
+ COMMAND="qpidd -d $QPIDD_ARGS"
+ id -nG | grep '\<ais\>' >/dev/null && COMMAND="sg ais -c '$COMMAND'"
+ if test "$DO_DELETE"; then COMMAND="rm -rf $CONF_DATA_DIR $CONF_LOG_FILE; $COMMAND"; fi
+ ssh $h "$SOURCE_ENV $COMMAND" || { echo "Failed to start on $h"; exit 1; }
+ done
+}
+
+do_stop() {
+ for h in $HOSTS; do
+ ssh $h "$SOURCE_ENV qpidd -q --no-module-dir --no-data-dir $QPIDD_ARGS"
+ done
+}
+
+do_status() {
+ for h in $HOSTS; do
+ if ssh $h "$SOURCE_ENV qpidd -c --no-module-dir --no-data-dir $QPIDD_ARGS > /dev/null"; then
+ echo "$h ok"
+ else
+ echo "$h not running"
+ STATUS=1
+ fi
+ done
+}
+
+case $CMD in
+ start) do_start ;;
+ stop) do_stop ;;
+ restart) do_stop ; do_start ;;
+ status) do_status ;;
+ *) usage;;
+esac
+
+exit $STATUS
diff --git a/qpid/cpp/src/tests/qpid-topic-listener.cpp b/qpid/cpp/src/tests/qpid-topic-listener.cpp
new file mode 100644
index 0000000000..c42e76d760
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-topic-listener.cpp
@@ -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.
+ *
+ */
+
+/**
+ * This file provides one half of a test and example of a pub-sub
+ * style of interaction. See qpid-topic-publisher.cpp for the other half,
+ * in which the logic for publishing is defined.
+ *
+ * This file contains the listener logic. A listener will subscribe to
+ * a logical 'topic'. It will count the number of messages it receives
+ * and the time elapsed between the first one and the last one. It
+ * recognises two types of 'special' message that tell it to (a) send
+ * a report containing this information, (b) shutdown (i.e. stop
+ * listening).
+ */
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/FieldValue.h"
+#include <iostream>
+#include <sstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using namespace qpid::framing;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+/**
+ * A message listener implementation in which the runtime logic is
+ * defined.
+ */
+class Listener : public MessageListener{
+ Session session;
+ SubscriptionManager& mgr;
+ const string responseQueue;
+ const bool transactional;
+ bool init;
+ int count;
+ AbsTime start;
+
+ void shutdown();
+ void report();
+public:
+ Listener(const Session& session, SubscriptionManager& mgr, const string& reponseQueue, bool tx);
+ virtual void received(Message& msg);
+ Subscription subscription;
+};
+
+/**
+ * A utility class for managing the options passed in.
+ */
+struct Args : public qpid::TestOptions {
+ int ack;
+ bool transactional;
+ bool durable;
+ int prefetch;
+ string statusqueue;
+
+ Args() : ack(0), transactional(false), durable(false), prefetch(0) {
+ addOptions()
+ ("ack", optValue(ack, "MODE"), "Ack frequency in messages (defaults to half the prefetch value)")
+ ("transactional", optValue(transactional), "Use transactions")
+ ("durable", optValue(durable), "subscribers should use durable queues")
+ ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)")
+ ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to put status messages on");
+ }
+};
+
+Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) :
+ session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){}
+
+void Listener::received(Message& message){
+ if(!init){
+ start = now();
+ count = 0;
+ init = true;
+ cout << "Batch started." << endl;
+ }
+ string type = message.getHeaders().getAsString("TYPE");
+
+ if(string("TERMINATION_REQUEST") == type){
+ shutdown();
+ }else if(string("REPORT_REQUEST") == type){
+ subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point
+ cout <<"Batch ended, sending report." << endl;
+ //send a report:
+ report();
+ init = false;
+ }else if (++count % 1000 == 0){
+ cout <<"Received " << count << " messages." << endl;
+ }
+}
+
+void Listener::shutdown(){
+ mgr.stop();
+}
+
+void Listener::report(){
+ AbsTime finish = now();
+ Duration time(start, finish);
+ stringstream reportstr;
+ reportstr << "Received " << count << " messages in "
+ << time/TIME_MSEC << " ms.";
+ Message msg(reportstr.str(), responseQueue);
+ msg.getHeaders().setString("TYPE", "REPORT");
+ session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+/**
+ * The main routine creates a Listener instance and sets it up to
+ * consume from a private queue bound to the exchange with the
+ * appropriate topic name.
+ */
+int main(int argc, char** argv){
+ try{
+ Args args;
+ args.parse(argc, argv);
+ if(args.help)
+ cout << args << endl;
+ else {
+ Connection connection;
+ args.open(connection);
+ AsyncSession session = connection.newSession();
+
+ //declare exchange, queue and bind them:
+ session.queueDeclare(arg::queue="response");
+ std::string control = "control_" + session.getId().str();
+ if (args.durable) {
+ session.queueDeclare(arg::queue=control, arg::durable=true);
+ } else {
+ session.queueDeclare(arg::queue=control, arg::exclusive=true, arg::autoDelete=true);
+ }
+ session.exchangeBind(arg::exchange="amq.topic", arg::queue=control, arg::bindingKey="topic_control");
+
+ //set up listener
+ SubscriptionManager mgr(session);
+ Listener listener(session, mgr, "response", args.transactional);
+ SubscriptionSettings settings;
+ if (args.prefetch) {
+ settings.autoAck = (args.ack ? args.ack : (args.prefetch / 2));
+ settings.flowControl = FlowControl::messageCredit(args.prefetch);
+ } else {
+ settings.acceptMode = ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl::unlimited();
+ }
+ listener.subscription = mgr.subscribe(listener, control, settings);
+ session.sync();
+
+ if( args.statusqueue.length() > 0 ) {
+ stringstream msg_str;
+ msg_str << "qpid-topic-listener: " << qpid::sys::SystemInfo::getProcessId();
+ session.messageTransfer(arg::content=Message(msg_str.str(), args.statusqueue));
+ cout << "Ready status put on queue '" << args.statusqueue << "'" << endl;
+ }
+
+ if (args.transactional) {
+ session.txSelect();
+ }
+
+ cout << "qpid-topic-listener: listening..." << endl;
+ mgr.run();
+ if (args.durable) {
+ session.queueDelete(arg::queue=control);
+ }
+ session.close();
+ cout << "closing connection" << endl;
+ connection.close();
+ }
+ return 0;
+ } catch (const std::exception& error) {
+ cout << "qpid-topic-listener: " << error.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/qpid-topic-publisher.cpp b/qpid/cpp/src/tests/qpid-topic-publisher.cpp
new file mode 100644
index 0000000000..f9107b90d0
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-topic-publisher.cpp
@@ -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.
+ *
+ */
+
+/**
+ * This file provides one half of a test and example of a pub-sub
+ * style of interaction. See qpid-topic-listener.cpp for the other half, in
+ * which the logic for subscribers is defined.
+ *
+ * This file contains the publisher logic. The publisher will send a
+ * number of messages to the exchange with the appropriate routing key
+ * for the logical 'topic'. Once it has done this it will then send a
+ * request that each subscriber report back with the number of message
+ * it has received and the time that elapsed between receiving the
+ * first one and receiving the report request. Once the expected
+ * number of reports are received, it sends out a request that each
+ * subscriber shutdown.
+ */
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include <cstdlib>
+#include <iostream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+/**
+ * The publishing logic is defined in this class. It implements
+ * message listener and can therfore be used to receive messages sent
+ * back by the subscribers.
+ */
+class Publisher {
+ AsyncSession session;
+ SubscriptionManager mgr;
+ LocalQueue queue;
+ const string controlTopic;
+ const bool transactional;
+ const bool durable;
+
+ string generateData(int size);
+
+public:
+ Publisher(const AsyncSession& session, const string& controlTopic, bool tx, bool durable);
+ int64_t publish(int msgs, int listeners, int size);
+ void terminate();
+};
+
+/**
+ * A utility class for managing the options passed in to the test
+ */
+struct Args : public TestOptions {
+ int messages;
+ int subscribers;
+ bool transactional;
+ bool durable;
+ int batches;
+ int delay;
+ int size;
+ string statusqueue;
+
+ Args() : messages(1000), subscribers(1),
+ transactional(false), durable(false),
+ batches(1), delay(0), size(256)
+ {
+ addOptions()
+ ("messages", optValue(messages, "N"), "how many messages to send")
+ ("subscribers", optValue(subscribers, "N"), "how many subscribers to expect reports from")
+ ("transactional", optValue(transactional), "client should use transactions")
+ ("durable", optValue(durable), "messages should be durable")
+ ("batches", optValue(batches, "N"), "how many batches to run")
+ ("delay", optValue(delay, "SECONDS"), "Causes a delay between each batch")
+ ("size", optValue(size, "BYTES"), "size of the published messages")
+ ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to read status messages from");
+ }
+};
+
+Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) :
+ session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d)
+{
+ mgr.subscribe(queue, "response");
+}
+
+int64_t Publisher::publish(int msgs, int listeners, int size){
+ Message msg(generateData(size), controlTopic);
+ if (durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+ AbsTime start = now();
+
+ for(int i = 0; i < msgs; i++){
+ session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1);
+ }
+ //send report request
+ Message reportRequest("", controlTopic);
+ reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST");
+ session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+ //wait for a response from each listener (TODO, could log these)
+ for (int i = 0; i < listeners; i++) {
+ Message report = queue.pop();
+ }
+
+ if(transactional){
+ sync(session).txCommit();
+ }
+
+ AbsTime finish = now();
+ return Duration(start, finish);
+}
+
+string Publisher::generateData(int size){
+ string data;
+ for(int i = 0; i < size; i++){
+ data += ('A' + (i / 26));
+ }
+ return data;
+}
+
+void Publisher::terminate(){
+ //send termination request
+ Message terminationRequest("", controlTopic);
+ terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST");
+ session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ session.txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv) {
+ try{
+ Args args;
+ args.parse(argc, argv);
+ if(args.help)
+ cout << args << endl;
+ else {
+ Connection connection;
+ args.open(connection);
+ AsyncSession session = connection.newSession();
+
+ // If status-queue is defined, wait for all expected listeners to join in before we start
+ if( args.statusqueue.length() > 0 ) {
+ cout << "Waiting for " << args.subscribers << " listeners..." << endl;
+ SubscriptionManager statusSubs(session);
+ LocalQueue statusQ;
+ statusSubs.subscribe(statusQ, args.statusqueue);
+ for (int i = 0; i < args.subscribers; i++) {
+ Message m = statusQ.get();
+ if( m.getData().find("topic_listener: ", 0) == 0 ) {
+ cout << "Listener " << (i+1) << " of " << args.subscribers
+ << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16)
+ << ")" << endl;
+ } else {
+ throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData()));
+ }
+ }
+ }
+
+ if (args.transactional) {
+ session.txSelect();
+ }
+ session.queueDeclare(arg::queue="response");
+ session.exchangeBind(arg::exchange="amq.direct", arg::queue="response", arg::bindingKey="response");
+
+ Publisher publisher(session, "topic_control", args.transactional, args.durable);
+
+ int batchSize(args.batches);
+ int64_t max(0);
+ int64_t min(0);
+ int64_t sum(0);
+ for(int i = 0; i < batchSize; i++){
+ if(i > 0 && args.delay) qpid::sys::sleep(args.delay);
+ int64_t msecs =
+ publisher.publish(args.messages,
+ args.subscribers,
+ args.size) / TIME_MSEC;
+ if(!max || msecs > max) max = msecs;
+ if(!min || msecs < min) min = msecs;
+ sum += msecs;
+ cout << "Completed " << (i+1) << " of " << batchSize
+ << " in " << msecs << "ms" << endl;
+ }
+ publisher.terminate();
+ int64_t avg = sum / batchSize;
+ if(batchSize > 1){
+ cout << batchSize << " batches completed. avg=" << avg <<
+ ", max=" << max << ", min=" << min << endl;
+ }
+ session.close();
+ connection.close();
+ }
+ return 0;
+ }catch(exception& error) {
+ cout << error.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/qpid-txtest.cpp b/qpid/cpp/src/tests/qpid-txtest.cpp
new file mode 100644
index 0000000000..d0ba2f1245
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-txtest.cpp
@@ -0,0 +1,340 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/framing/Array.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ bool init, transfer, check;//actions
+ uint size;
+ bool durable;
+ uint queues;
+ string base;
+ uint msgsPerTx;
+ uint txCount;
+ uint totalMsgCount;
+ bool dtx;
+ bool quiet;
+
+ Args() : init(true), transfer(true), check(true),
+ size(256), durable(true), queues(2),
+ base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10),
+ dtx(false), quiet(false)
+ {
+ addOptions()
+
+ ("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.")
+ ("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.")
+ ("check", optValue(check, "yes|no"), "Check that the initial messages are all still available.")
+ ("size", optValue(size, "N"), "message size")
+ ("durable", optValue(durable, "yes|no"), "use durable messages")
+ ("queues", optValue(queues, "N"), "number of queues")
+ ("queue-base-name", optValue(base, "<name>"), "base name for queues")
+ ("messages-per-tx", optValue(msgsPerTx, "N"), "number of messages transferred per transaction")
+ ("tx-count", optValue(txCount, "N"), "number of transactions per 'agent'")
+ ("total-messages", optValue(totalMsgCount, "N"), "total number of messages in 'circulation'")
+ ("dtx", optValue(dtx, "yes|no"), "use distributed transactions")
+ ("quiet", optValue(quiet), "reduce output from test");
+ }
+};
+
+const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+std::string generateData(uint size)
+{
+ if (size < chars.length()) {
+ return chars.substr(0, size);
+ }
+ std::string data;
+ for (uint i = 0; i < (size / chars.length()); i++) {
+ data += chars;
+ }
+ data += chars.substr(0, size % chars.length());
+ return data;
+}
+
+void generateSet(const std::string& base, uint count, StringSet& collection)
+{
+ for (uint i = 0; i < count; i++) {
+ std::ostringstream out;
+ out << base << "-" << (i+1);
+ collection.push_back(out.str());
+ }
+}
+
+Args opts;
+
+struct Client
+{
+ Connection connection;
+ AsyncSession session;
+
+ Client()
+ {
+ opts.open(connection);
+ session = connection.newSession();
+ }
+
+ ~Client()
+ {
+ try{
+ session.close();
+ connection.close();
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ }
+ }
+};
+
+struct Transfer : public Client, public Runnable
+{
+ std::string src;
+ std::string dest;
+ Thread thread;
+ framing::Xid xid;
+
+ Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {}
+
+ void run()
+ {
+ try {
+
+ if (opts.dtx) session.dtxSelect();
+ else session.txSelect();
+ SubscriptionManager subs(session);
+
+ LocalQueue lq;
+ SubscriptionSettings settings(FlowControl::messageWindow(opts.msgsPerTx));
+ settings.autoAck = 0; // Disabled
+ Subscription sub = subs.subscribe(lq, src, settings);
+
+ for (uint t = 0; t < opts.txCount; t++) {
+ Message in;
+ Message out("", dest);
+ if (opts.dtx) {
+ setNewXid(xid);
+ session.dtxStart(arg::xid=xid);
+ }
+ for (uint m = 0; m < opts.msgsPerTx; m++) {
+ in = lq.pop();
+ std::string& data = in.getData();
+ if (data.size() != opts.size) {
+ std::ostringstream oss;
+ oss << "Message size incorrect: size=" << in.getData().size() << "; expected " << opts.size;
+ throw std::runtime_error(oss.str());
+ }
+ out.setData(data);
+ out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId());
+ out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode());
+ session.messageTransfer(arg::content=out, arg::acceptMode=1);
+ }
+ sub.accept(sub.getUnaccepted());
+ if (opts.dtx) {
+ session.dtxEnd(arg::xid=xid);
+ session.dtxPrepare(arg::xid=xid);
+ session.dtxCommit(arg::xid=xid);
+ } else {
+ session.txCommit();
+ }
+ }
+ } catch(const std::exception& e) {
+ std::cout << "Transfer interrupted: " << e.what() << std::endl;
+ }
+ }
+
+ void setNewXid(framing::Xid& xid) {
+ framing::Uuid uuid(true);
+ xid.setGlobalId(uuid.str());
+ }
+};
+
+struct Controller : public Client
+{
+ StringSet ids;
+ StringSet queues;
+
+ Controller()
+ {
+ generateSet(opts.base, opts.queues, queues);
+ generateSet("msg", opts.totalMsgCount, ids);
+ }
+
+ void init()
+ {
+ //declare queues
+ for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
+ session.queueDeclare(arg::queue=*i, arg::durable=opts.durable);
+ session.sync();
+ }
+
+ Message msg(generateData(opts.size), *queues.begin());
+ if (opts.durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+
+ //publish messages
+ for (StringSet::iterator i = ids.begin(); i != ids.end(); i++) {
+ msg.getMessageProperties().setCorrelationId(*i);
+ session.messageTransfer(arg::content=msg, arg::acceptMode=1);
+ }
+ }
+
+ void transfer()
+ {
+ boost::ptr_vector<Transfer> agents(opts.queues);
+ //launch transfer agents
+ for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
+ StringSet::iterator next = i + 1;
+ if (next == queues.end()) next = queues.begin();
+
+ if (!opts.quiet) std::cout << "Transfering from " << *i << " to " << *next << std::endl;
+ agents.push_back(new Transfer(*i, *next));
+ agents.back().thread = Thread(agents.back());
+ }
+
+ for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++) {
+ i->thread.join();
+ }
+ }
+
+ int check()
+ {
+ SubscriptionManager subs(session);
+
+ // Recover DTX transactions (if any)
+ if (opts.dtx) {
+ std::vector<std::string> inDoubtXids;
+ framing::DtxRecoverResult dtxRes = session.dtxRecover().get();
+ const framing::Array& xidArr = dtxRes.getInDoubt();
+ xidArr.collect(inDoubtXids);
+
+ if (inDoubtXids.size()) {
+ if (!opts.quiet) std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl;
+ framing::StructHelper decoder;
+ framing::Xid xid;
+ // abort even, commit odd transactions
+ for (unsigned i = 0; i < inDoubtXids.size(); i++) {
+ decoder.decode(xid, inDoubtXids[i]);
+ if (!opts.quiet) std::cout << (i%2 ? " * aborting " : " * committing ");
+ xid.print(std::cout);
+ std::cout << std::endl;
+ if (i%2) {
+ session.dtxRollback(arg::xid=xid);
+ } else {
+ session.dtxCommit(arg::xid=xid);
+ }
+ }
+ }
+ }
+
+ StringSet drained;
+ //drain each queue and verify the correct set of messages are available
+ for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
+ //subscribe, allocate credit and flushn
+ LocalQueue lq;
+ SubscriptionSettings settings(FlowControl::unlimited(), ACCEPT_MODE_NONE);
+ subs.subscribe(lq, *i, settings);
+ session.messageFlush(arg::destination=*i);
+ session.sync();
+
+ uint count(0);
+ while (!lq.empty()) {
+ Message m = lq.pop();
+ //add correlation ids of received messages to drained
+ drained.push_back(m.getMessageProperties().getCorrelationId());
+ ++count;
+ }
+ if (!opts.quiet) std::cout << "Drained " << count << " messages from " << *i << std::endl;
+ }
+
+ sort(ids.begin(), ids.end());
+ sort(drained.begin(), drained.end());
+
+ //check that drained == ids
+ StringSet missing;
+ set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing));
+
+ StringSet extra;
+ set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra));
+
+ if (missing.empty() && extra.empty()) {
+ std::cout << "All expected messages were retrieved." << std::endl;
+ return 0;
+ } else {
+ if (!missing.empty()) {
+ std::cout << "The following ids were missing:" << std::endl;
+ for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) {
+ std::cout << " '" << *i << "'" << std::endl;
+ }
+ }
+ if (!extra.empty()) {
+ std::cout << "The following extra ids were encountered:" << std::endl;
+ for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) {
+ std::cout << " '" << *i << "'" << std::endl;
+ }
+ }
+ return 1;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Controller controller;
+ if (opts.init) controller.init();
+ if (opts.transfer) controller.transfer();
+ if (opts.check) return controller.check();
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ }
+ return 2;
+}
diff --git a/qpid/cpp/src/tests/queue_flow_limit_tests.py b/qpid/cpp/src/tests/queue_flow_limit_tests.py
new file mode 100644
index 0000000000..dec7cfb3af
--- /dev/null
+++ b/qpid/cpp/src/tests/queue_flow_limit_tests.py
@@ -0,0 +1,371 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+from qpid.testlib import TestBase010
+from qpid import datatypes, messaging
+from qpid.messaging import Message, Empty
+from threading import Thread, Lock
+from logging import getLogger
+from time import sleep, time
+from os import environ, popen
+
+class QueueFlowLimitTests(TestBase010):
+
+ def __getattr__(self, name):
+ if name == "assertGreater":
+ return lambda a, b: self.failUnless(a > b)
+ else:
+ raise AttributeError
+
+ def _create_queue(self, name,
+ stop_count=None, resume_count=None,
+ stop_size=None, resume_size=None,
+ max_size=None, max_count=None):
+ """ Create a queue with the given flow settings via the queue.declare
+ command.
+ """
+ args={}
+ if (stop_count is not None):
+ args["qpid.flow_stop_count"] = stop_count;
+ if (resume_count is not None):
+ args["qpid.flow_resume_count"] = resume_count;
+ if (stop_size is not None):
+ args["qpid.flow_stop_size"] = stop_size;
+ if (resume_size is not None):
+ args["qpid.flow_resume_size"] = resume_size;
+ if (max_size is not None):
+ args["qpid.max_size"] = max_size;
+ if (max_count is not None):
+ args["qpid.max_count"] = max_count;
+
+
+ self.session.queue_declare(queue=name, arguments=args)
+
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == name:
+ # verify flow settings
+ if (stop_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), stop_count)
+ if (resume_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), resume_count)
+ if (stop_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), stop_size)
+ if (resume_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), resume_size)
+ if (max_size is not None):
+ self.assertEqual(i.arguments.get("qpid.max_size"), max_size)
+ if (max_count is not None):
+ self.assertEqual(i.arguments.get("qpid.max_count"), max_count)
+ self.failIf(i.flowStopped)
+ return i.getObjectId()
+ self.fail("Unable to create queue '%s'" % name)
+ return None
+
+
+ def _delete_queue(self, name):
+ """ Delete a named queue
+ """
+ self.session.queue_delete(queue=name)
+
+
+ def _start_qpid_send(self, queue, count, content="X", capacity=100):
+ """ Use the qpid-send client to generate traffic to a queue.
+ """
+ command = "qpid-send" + \
+ " -b" + " %s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --content-string " + str(content) \
+ + " --capacity " + str(capacity)
+ return popen(command)
+
+ def _start_qpid_receive(self, queue, count, timeout=5):
+ """ Use the qpid-receive client to consume from a queue.
+ Note well: prints one line of text to stdout for each consumed msg.
+ """
+ command = "qpid-receive" + \
+ " -b " + "%s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --timeout " + str(timeout) \
+ + " --print-content yes"
+ return popen(command)
+
+ def test_qpid_config_cmd(self):
+ """ Test the qpid-config command's ability to configure a queue's flow
+ control thresholds.
+ """
+ tool = environ.get("QPID_CONFIG_EXEC")
+ if tool:
+ command = tool + \
+ " --broker-addr=%s:%s " % (self.broker.host, self.broker.port) \
+ + "add queue test01 --flow-stop-count=999" \
+ + " --flow-resume-count=55 --flow-stop-size=5000000" \
+ + " --flow-resume-size=100000"
+ cmd = popen(command)
+ rc = cmd.close()
+ self.assertEqual(rc, None)
+
+ # now verify the settings
+ self.startQmf();
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == "test01":
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), 999)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), 55)
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), 5000000)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), 100000)
+ self.failIf(i.flowStopped)
+ break;
+ self.assertEqual(i.name, "test01")
+ self._delete_queue("test01")
+
+
+ def test_flow_count(self):
+ """ Create a queue with count-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_count=373, resume_count=229)
+ self.assertEqual(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount, 0)
+
+ sndr1 = self._start_qpid_send("test-q", count=1213, content="XXX", capacity=50);
+ sndr2 = self._start_qpid_send("test-q", count=797, content="Y", capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=331, content="ZZZZZ", capacity=149);
+ totalMsgs = 1213 + 797 + 331
+
+ # wait until flow control is active
+ deadline = time() + 10
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(depth, 373)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount)
+
+ self._delete_queue("test-q")
+
+
+ def test_flow_size(self):
+ """ Create a queue with size-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_size=351133, resume_size=251143)
+
+ sndr1 = self._start_qpid_send("test-q", count=1699, content="X"*439, capacity=53);
+ sndr2 = self._start_qpid_send("test-q", count=1129, content="Y"*631, capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=881, content="Z"*823, capacity=149);
+ totalMsgs = 1699 + 1129 + 881
+
+ # wait until flow control is active
+ deadline = time() + 10
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.assertGreater(self.qmf.getObjects(_objectId=oid)[0].byteDepth, 351133)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+
+ self._delete_queue("test-q")
+
+
+ def verify_limit(self, testq):
+ """ run a limit check against the testq object
+ """
+
+ testq.mgmt = self.qmf.getObjects(_objectId=testq.oid)[0]
+
+ # fill up the queue, waiting until flow control is active
+ sndr1 = self._start_qpid_send(testq.mgmt.name, count=testq.sendCount, content=testq.content)
+ deadline = time() + 10
+ while (not testq.mgmt.flowStopped) and time() < deadline:
+ testq.mgmt.update()
+
+ self.failUnless(testq.verifyStopped())
+
+ # now consume enough messages to drop below the flow resume point, and
+ # verify flow control is released.
+ rcvr = self._start_qpid_receive(testq.mgmt.name, count=testq.consumeCount)
+ rcvr.readlines() # prints a line for each received msg
+ rcvr.close();
+
+ # we should now be below the resume threshold
+ self.failUnless(testq.verifyResumed())
+
+ self._delete_queue(testq.mgmt.name)
+ sndr1.close();
+
+
+ def test_default_flow_count(self):
+ """ Create a queue with count-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_count == 1000 -> stop == 800, resume == 700
+ self.oid = oid
+ self.sendCount = 1000
+ self.consumeCount = 301 # (send - resume) + 1 to reenable flow
+ self.content = "X"
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.msgDepth > 800)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.msgDepth < 700)
+
+ self.startQmf();
+ oid = self._create_queue("test-X", max_count=1000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_default_flow_size(self):
+ """ Create a queue with byte-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_size == 10000 -> stop == 8000 bytes, resume == 7000 bytes
+ self.oid = oid
+ self.sendCount = 2000
+ self.consumeCount = 601 # (send - resume) + 1 to reenable flow
+ self.content = "XXXXX" # 5 bytes per message sent.
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.byteDepth > 8000)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.byteDepth < 7000)
+
+ self.startQmf();
+ oid = self._create_queue("test-Y", max_size=10000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_blocked_queue_delete(self):
+ """ Verify that blocked senders are unblocked when a queue that is flow
+ controlled is deleted.
+ """
+
+ class BlockedSender(Thread):
+ def __init__(self, tester, queue, count, capacity=10):
+ self.tester = tester
+ self.queue = queue
+ self.count = count
+ self.capacity = capacity
+ Thread.__init__(self)
+ self.done = False
+ self.start()
+ def run(self):
+ # spawn qpid-send
+ p = self.tester._start_qpid_send(self.queue,
+ self.count,
+ self.capacity)
+ p.close() # waits for qpid-send to complete
+ self.done = True
+
+ self.startQmf();
+ oid = self._create_queue("kill-q", stop_size=10, resume_size=2)
+ q = self.qmf.getObjects(_objectId=oid)[0]
+ self.failIf(q.flowStopped)
+
+ sender = BlockedSender(self, "kill-q", count=100)
+ # wait for flow control
+ deadline = time() + 10
+ while (not q.flowStopped) and time() < deadline:
+ q.update()
+
+ self.failUnless(q.flowStopped)
+ self.failIf(sender.done) # sender blocked
+
+ self._delete_queue("kill-q")
+ sender.join(5)
+ self.failIf(sender.isAlive())
+ self.failUnless(sender.done)
+
+
+
+
diff --git a/qpid/cpp/src/tests/quick_perftest b/qpid/cpp/src/tests/quick_perftest
new file mode 100755
index 0000000000..362f9ee96a
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_perftest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_test ./qpid-perftest --summary --count 100
diff --git a/qpid/cpp/src/tests/quick_topictest b/qpid/cpp/src/tests/quick_topictest
new file mode 100755
index 0000000000..0a6b29b33f
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_topictest
@@ -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.
+#
+
+
+# Quick and quiet topic test for make check.
+test -z "$srcdir" && srcdir=`dirname $0`
+$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || {
+ echo $0 FAILED:
+ cat topictest.log
+ exit 1
+}
+rm topictest.log
diff --git a/qpid/cpp/src/tests/quick_topictest.ps1 b/qpid/cpp/src/tests/quick_topictest.ps1
new file mode 100644
index 0000000000..8f5b2caff7
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_topictest.ps1
@@ -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.
+#
+
+# Quick and quiet topic test for make check.
+[string]$me = $myInvocation.InvocationName
+$srcdir = Split-Path $me
+Invoke-Expression "$srcdir\topictest.ps1 -subscribers 2 -messages 2 -batches 1" > topictest.log 2>&1
+if (!$?) {
+ "$me FAILED:"
+ cat topictest.log
+ exit 1
+}
+Remove-Item topictest.log
+exit 0
diff --git a/qpid/cpp/src/tests/quick_txtest b/qpid/cpp/src/tests/quick_txtest
new file mode 100755
index 0000000000..c872fcec12
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_txtest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_test ./qpid-txtest --queues 4 --tx-count 10 --quiet
diff --git a/qpid/cpp/src/tests/receiver.cpp b/qpid/cpp/src/tests/receiver.cpp
new file mode 100644
index 0000000000..f1b462d6e4
--- /dev/null
+++ b/qpid/cpp/src/tests/receiver.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/SubscriptionSettings.h>
+#include "TestOptions.h"
+
+#include <iostream>
+#include <fstream>
+
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string queue;
+ uint messages;
+ bool ignoreDuplicates;
+ uint creditWindow;
+ uint ackFrequency;
+ bool browse;
+
+ Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1), browse(false)
+ {
+ addOptions()
+ ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages")
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely")
+ ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)")
+ ("credit-window", qpid::optValue(creditWindow, "N"), "Credit window (0 implies infinite window)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("browse", qpid::optValue(browse), "Browse rather than consuming");
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+class Receiver : public MessageListener, public FailoverManager::Command
+{
+ public:
+ Receiver(const string& queue, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse);
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ private:
+ const string queue;
+ const uint count;
+ const bool skipDups;
+ SubscriptionSettings settings;
+ Subscription subscription;
+ uint processed;
+ uint lastSn;
+
+ bool isDuplicate(Message& message);
+};
+
+Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) :
+ queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0)
+{
+ if (browse) settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
+ if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow);
+ settings.autoAck = ackFrequency;
+}
+
+void Receiver::received(Message& message)
+{
+ if (!(skipDups && isDuplicate(message))) {
+ bool eos = message.getData() == EOS;
+ if (!eos) std::cout << message.getData() << std::endl;
+ if (eos || ++processed == count) subscription.cancel();
+ }
+}
+
+bool Receiver::isDuplicate(Message& message)
+{
+ uint sn = message.getHeaders().getAsInt(SN);
+ if (lastSn < sn) {
+ lastSn = sn;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void Receiver::execute(AsyncSession& session, bool /*isRetry*/)
+{
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queue, settings);
+ subs.run();
+ if (settings.autoAck) {
+ subscription.accept(subscription.getUnaccepted());
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ Receiver receiver(opts.queue, opts.messages, opts.ignoreDuplicates, opts.creditWindow, opts.ackFrequency, opts.browse);
+ connection.execute(receiver);
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cerr << "Failure: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/reliable_replication_test b/qpid/cpp/src/tests/reliable_replication_test
new file mode 100755
index 0000000000..f57d11a263
--- /dev/null
+++ b/qpid/cpp/src/tests/reliable_replication_test
@@ -0,0 +1,93 @@
+#!/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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+stop_brokers() {
+ if [[ $BROKER_A ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [[ $BROKER_B ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ fi
+}
+
+setup() {
+ rm -f replication-source.log replication-dest.log
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable trace+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd-repl.port
+ BROKER_A=`cat qpidd-repl.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd-repl.port
+ BROKER_B=`cat qpidd-repl.port`
+
+ echo "Testing replication from port $BROKER_A to port $BROKER_B"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ #create test queue (only replicate enqueues for this test):
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a
+}
+
+send() {
+ ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < replicated.expected
+}
+
+receive() {
+ rm -f replicated.actual
+ ./receiver --port $BROKER_B --queue queue-a > replicated.actual
+}
+
+bounce_link() {
+ echo "Destroying link..."
+ $PYTHON_COMMANDS/qpid-route link del "localhost:$BROKER_B" "localhost:$BROKER_A"
+ echo "Link destroyed; recreating route..."
+ sleep 2
+ $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+ echo "Route re-established"
+}
+
+if test -d ${PYTHON_DIR} && test -e $REPLICATING_LISTENER_LIB && test -e $REPLICATION_EXCHANGE_LIB ; then
+ setup
+ for i in `seq 1 100000`; do echo Message $i; done > replicated.expected
+ send &
+ receive &
+ for i in `seq 1 5`; do sleep 10; bounce_link; done;
+ wait
+ #check that received list is identical to sent list
+ diff replicated.actual replicated.expected || FAIL=1
+ if [[ $FAIL ]]; then
+ echo reliable replication test failed: expectations not met!
+ exit 1
+ else
+ echo replication reliable in the face of link failures
+ rm -f replication.actual replication.expected replication-source.log replication-dest.log qpidd-repl.port
+ fi
+fi
+
diff --git a/qpid/cpp/src/tests/replaying_sender.cpp b/qpid/cpp/src/tests/replaying_sender.cpp
new file mode 100644
index 0000000000..a5549bfdf2
--- /dev/null
+++ b/qpid/cpp/src/tests/replaying_sender.cpp
@@ -0,0 +1,165 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/Exception.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(const std::string& queue, uint count, uint reportFreq);
+ void execute(AsyncSession& session, bool isRetry);
+ uint getSent();
+
+ void setVerbosity ( int v ) { verbosity = v; }
+ void setPersistence ( int p ) { persistence = p; }
+
+ private:
+ MessageReplayTracker sender;
+ const uint count;
+ uint sent;
+ const uint reportFrequency;
+ Message message;
+ int verbosity;
+ int persistence;
+ string queueName;
+};
+
+Sender::Sender(const std::string& queue, uint count_, uint reportFreq )
+ : sender(10),
+ count(count_),
+ sent(0),
+ reportFrequency(reportFreq),
+ verbosity(0),
+ persistence(0),
+ queueName ( queue )
+{
+ message.getDeliveryProperties().setRoutingKey(queueName.c_str());
+}
+
+const string SN("sn");
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (verbosity > 0)
+ std::cout << "replaying_sender " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ while (sent < count) {
+ stringstream message_data;
+ message_data << ++sent;
+ message.setData(message_data.str());
+ message.getHeaders().setInt(SN, sent);
+ if ( persistence )
+ message.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+
+ sender.send(message);
+ if (count > reportFrequency && !(sent % reportFrequency)) {
+ if ( verbosity > 0 )
+ std::cout << "Sender sent "
+ << sent
+ << " of "
+ << count
+ << " on queue "
+ << queueName
+ << std::endl;
+ }
+ }
+ message.setData("That's all, folks!");
+ sender.send(message);
+
+ if ( verbosity > 0 )
+ std::cout << "SENDER COMPLETED\n";
+}
+
+uint Sender::getSent()
+{
+ return sent;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 8 )
+ {
+ std::cerr << "Usage: replaying_sender host port n_messages report_frequency verbosity persistence queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int n_messages = atoi(argv[3]);
+ int reportFrequency = atoi(argv[4]);
+ int verbosity = atoi(argv[5]);
+ int persistence = atoi(argv[6]);
+ char * queue_name = argv[7];
+
+ FailoverManager connection(settings);
+ Sender sender(queue_name, n_messages, reportFrequency );
+ sender.setVerbosity ( verbosity );
+ sender.setPersistence ( persistence );
+ try {
+ connection.execute ( sender );
+ if ( verbosity > 0 )
+ {
+ std::cout << "Sender finished. Sent "
+ << sender.getSent()
+ << " messages on queue "
+ << queue_name
+ << endl;
+ }
+ connection.close();
+ return 0;
+ }
+ catch(const std::exception& error)
+ {
+ cerr << "Sender (host: "
+ << settings.host
+ << " port: "
+ << settings.port
+ << " ) "
+ << " Failed: "
+ << error.what()
+ << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/replication_test b/qpid/cpp/src/tests/replication_test
new file mode 100755
index 0000000000..8c37568875
--- /dev/null
+++ b/qpid/cpp/src/tests/replication_test
@@ -0,0 +1,182 @@
+#!/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.
+#
+
+# Run a test of the replication feature
+
+source ./test_env.sh
+
+trap stop_brokers INT TERM QUIT
+
+stop_brokers() {
+ if [ x$BROKER_A != x ]; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [ x$BROKER_B != x ]; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ fi
+}
+
+if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REPLICATION_EXCHANGE_LIB"; then
+ rm -f queue-*.repl replication-*.log #cleanup from any earlier runs
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
+ BROKER_A=`cat qpidd.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+ echo "Running replication test between localhost:$BROKER_A and localhost:$BROKER_B"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ #create test queues
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-b
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-c
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ #queue-d deliberately not declared on DR; this error case should be handled
+
+ #publish and consume from test queues on broker A:
+ i=1
+ while [ $i -le 10 ]; do
+ echo Message $i for A >> queue-a-input.repl
+ i=`expr $i + 1`
+ done
+ i=1
+ while [ $i -le 20 ]; do
+ echo Message $i for B >> queue-b-input.repl
+ i=`expr $i + 1`
+ done
+ i=1
+ while [ $i -le 15 ]; do
+ echo Message $i for C >> queue-c-input.repl
+ i=`expr $i + 1`
+ done
+
+ ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < queue-a-input.repl
+ ./sender --port $BROKER_A --routing-key queue-b --send-eos 1 < queue-b-input.repl
+ ./sender --port $BROKER_A --routing-key queue-c --send-eos 1 < queue-c-input.repl
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
+
+ ./receiver --port $BROKER_A --queue queue-a --messages 5 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-b --messages 10 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-c --messages 10 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-d > /dev/null
+
+
+ # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
+ # making sure all the messages have been flushed from the replication queue.
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-e --send-eos 1
+ ./receiver --port $BROKER_B --queue queue-e --messages 1 > /dev/null
+
+ #shutdown broker A then check that broker Bs versions of the queues are as expected
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+
+ #validate replicated queues:
+ ./receiver --port $BROKER_B --queue queue-a > queue-a-backup.repl
+ ./receiver --port $BROKER_B --queue queue-b > queue-b-backup.repl
+ ./receiver --port $BROKER_B --queue queue-c > queue-c-backup.repl
+
+
+ tail -5 queue-a-input.repl > queue-a-expected.repl
+ tail -10 queue-b-input.repl > queue-b-expected.repl
+ diff queue-a-backup.repl queue-a-expected.repl || FAIL=1
+ diff queue-b-backup.repl queue-b-expected.repl || FAIL=1
+ diff queue-c-backup.repl queue-c-input.repl || FAIL=1
+
+ grep 'queue-d does not exist' replication-dest.log > /dev/null || echo "WARNING: Expected error to be logged!"
+
+ stop_brokers
+
+ # now check offsets working (enqueue based on position being set, not queue abs position)
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
+ BROKER_A=`cat qpidd.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-d
+
+ i=1
+ while [ $i -le 10 ]; do
+ echo Message $i for A >> queue-e-input.repl
+ i=`expr $i + 1`
+ done
+
+ ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e-input.repl
+ ./receiver --port $BROKER_A --queue queue-e --messages 10 > /dev/null
+
+ # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
+ # making sure all the messages have been flushed from the replication queue.
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
+ ./receiver --port $BROKER_B --queue queue-d --messages 1 > /dev/null
+
+ # now check offsets working
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+ # now send another 15
+ i=11
+ while [ $i -le 15 ]; do
+ echo Message $i for A >> queue-e1-input.repl
+ i=`expr $i + 1`
+ done
+ ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e1-input.repl
+
+ ./receiver --port $BROKER_B --queue queue-e > queue-e-backup.repl
+ diff queue-e-backup.repl queue-e1-input.repl || FAIL=1
+
+ stop_brokers
+
+ if [ x$FAIL != x ]; then
+ echo replication test failed: expectations not met!
+ exit 1
+ else
+ echo queue state replicated as expected
+ rm -f queue-*.repl replication-*.log
+ fi
+
+else
+ echo "Skipping replication test, plugins not built or python utils not located"
+fi
+
diff --git a/qpid/cpp/src/tests/restart_cluster b/qpid/cpp/src/tests/restart_cluster
new file mode 100755
index 0000000000..6a6abc8042
--- /dev/null
+++ b/qpid/cpp/src/tests/restart_cluster
@@ -0,0 +1,38 @@
+#!/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.
+#
+
+# Re-start a cluster on the local host.
+
+srcdir=`dirname $0`
+$srcdir/stop_cluster
+exec $srcdir/start_cluster "$@"
+#!/bin/sh
+# Re-start a cluster on the local host.
+
+srcdir=`dirname $0`
+$srcdir/stop_cluster
+exec $srcdir/start_cluster "$@"
+#!/bin/sh
+# Re-start a cluster on the local host.
+
+srcdir=`dirname $0`
+$srcdir/stop_cluster
+exec $srcdir/start_cluster "$@"
diff --git a/qpid/cpp/src/tests/resuming_receiver.cpp b/qpid/cpp/src/tests/resuming_receiver.cpp
new file mode 100644
index 0000000000..2e22a7c572
--- /dev/null
+++ b/qpid/cpp/src/tests/resuming_receiver.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+
+#include <iostream>
+#include <fstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+
+
+namespace qpid {
+namespace tests {
+
+class Listener : public MessageListener,
+ public FailoverManager::Command,
+ public FailoverManager::ReconnectionStrategy
+{
+ public:
+ Listener ( int report_frequency = 1000,
+ int verbosity = 0,
+ char const * queue_name = "message_queue" );
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ void check();
+ void editUrlList(vector<Url>& urls);
+ private:
+ Subscription subscription;
+ uint count;
+ vector<int> received_twice;
+ uint lastSn;
+ bool gaps;
+ uint reportFrequency;
+ int verbosity;
+ bool done;
+ string queueName;
+};
+
+
+Listener::Listener ( int freq, int verbosity, char const * name )
+ : count(0),
+ lastSn(0),
+ gaps(false),
+ reportFrequency(freq),
+ verbosity(verbosity),
+ done(false),
+ queueName ( name )
+{}
+
+const std::string SN("sn");
+
+void Listener::received(Message & message)
+{
+ if (message.getData() == "That's all, folks!")
+ {
+ done = true;
+ if(verbosity > 0 )
+ {
+ cout << "Shutting down listener for "
+ << message.getDestination() << endl;
+
+ cout << "Listener received "
+ << count
+ << " messages ("
+ << received_twice.size()
+ << " received_twice)"
+ << endl;
+
+ }
+ subscription.cancel();
+ if ( verbosity > 0 )
+ cout << "LISTENER COMPLETED\n";
+
+ if ( ! gaps ) {
+ cout << "no gaps were detected\n";
+ cout << received_twice.size() << " messages were received twice.\n";
+ }
+ else {
+ cout << "gaps detected\n";
+ for ( unsigned int i = 0; i < received_twice.size(); ++ i )
+ cout << "received_twice "
+ << received_twice[i]
+ << endl;
+ }
+ } else {
+ uint sn = message.getHeaders().getAsInt(SN);
+ if (lastSn < sn) {
+ if (sn - lastSn > 1) {
+ cerr << "Error: gap in sequence between " << lastSn << " and " << sn << endl;
+ gaps = true;
+ }
+ lastSn = sn;
+ ++count;
+ if ( ! ( count % reportFrequency ) ) {
+ if ( verbosity > 0 )
+ cout << "Listener has received "
+ << count
+ << " messages on queue "
+ << queueName
+ << endl;
+ }
+ } else {
+ received_twice.push_back ( sn );
+ }
+ }
+}
+
+void Listener::check()
+{
+ if (gaps) throw Exception("Detected gaps in sequence; messages appear to have been lost.");
+}
+
+void Listener::execute(AsyncSession& session, bool isRetry) {
+ if (verbosity > 0)
+ cout << "resuming_receiver " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (!done) {
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queueName);
+ subs.run();
+ }
+}
+
+void Listener::editUrlList(vector<Url>& urls)
+{
+ /**
+ * A more realistic algorithm would be to search through the list
+ * for prefered hosts and ensure they come first in the list.
+ */
+ if (urls.size() > 1) rotate(urls.begin(), urls.begin() + 1, urls.end());
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 6 )
+ {
+ cerr << "Usage: resuming_receiver host port report_frequency verbosity queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int reportFrequency = atoi(argv[3]);
+ int verbosity = atoi(argv[4]);
+ char * queue_name = argv[5];
+
+ Listener listener ( reportFrequency, verbosity, queue_name );
+ FailoverManager connection(settings, &listener);
+
+ try {
+ connection.execute(listener);
+ connection.close();
+ listener.check();
+ return 0;
+ } catch(const exception& error) {
+ cerr << "Receiver failed: " << error.what() << endl;
+ }
+ return 1;
+}
+
+
+
diff --git a/qpid/cpp/src/tests/ring_queue_test b/qpid/cpp/src/tests/ring_queue_test
new file mode 100755
index 0000000000..553746eb49
--- /dev/null
+++ b/qpid/cpp/src/tests/ring_queue_test
@@ -0,0 +1,174 @@
+#!/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 script for validating the behaviour of a ring queue
+
+QUEUE_NAME=ring-queue
+LIMIT=100
+DURABLE=0
+MESSAGES=10000
+SENDERS=1
+RECEIVERS=1
+CONCURRENT=0
+BROKER_URL="-a ${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+setup() {
+ if [[ $DURABLE -gt 0 ]]; then
+ EXTRA_ARGS=" --durable"
+ fi
+ qpid-config $BROKER_URL add queue $QUEUE_NAME --max-queue-count $LIMIT --limit-policy ring $EXTRA_ARGS
+}
+
+send() {
+ datagen --count $MESSAGES | tee sender_${QUEUE_NAME}_${1} | sender --durable $DURABLE --routing-key $QUEUE_NAME
+}
+
+receive() {
+ #TODO: allow a variety of receiver options to be passed in (ack-frequency, credit-window etc)
+ receiver --queue $QUEUE_NAME > receiver_${QUEUE_NAME}_${1}
+}
+
+cleanup() {
+ rm -f sender_${QUEUE_NAME}_* receiver_${QUEUE_NAME}_*
+ qpid-config $BROKER_URL del queue $QUEUE_NAME --force
+}
+
+log() {
+ echo $1
+}
+
+fail() {
+ echo $1
+ FAILED=1
+}
+
+validate() {
+ if [[ $RECEIVERS -eq 0 ]]; then
+ #queue should have $LIMIT messages on it, but need to send an eos also
+ sender --routing-key $QUEUE_NAME --send-eos 1 < /dev/null
+ received=$(receiver --queue $QUEUE_NAME --browse | wc -l)
+ if [[ received -eq $(( $LIMIT - 1)) ]]; then
+ log "queue contains $LIMIT messages as expected"
+ else
+ fail "queue does not contain the expected $LIMIT messages (received $received)"
+ fi
+ elif [[ $CONCURRENT -eq 0 ]]; then
+ #sum of length of all output files should be equal to $LIMIT - $RECEIVERS (1 eos message each)
+ received=$(cat receiver_${QUEUE_NAME}_* | wc -l)
+ expected=$(( $LIMIT - $RECEIVERS ))
+ if [[ $received -eq $expected ]]; then
+ log "received $LIMIT messages as expected"
+ else
+ fail "received $received messages, expected $expected"
+ fi
+ #if there were only a single sender and receiver (executed serially) we can check the
+ #actual received contents
+ if [[ $RECEIVERS -eq 1 ]] && [[ $SENDERS -eq 1 ]]; then
+ tail -n $(($LIMIT - 1)) sender_${QUEUE_NAME}_1 | diff - receiver_${QUEUE_NAME}_1 || FAILED=1
+ if [[ $FAILED -eq 1 ]]; then
+ fail "did not receive expected messages"
+ else
+ log "received messages matched expectations"
+ fi
+ fi
+ else
+ #multiple receivers, concurrent with senders; ring queue functionality cannot be validated in this case
+ if [[ $(cat receiver_${QUEUE_NAME}_* | wc -l) -le $(cat sender_${QUEUE_NAME}_* | wc -l) ]]; then
+ log "sent at least as many messages as were received"
+ else
+ #Note: if any receiver was browsing, this would be valid (would need to add 'sort | uniq')
+ # to pipeline above
+ fail "received more messages than were sent"
+ fi
+ fi
+
+ if [[ $FAILED ]]; then
+ echo $(basename $0): FAILED
+ exit 1
+ else
+ cleanup
+ fi
+}
+
+run_test() {
+ if [[ $CONCURRENT -eq 0 ]]; then
+ echo "Starting $SENDERS senders followed by $RECEIVERS receivers "
+ else
+ echo "Starting $SENDERS senders concurrently with $RECEIVERS receivers"
+ fi
+ for ((i=1; i <= $SENDERS; i++)); do
+ send $i &
+ sender_pids[$i]=$!
+ done
+ if [[ $CONCURRENT -eq 0 ]] && [[ $RECEIVERS -gt 0 ]]; then
+ wait
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ for ((i=1; i <= $RECEIVERS; i++)); do
+ receive $i &
+ done
+ if [[ $CONCURRENT -gt 0 ]]; then
+ for ((i=1; i <= $SENDERS; i++)); do
+ wait ${sender_pids[$i]}
+ done
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ wait
+}
+
+usage() {
+ cat <<EOF
+$(basename $0): Test script for validating the behaviour of a ring queue
+
+Options:
+ -q <queue> the name of the queue to use
+ -s <senders> the number of senders to start
+ -r <receivers> the number of receivers to start
+ -l <limit> the limit for the ring queue
+ -m <messages> the number of messages to send
+ -c if specified, receivers will run concurrently with senders
+ -d if specified the queue and messages will be durable
+EOF
+ exit 1
+}
+
+while getopts "s:r:m:u:dch" opt ; do
+ case $opt in
+ q) QUEUE_NAME=$OPTARG ;;
+ l) LIMIT=$OPTARG ;;
+ s) SENDERS=$OPTARG ;;
+ r) RECEIVERS=$OPTARG ;;
+ m) MESSAGES=$OPTARG ;;
+ d) DURABLE=1 ;;
+ c) CONCURRENT=1 ;;
+ h) usage;;
+ ?) usage;;
+ esac
+done
+
+if [[ $SENDERS -gt 0 ]]; then
+ setup
+ run_test
+ validate
+else
+ echo "Nothing can be done if there are no senders"
+fi
+
diff --git a/qpid/cpp/src/tests/rsynchosts b/qpid/cpp/src/tests/rsynchosts
new file mode 100755
index 0000000000..56ee57e898
--- /dev/null
+++ b/qpid/cpp/src/tests/rsynchosts
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+abspath() {
+ if test -d "$1"; then absdir "$1";
+ else echo $(absdir $(dirname "$1"))/$(basename "$1")
+ fi
+}
+
+usage() {
+ echo "Usage: $(basename $0) file [file...]
+Synchronize the contents of each file or directory to the same absolute path on
+each host in \$HOSTS.
+"
+ exit 1
+}
+
+test "$*" || usage
+
+for f in $*; do FILES="$FILES $(abspath $f)" || exit 1; done
+
+OK_FILE=`mktemp` # Will be deleted if anything goes wrong.
+trap "rm -f $OK_FILE" EXIT
+
+for h in $HOSTS; do
+ rsync -aRO --delete $FILES $h:/ || { echo "rsync to $h failed"; rm -f $OK_FILE; } &
+done
+wait
+test -f $OK_FILE
+
diff --git a/qpid/cpp/src/tests/run-unit-tests b/qpid/cpp/src/tests/run-unit-tests
new file mode 100755
index 0000000000..862a76c4f5
--- /dev/null
+++ b/qpid/cpp/src/tests/run-unit-tests
@@ -0,0 +1,48 @@
+#!/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.
+#
+
+#
+# Library names (without path or .so) and CppUnit test paths can be
+# specified on the command line or in env var UNIT_TESTS. For example:
+#
+# Selected test classes:
+# ./run-unit-tests ValueTest ClientChannelTest
+#
+# Individual test method
+# ./run-unit-tests ValueTest :ValueTest::testStringValueEquals
+#
+# Build and run selected tests:
+# make check TESTS=run-unit-tests UNIT_TESTS=ClientChannelTest
+#
+
+for u in $* $UNIT_TESTS ; do
+ case $u in
+ :*) TEST_ARGS="$TEST_ARGS $u" ;; # A test path.
+ *) TEST_ARGS="$TEST_ARGS .libs/$u.so" ;; # A test library.
+ esac
+done
+test -z "$TEST_ARGS" && TEST_ARGS=".libs/*Test.so"
+
+test -z "$srcdir" && srcdir=.
+
+# libdlclose_noop prevents unloading symbols needed for valgrind output.
+export LD_PRELOAD=.libs/libdlclose_noop.so
+source $srcdir/run_test DllPlugInTester -c -b $TEST_ARGS
diff --git a/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests
new file mode 100755
index 0000000000..41f41e20e1
--- /dev/null
+++ b/qpid/cpp/src/tests/run_acl_tests
@@ -0,0 +1,62 @@
+#!/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.
+#
+
+# Run the acl tests. $srcdir is set by the Makefile.
+source ./test_env.sh
+DATA_DIR=`pwd`/data_dir
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+}
+
+test_loading_acl_from_absolute_path(){
+ POLICY_FILE=$srcdir/policy.acl
+ rm -f temp.log
+ PORT=`../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module $ACL_LIB --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null`
+ ACL_FILE=`grep "notice Read ACL file" temp.log | sed 's/^.*Read ACL file //'`
+ $QPIDD_EXEC --no-module-dir -q --port $PORT
+ if test "$ACL_FILE" != "\"$POLICY_FILE\""; then
+ echo "unable to load policy file from an absolute path";
+ return 1;
+ fi
+ rm temp.log
+}
+
+if test -d ${PYTHON_DIR} ; then
+ rm -rf $DATA_DIR
+ mkdir -p $DATA_DIR
+ cp $srcdir/policy.acl $DATA_DIR
+ start_brokers
+ echo "Running acl tests using brokers on ports $LOCAL_PORT"
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl || EXITCODE=1
+ stop_brokers || EXITCODE=1
+ test_loading_acl_from_absolute_path || EXITCODE=1
+ rm -rf $DATA_DIR
+ exit $EXITCODE
+fi
+
diff --git a/qpid/cpp/src/tests/run_acl_tests.ps1 b/qpid/cpp/src/tests/run_acl_tests.ps1
new file mode 100644
index 0000000000..a1848779c7
--- /dev/null
+++ b/qpid/cpp/src/tests/run_acl_tests.ps1
@@ -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.
+#
+
+# Run the acl tests.
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping acl tests as python libs not found"
+ exit 1
+}
+
+$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py"
+$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py"
+$env:PYTHONPATH="$PYTHON_DIR;$srcdir;$PYTHON_TEST_DIR;$QMF_LIB"
+$Global:BROKER_EXE = ""
+
+Function start_broker($acl_options)
+{
+ # Test runs from the tests directory but the broker executable is one level
+ # up, and most likely in a subdirectory from there based on what build type.
+ # Look around for it before trying to start it.
+ . $srcdir\find_prog.ps1 ..\qpidd.exe
+ if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+ }
+ $Global:BROKER_EXE = $prog
+ if (Test-Path qpidd.port) {
+ Remove-Item qpidd.port
+ }
+ $cmdline = "$prog --auth=no --no-module-dir --port=0 --log-to-file qpidd.log $acl_options | foreach { set-content qpidd.port `$_ }"
+ $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+ . $srcdir\background.ps1 $cmdblock
+ # Wait for the broker to start
+ $wait_time = 0
+ while (!(Test-Path qpidd.port) -and ($wait_time -lt 10)) {
+ Start-Sleep 2
+ $wait_time += 2
+ }
+ if (!(Test-Path qpidd.port)) {
+ "Timeout waiting for broker to start"
+ exit 1
+ }
+ set-item -path env:BROKER_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+Function stop_broker
+{
+ "Stopping $Global:BROKER_EXE"
+ Invoke-Expression "$Global:BROKER_EXE --no-module-dir -q --port $env:BROKER_PORT" | Write-Output
+ Remove-Item qpidd.port
+}
+
+$DATA_DIR = [IO.Directory]::GetCurrentDirectory() + "\data_dir"
+Remove-Item $DATA_DIR -recurse
+New-Item $DATA_DIR -type directory
+Copy-Item $srcdir\policy.acl $DATA_DIR
+start_broker("--data-dir $DATA_DIR --acl-file policy.acl")
+"Running acl tests using broker on port $env:BROKER_PORT"
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m acl -b localhost:$env:BROKER_PORT" | Out-Default
+$RETCODE=$LASTEXITCODE
+stop_broker
+
+# Now try reading the acl file from an absolute path.
+Remove-Item qpidd.log
+$policy_full_path = "$srcdir\policy.acl"
+start_broker("--no-data-dir --acl-file $policy_full_path")
+#test_loading_acl_from_absolute_path(){
+# POLICY_FILE=$srcdir/policy.acl
+# rm -f temp.log
+# PORT=`../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module $ACL_LIB --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null`
+# ACL_FILE=`grep "notice Read ACL file" temp.log | sed 's/^.*Read ACL file //'`
+# $QPIDD_EXEC --no-module-dir -q --port $PORT
+# if test "$ACL_FILE" != "\"$POLICY_FILE\""; then
+# echo "unable to load policy file from an absolute path";
+# return 1;
+# fi
+# rm temp.log
+#}
+#
+# test_loading_acl_from_absolute_path || EXITCODE=1
+# rm -rf $DATA_DIR
+# exit $EXITCODE
+stop_broker
+exit $RETCODE
diff --git a/qpid/cpp/src/tests/run_cli_tests b/qpid/cpp/src/tests/run_cli_tests
new file mode 100755
index 0000000000..ec5c71b646
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cli_tests
@@ -0,0 +1,81 @@
+#!/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.
+#
+
+# Run the cli-utility tests.
+
+source ./test_env.sh
+CLI_DIR=$PYTHON_COMMANDS
+
+trap stop_brokers INT TERM QUIT
+
+# helper function to create test.xquery in the current directory, so
+# that the python test program can find it. yes, it leaves a turd.
+create_test_xquery() {
+ cat <<EOF > ./test.xquery
+ let \$w := ./weather
+ return \$w/station = 'Raleigh-Durham International Airport (KRDU)'
+ and \$w/temperature_f > 50
+ and \$w/temperature_f - \$w/dewpoint > 5
+ and \$w/wind_speed_mph > 7
+ and \$w/wind_speed_mph < 20
+EOF
+}
+
+start_brokers() {
+ # if the xml lib is present, use it. if not, disable any tests which
+ # look like they're xml related.
+ # if we start supporting xml on windows, it will need something similar
+ # here
+ if [ -f ../.libs/xml.so ] ; then
+ xargs="--load-module ../.libs/xml.so"
+ if [ ! -f test.xquery ] ; then
+ create_test_xquery
+ fi
+ targs=""
+ else
+ echo "Ignoring XML tests"
+ xargs=""
+ targs="--ignore=*xml*"
+ fi
+
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no $xargs > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no $xargs > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
+ PYTHON_TESTS=${PYTHON_TESTS:-$*}
+ $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $PYTHON_TESTS $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL CLI tests"; exit 1;
+ fi
+fi
+
diff --git a/qpid/cpp/src/tests/run_cluster_authentication_soak b/qpid/cpp/src/tests/run_cluster_authentication_soak
new file mode 100755
index 0000000000..7bc406c4ca
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cluster_authentication_soak
@@ -0,0 +1,26 @@
+#! /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.
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+source sasl_test_setup.sh
+
+with_ais_group ./cluster_authentication_soak 500
+
diff --git a/qpid/cpp/src/tests/run_cluster_authentication_test b/qpid/cpp/src/tests/run_cluster_authentication_test
new file mode 100755
index 0000000000..647200b869
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cluster_authentication_test
@@ -0,0 +1,26 @@
+#! /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.
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+source sasl_test_setup.sh
+
+with_ais_group ./cluster_authentication_soak
+
diff --git a/qpid/cpp/src/tests/run_cluster_test b/qpid/cpp/src/tests/run_cluster_test
new file mode 100755
index 0000000000..c022eea1fe
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cluster_test
@@ -0,0 +1,26 @@
+#!/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.
+#
+
+
+# Run the tests
+srcdir=`dirname $0`
+. $srcdir/ais_check
+with_ais_group $srcdir/run_test ./cluster_test
diff --git a/qpid/cpp/src/tests/run_cluster_tests b/qpid/cpp/src/tests/run_cluster_tests
new file mode 100755
index 0000000000..e136d3810a
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cluster_tests
@@ -0,0 +1,37 @@
+#!/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.
+#
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+test -x $QPID_PYTHON_TEST || { echo Skipping test, $QPID_PYTHON_TEST not found; exit 0; }
+
+# Delete old cluster test data
+OUTDIR=${OUTDIR:-brokertest.tmp}
+rm -rf $OUTDIR
+mkdir -p $OUTDIR
+
+# Ignore tests requiring a store by default.
+CLUSTER_TESTS_IGNORE=${CLUSTER_TESTS_IGNORE:--i cluster_tests.StoreTests.* -I $srcdir/cluster_tests.fail}
+CLUSTER_TESTS=${CLUSTER_TESTS:-$*}
+
+with_ais_group $QPID_PYTHON_TEST -DOUTDIR=$OUTDIR -m cluster_tests $CLUSTER_TESTS_IGNORE $CLUSTER_TESTS || exit 1
+rm -rf $OUTDIR
diff --git a/qpid/cpp/src/tests/run_failover_soak b/qpid/cpp/src/tests/run_failover_soak
new file mode 100755
index 0000000000..cce8b07a26
--- /dev/null
+++ b/qpid/cpp/src/tests/run_failover_soak
@@ -0,0 +1,38 @@
+#!/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.
+#
+
+source ./test_env.sh
+. $srcdir/ais_check
+
+host=127.0.0.1
+
+unset QPID_NO_MODULE_DIR # failover_soak uses --module-dir, dont want clash
+MODULES=${MODULES:-$moduledir}
+MESSAGES=${MESSAGES:-500000}
+REPORT_FREQUENCY=${REPORT_FREQUENCY:-20000}
+VERBOSITY=${VERBOSITY:-0}
+DURABILITY=${DURABILITY:-0}
+N_QUEUES=${N_QUEUES:-1}
+N_BROKERS=${N_BROKERS:-4}
+
+rm -f soak-*.log
+exec ./failover_soak $MODULES ./declare_queues ./replaying_sender ./resuming_receiver $MESSAGES $REPORT_FREQUENCY $VERBOSITY $DURABILITY $N_QUEUES $N_BROKERS
+
diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests
new file mode 100755
index 0000000000..590f74746e
--- /dev/null
+++ b/qpid/cpp/src/tests/run_federation_tests
@@ -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.
+#
+
+# Run the federation tests.
+
+source ./test_env.sh
+
+trap stop_brokers INT TERM QUIT
+
+if [ -f ../.libs/xml.so ] ; then
+ MODULES="--load-module xml" # Load the XML exchange and run XML exchange federation tests
+ SKIPTESTS=""
+else
+ MODULES="--no-module-dir"
+ SKIPTESTS="-i *xml"
+fi
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+
+ ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
+ REMOTE_B1=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
+ REMOTE_B2=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC $MODULES -q --port $LOCAL_PORT
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_PORT
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_B1
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_B2
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT $REMOTE_B1 $REMOTE_B2"
+ $QPID_PYTHON_TEST -m federation $SKIPTESTS -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dextra-brokers="$REMOTE_B1 $REMOTE_B2" $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/run_federation_tests.ps1 b/qpid/cpp/src/tests/run_federation_tests.ps1
new file mode 100644
index 0000000000..35353a870f
--- /dev/null
+++ b/qpid/cpp/src/tests/run_federation_tests.ps1
@@ -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.
+#
+
+# Run the federation tests.
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping federation tests as python libs not found"
+ exit 1
+}
+
+$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py"
+$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py"
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+$cmdline = "$prog --auth=no --no-module-dir --no-data-dir --port=0 --ssl-port=0 --log-to-file qpidd.log $args | foreach { set-content qpidd.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+
+function start_brokers {
+ # Start 2 brokers, saving the port numbers in LOCAL_PORT, REMOTE_PORT.
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:LOCAL_PORT -value (get-content -path qpidd.port -totalcount 1)
+ Remove-Item qpidd.port
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:REMOTE_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+function stop_brokers {
+ Invoke-Expression "$prog -q --port $env:LOCAL_PORT" | Out-Default
+ Invoke-Expression "$prog -q --port $env:REMOTE_PORT" | Out-Default
+}
+
+trap {
+ &stop_brokers
+ break
+}
+
+&start_brokers
+"Running federation tests using brokers on ports $env:LOCAL_PORT $env:REMOTE_PORT"
+$env:PYTHONPATH="$srcdir;$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB"
+$tests = "*"
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m federation -b localhost:$env:LOCAL_PORT -Dremote-port=$env:REMOTE_PORT $tests" | Out-Default
+$RETCODE=$LASTEXITCODE
+&stop_brokers
+if ($RETCODE -ne 0) {
+ "FAIL federation tests"
+ exit 1
+}
diff --git a/qpid/cpp/src/tests/run_header_test b/qpid/cpp/src/tests/run_header_test
new file mode 100755
index 0000000000..34008132cc
--- /dev/null
+++ b/qpid/cpp/src/tests/run_header_test
@@ -0,0 +1,37 @@
+#!/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.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+srcdir=`dirname $0`
+source ./test_env.sh
+
+test -f qpidd.port && QPID_PORT=`cat qpidd.port`
+
+if test -d ${PYTHON_DIR} ; then
+ ./header_test -p $QPID_PORT
+ $srcdir/header_test.py "localhost" $QPID_PORT
+else
+ echo "Skipping header test as python libs not found"
+fi
+
diff --git a/qpid/cpp/src/tests/run_header_test.ps1 b/qpid/cpp/src/tests/run_header_test.ps1
new file mode 100644
index 0000000000..7d3e43a30f
--- /dev/null
+++ b/qpid/cpp/src/tests/run_header_test.ps1
@@ -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.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping header test as python libs not found"
+ exit 0
+}
+
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+# Test runs from the tests directory but the test executables are in a
+# subdirectory based on the build type. Look around for it before trying
+# to start it.
+. $srcdir\find_prog.ps1 .\header_test.exe
+if (!(Test-Path $prog)) {
+ "Cannot locate header_test.exe"
+ exit 1
+}
+
+Invoke-Expression "$prog -p $env:QPID_PORT" | Write-Output
+$env:PYTHONPATH="$PYTHON_DIR;$env:PYTHONPATH"
+Invoke-Expression "python $srcdir/header_test.py localhost $env:QPID_PORT" | Write-Output
+exit $LASTEXITCODE
diff --git a/qpid/cpp/src/tests/run_headers_federation_tests b/qpid/cpp/src/tests/run_headers_federation_tests
new file mode 100644
index 0000000000..a4584e6884
--- /dev/null
+++ b/qpid/cpp/src/tests/run_headers_federation_tests
@@ -0,0 +1,49 @@
+#!/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.
+#
+
+# Run the federation tests for the Headers Exchange.
+
+source ./test_env.sh
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running HeadersExchange federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
+ $QPID_PYTHON_TEST -m headers_federation -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/run_long_cluster_tests b/qpid/cpp/src/tests/run_long_cluster_tests
new file mode 100755
index 0000000000..5dce0be585
--- /dev/null
+++ b/qpid/cpp/src/tests/run_long_cluster_tests
@@ -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.
+#
+
+srcdir=`dirname $0`
+$srcdir/run_cluster_tests 'cluster_tests.LongTests.*' -DDURATION=4
+
diff --git a/qpid/cpp/src/tests/run_perftest b/qpid/cpp/src/tests/run_perftest
new file mode 100755
index 0000000000..5ad7c1ff4f
--- /dev/null
+++ b/qpid/cpp/src/tests/run_perftest
@@ -0,0 +1,28 @@
+#!/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: count [qpid-perftest options...]
+# Run a qpid-perftest with count multiplied.
+#
+MULTIPLIER=3
+COUNT=`expr $1 \* $MULTIPLIER`
+shift
+exec `dirname $0`/run_test ./qpid-perftest --summary --count $COUNT "$@"
diff --git a/qpid/cpp/src/tests/run_queue_flow_limit_tests b/qpid/cpp/src/tests/run_queue_flow_limit_tests
new file mode 100755
index 0000000000..f921cf5e7e
--- /dev/null
+++ b/qpid/cpp/src/tests/run_queue_flow_limit_tests
@@ -0,0 +1,57 @@
+#!/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.
+#
+
+# Run tests against Queue producer flow control.
+
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping queue flow control tests, no python dir."; exit 0; }
+
+LOG_FILE=queue_flow_limit_test.log
+PORT=""
+
+trap stop_broker INT TERM QUIT
+
+error() {
+ echo $*
+ exit 1;
+}
+
+start_broker() {
+ # Note: if you change the DEFAULT_THRESHOLDS, you will need to update queue_flow_limit_tests.py
+ DEFAULT_THRESHOLDS="--default-flow-stop-threshold=80 --default-flow-resume-threshold=70"
+ rm -rf $LOG_FILE
+ PORT=$($QPIDD_EXEC $DEFAULT_THRESHOLDS --auth=no --no-module-dir --daemon --port=0 -t --log-to-file $LOG_FILE) || error "Could not start broker"
+}
+
+stop_broker() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+start_broker
+echo "Running Queue flow limit tests using broker on port $PORT"
+$QPID_PYTHON_TEST -m queue_flow_limit_tests $SKIPTESTS -b localhost:$PORT $@
+RETCODE=$?
+stop_broker
+if test x$RETCODE != x0; then
+ echo "FAIL queue flow limit tests"; exit 1;
+fi
+rm -rf $LOG_FILE
+
diff --git a/qpid/cpp/src/tests/run_ring_queue_test b/qpid/cpp/src/tests/run_ring_queue_test
new file mode 100755
index 0000000000..7ca870841e
--- /dev/null
+++ b/qpid/cpp/src/tests/run_ring_queue_test
@@ -0,0 +1,36 @@
+#!/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.
+#
+#script to run a sequence of ring queue tests via make
+
+#setup path to find qpid-config and sender/receiver test progs
+source ./test_env.sh
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+ring_queue_test -c -s 4 -r 4
+ring_queue_test -s 4 -r 0
+ring_queue_test -s 1 -r 1
+
+
diff --git a/qpid/cpp/src/tests/run_store_tests.ps1 b/qpid/cpp/src/tests/run_store_tests.ps1
new file mode 100644
index 0000000000..76b46737f0
--- /dev/null
+++ b/qpid/cpp/src/tests/run_store_tests.ps1
@@ -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.
+#
+
+# Run the store tests.
+# There are two sets of tests:
+# 1. A subset of the normal broker python tests, dtx and persistence, but
+# run again with the desired store loaded.
+# 2. store.py, which tests recovering things across broker restarts.
+
+$test_store = $args[0]
+if ($test_store -ne "MSSQL" -and $test_store -ne "MSSQL-CLFS") {
+ "Invalid store test type $test_store - must be MSSQL or MSSQL-CLFS"
+ exit 1
+}
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping store tests as python libs not found"
+ exit 1
+}
+
+$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py"
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+
+# The store to test is the same build type as the broker.
+$store_dir = "..\qpid\store\$sub"
+if (!([string]::Compare($sub, "Debug", $True))) {
+ $suffix = "d"
+}
+
+$stamp = Get-Date -format %dMMMyyyy_HHmmss
+$env:STORE_LIB="$store_dir\store$suffix.dll"
+if ($test_store -eq "MSSQL") {
+ $test_store_module="$store_dir\mssql_store$suffix.dll"
+ $env:STORE_SQL_LIB=$test_store_module
+ $env:STORE_CATALOG="store_recovery_sql_test_$stamp"
+ $cat1="store_sql_test_$stamp"
+ $out = "sql_store_test_$stamp"
+}
+else {
+ $test_store_module="$store_dir\msclfs_store$suffix.dll"
+ $env:STORE_SQL_CLFS_LIB=$test_store_module
+ $env:STORE_CATALOG="store_recovery_clfs_test_$stamp"
+ $cat1="store_clfs_test_$stamp"
+ $out = "clfs_store_test_$stamp"
+}
+
+$FAILCODE = 0
+
+# Test 1... re-run some of the regular python broker tests against a broker
+# with the store module loaded.
+$cmdline = "$prog --auth=no --port=0 --log-to-file qpidd-store.log --no-module-dir --load-module $env:STORE_LIB --load-module $test_store_module --catalog $cat1 | foreach { set-content qpidd-store.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+. $srcdir\background.ps1 $cmdblock
+
+$wait_time = 0
+while (!(Test-Path qpidd-store.port) -and ($wait_time -lt 90)) {
+ Start-Sleep 2
+ $wait_time += 2
+}
+if (!(Test-Path qpidd-store.port)) {
+ "Time out waiting for broker to start"
+ exit 1
+}
+set-item -path env:QPID_PORT -value (get-content -path qpidd-store.port -totalcount 1)
+Remove-Item qpidd-store.port
+
+$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py\qpid_tests\broker_0_10"
+$env:PYTHONPATH="$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB"
+python $PYTHON_DIR/qpid-python-test -m dtx -m persistence -b localhost:$env:QPID_PORT $fails $tests
+$RETCODE=$LASTEXITCODE
+if ($RETCODE -ne 0) {
+ $FAILCODE = 1
+}
+
+# Piping the output makes the script wait for qpidd to finish.
+Invoke-Expression "$prog --quit --port $env:QPID_PORT" | Write-Output
+
+
+# Test 2... store.py starts/stops/restarts its own brokers
+
+$tests = "*"
+$env:PYTHONPATH="$PYTHON_DIR;$srcdir"
+$env:QPIDD_EXEC="$prog"
+$env:STORE_LIB="$store_dir\store$suffix.dll"
+if ($test_store -eq "MSSQL") {
+ $env:STORE_SQL_LIB="$store_dir\mssql_store$suffix.dll"
+ $env:STORE_CATALOG="store_recovery_sql_test_$stamp"
+ $out = "sql_store_test_$stamp"
+}
+else {
+ $env:STORE_SQL_CLFS_LIB="$store_dir\msclfs_store$suffix.dll"
+ $env:STORE_CATALOG="store_recovery_clfs_test_$stamp"
+ $out = "clfs_store_test_$stamp"
+}
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m store -D OUTDIR=$out $tests" | Out-Default
+$RETCODE=$LASTEXITCODE
+if ($RETCODE -ne 0) {
+ "FAIL $test_store store tests"
+ $FAILCODE = 1
+}
+exit $FAILCODE
diff --git a/qpid/cpp/src/tests/run_test b/qpid/cpp/src/tests/run_test
new file mode 100755
index 0000000000..6ec1fd892b
--- /dev/null
+++ b/qpid/cpp/src/tests/run_test
@@ -0,0 +1,82 @@
+#!/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 up environment and run a test executable or script.
+#
+# Output nothing if test passes, show the output if it fails and
+# leave output in <test>.log for examination.
+#
+# If qpidd.port exists and is not empty run test with QPID_PORT=`cat qpidd.port`
+#
+# If $VALGRIND if is set run under valgrind. If there are valgrind
+# erros show valgrind output, also leave it in <test>.valgrind for
+# examination.
+#
+
+srcdir=`dirname $0`
+source ./test_env.sh
+source $srcdir/vg_check
+
+# Export variables from makefile.
+export srcdir
+
+# Set QPID_PORT if qpidd.port exists.
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+# Avoid silly libtool error messages if these are not defined
+test -z "$LC_ALL" && LC_ALL=
+test -z "$LC_CTYPE" && LC_CTYPE=
+test -z "$LC_COLLATE" && LC_COLLATE=
+test -z "$LC_MESSAGES" && LC_MESSAGES=
+export LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+
+VG_LOG="`basename $1`.vglog"
+rm -f $VG_LOG*
+
+# Use VALGRIND_OPTS="--gen-suppressions=all" to generated suppressions
+VALGRIND_OPTS="$VALGRIND_OPTS
+--leak-check=full
+--demangle=yes
+--suppressions=$srcdir/.valgrind.supp
+--num-callers=25
+--log-file=$VG_LOG --
+"
+ERROR=0
+if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then
+ # This is a libtool "executable". Valgrind it if VALGRIND specified.
+ test -n "$VALGRIND" && VALGRIND="$VALGRIND $VALGRIND_OPTS"
+ # Hide output unless there's an error.
+ $LIBTOOL --mode=execute $VALGRIND "$@" 2>&1 || ERROR=1
+ test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; }
+elif file $1 | grep -q text; then
+ # This is a non-libtool shell script, just execute it.
+ exec "$@"
+else
+ # This is a real executable, valgrind it.
+ test -n "$VALGRIND" && VALGRIND="$VALGRIND $VALGRIND_OPTS"
+ # Hide output unless there's an error.
+ $VALGRIND "$@" 2>&1 || ERROR=1
+ test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; }
+fi
+
+exit $ERROR
+
diff --git a/qpid/cpp/src/tests/run_test.ps1 b/qpid/cpp/src/tests/run_test.ps1
new file mode 100644
index 0000000000..ca990bc057
--- /dev/null
+++ b/qpid/cpp/src/tests/run_test.ps1
@@ -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.
+#
+
+$srcdir = Split-Path $myInvocation.InvocationName
+
+# Set up environment and run a test executable or script.
+$env:QPID_DATA_DIR = ""
+$env:BOOST_TEST_SHOW_PROGRESS = "yes"
+
+# The test exe is probably not in the current binary dir - it's usually
+# placed in a subdirectory based on the configuration built in Visual Studio.
+# So check around to see where it is - when located, set the QPID_LIB_DIR
+# and PATH to look in the corresponding configuration off the src directory,
+# one level up.
+$prog = $args[0]
+$is_script = $prog -match ".ps1$"
+if (!$is_script) {
+ . $srcdir\find_prog.ps1 $prog
+ $args[0] = $prog
+ $env:QPID_LIB_DIR = "..\$sub"
+ $env:PATH += ";$dir\$sub;..\$sub"
+}
+
+# If qpidd.port exists and is not empty run test with QPID_PORT set.
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+$si = new-object System.Diagnostics.ProcessStartInfo
+$si.WorkingDirectory = $pwd
+$si.UseShellExecute = $false
+$si.CreateNoWindow = $true
+$si.RedirectStandardOutput = $true
+if ($is_script) {
+ $si.FileName = (get-command powershell.exe).Definition
+ $si.Arguments = $args
+}
+else {
+ $si.FileName = $args[0]
+ if ($args.length -gt 1) {
+ $si.Arguments = $args[1..($args.length-1)]
+ }
+}
+$p = [System.Diagnostics.Process]::Start($si)
+$line = ""
+while (($line = $p.StandardOutput.ReadLine()) -ne $null) {
+ $line
+}
+# ReadToEnd() works, but doesn't show any output until the program exits.
+#$p.StandardOutput.ReadToEnd()
+$p.WaitForExit()
+$status = $p.ExitCode
+exit $status
diff --git a/qpid/cpp/src/tests/sasl.mk b/qpid/cpp/src/tests/sasl.mk
new file mode 100644
index 0000000000..20eaa7c7a5
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl.mk
@@ -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.
+#
+
+# Test that are only relevant if SASL is enabled.
+if HAVE_SASL
+
+check_PROGRAMS+=cluster_authentication_soak
+cluster_authentication_soak_INCLUDES=$(PUBLIC_INCLUDES)
+cluster_authentication_soak_SOURCES=cluster_authentication_soak.cpp ForkedBroker.h ForkedBroker.cpp
+cluster_authentication_soak_LDADD=$(lib_client) $(lib_broker)
+
+# Note: sasl_version is not a test -- it is a tool used by tests.
+check_PROGRAMS+=sasl_version
+sasl_version_SOURCES=sasl_version.cpp
+sasl_version_LDADD=$(lib_client)
+
+TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex_dynamic sasl_fed_ex_link sasl_fed_ex_queue sasl_fed_ex_route sasl_fed_ex_route_cluster sasl_fed_ex_link_cluster sasl_fed_ex_queue_cluster sasl_fed_ex_dynamic_cluster
+LONG_TESTS += run_cluster_authentication_soak
+EXTRA_DIST += run_cluster_authentication_test \
+ sasl_fed \
+ sasl_fed_ex \
+ run_cluster_authentication_soak \
+ sasl_fed_ex_dynamic \
+ sasl_fed_ex_link \
+ sasl_fed_ex_queue \
+ sasl_fed_ex_route \
+ sasl_fed_ex_dynamic_cluster \
+ sasl_fed_ex_link_cluster \
+ sasl_fed_ex_queue_cluster \
+ sasl_fed_ex_route_cluster
+
+
+endif # HAVE_SASL
diff --git a/qpid/cpp/src/tests/sasl_fed b/qpid/cpp/src/tests/sasl_fed
new file mode 100755
index 0000000000..884c44177c
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed
@@ -0,0 +1,166 @@
+#! /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.
+#
+
+source ./test_env.sh
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary becasue this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+# In a distribution, the python tools will be absent.
+if [ ! -f $QPID_CONFIG_EXEC ] || [ ! -f $QPID_ROUTE_EXEC ] ; then
+ echo "python tools absent - skipping sasl_fed."
+ exit 0
+fi
+
+
+sasl_config_file=$builddir/sasl_config
+
+my_random_number=$RANDOM
+tmp_root=/tmp/sasl_fed_$my_random_number
+mkdir -p $tmp_root
+
+
+#--------------------------------------------------
+#echo " Starting broker 1"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 \
+ --data-dir $tmp_root/data_1 \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file $tmp_root/qpidd_1.log \
+ --sasl-config=$sasl_config_file \
+ -d > $tmp_root/broker_1_port
+
+broker_1_port=`cat $tmp_root/broker_1_port`
+
+
+#--------------------------------------------------
+#echo " Starting broker 2"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 \
+ --data-dir $tmp_root/data_2 \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file $tmp_root/qpidd_2.log \
+ --sasl-config=$sasl_config_file \
+ -d > $tmp_root/broker_2_port
+
+broker_2_port=`cat $tmp_root/broker_2_port`
+
+sleep 2
+
+# I am not randomizing these names, because the test creates its own brokers.
+QUEUE_NAME=sasl_fed_queue
+ROUTING_KEY=sasl_fed_queue
+EXCHANGE_NAME=sasl_fedex
+
+#--------------------------------------------------
+#echo " add exchanges"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -a localhost:$broker_1_port add exchange direct $EXCHANGE_NAME
+$QPID_CONFIG_EXEC -a localhost:$broker_2_port add exchange direct $EXCHANGE_NAME
+
+
+#--------------------------------------------------
+#echo " add queues"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -a localhost:$broker_1_port add queue $QUEUE_NAME
+$QPID_CONFIG_EXEC -a localhost:$broker_2_port add queue $QUEUE_NAME
+
+sleep 5
+
+#--------------------------------------------------
+#echo " create bindings"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -a localhost:$broker_1_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+$QPID_CONFIG_EXEC -a localhost:$broker_2_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+
+sleep 5
+
+
+#--------------------------------------------------
+#echo " qpid-route route add"
+#--------------------------------------------------
+$QPID_ROUTE_EXEC route add zag/zag@localhost:$broker_2_port zag/zag@localhost:$broker_1_port $EXCHANGE_NAME $ROUTING_KEY "" "" DIGEST-MD5
+
+sleep 5
+
+
+n_messages=100
+#--------------------------------------------------
+#echo " Sending 100 messages to $broker_1_port "
+#--------------------------------------------------
+$builddir/datagen --count $n_messages | $SENDER_EXEC --mechanism DIGEST-MD5 --username zag --password zag --exchange $EXCHANGE_NAME --routing-key $ROUTING_KEY --port $broker_1_port
+
+sleep 5
+
+#--------------------------------------------------
+#echo " Examine Broker $broker_1_port"
+#--------------------------------------------------
+broker_1_message_count=`$PYTHON_COMMANDS/qpid-stat -q localhost:$broker_1_port | grep sasl_fed_queue | awk '{print $2}'`
+#echo " "
+
+#--------------------------------------------------
+#echo " Examine Broker $broker_2_port"
+#--------------------------------------------------
+broker_2_message_count=`$PYTHON_COMMANDS/qpid-stat -q localhost:$broker_2_port | grep sasl_fed_queue | awk '{print $2}'`
+#echo " "
+
+#--------------------------------------------------
+#echo " Asking brokers to quit."
+#--------------------------------------------------
+$QPIDD_EXEC --port $broker_1_port --quit
+$QPIDD_EXEC --port $broker_2_port --quit
+
+
+#--------------------------------------------------
+#echo "Removing temporary directory $tmp_root"
+#--------------------------------------------------
+rm -rf $tmp_root
+
+if [ "$broker_2_message_count" -eq "$n_messages" ]; then
+ # echo "good: |$broker_2_message_count| == |$n_messages|"
+ exit 0
+else
+ # echo "not ideal: |$broker_1_message_count| != |$n_messages|"
+ exit 1
+fi
+
+
+
+
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex b/qpid/cpp/src/tests/sasl_fed_ex
new file mode 100755
index 0000000000..716a806874
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex
@@ -0,0 +1,361 @@
+#! /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.
+#
+
+#===============================================================================
+# These tests create federated links between two brokers using SASL security.
+# The SASL mechanism used is EXTERNAL, which is satisfied by SSL
+# transport-layer security.
+#===============================================================================
+
+source ./test_env.sh
+
+script_name=`basename $0`
+
+if [ $# -lt 1 ] || [ $# -gt 2 ]
+then
+ echo
+ # These are the four different ways of creating links ( or routes+links )
+ # that the qpid-route command provides.
+ echo "Usage: ${script_name} dynamic|link|queue|route [cluster]"
+ echo
+ exit 1
+fi
+
+# Has the user told us to do clustering ? -----------
+clustering_flag=
+if [ $# -eq "2" ] && [ "$2" == "cluster" ]; then
+ clustering_flag=true
+fi
+
+qpid_route_method=$1
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+print "=========== start sasl_fed_ex $* ============"
+
+
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary because this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+# In a distribution, the python tools will be absent.
+if [ ! -f $QPID_CONFIG_EXEC ] || [ ! -f $QPID_ROUTE_EXEC ] ; then
+ echo "python tools absent - skipping sasl_fed_ex."
+ exit 0
+fi
+
+CERT_DIR=`pwd`/test_cert_db
+CERT_PW_FILE=`pwd`/cert.password
+TEST_HOSTNAME=127.0.0.1
+
+create_certs() {
+ #create certificate and key databases with single, simple, self-signed certificate in it
+ mkdir ${CERT_DIR}
+ certutil -N -d ${CERT_DIR} -f ${CERT_PW_FILE}
+ certutil -S -d ${CERT_DIR} -n ${TEST_HOSTNAME} -s "CN=${TEST_HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil 2> /dev/null
+}
+
+delete_certs() {
+ if [[ -e ${CERT_DIR} ]] ; then
+ print "removing cert dir ${CERT_DIR}"
+ rm -rf ${CERT_DIR}
+ fi
+}
+
+
+CERTUTIL=$(type -p certutil)
+if [[ !(-x $CERTUTIL) ]] ; then
+ echo "No certutil, skipping ssl test";
+ exit 0;
+fi
+
+delete_certs
+create_certs 2> /dev/null
+if [ ! $? ]; then
+ error "Could not create test certificate"
+ exit 1
+fi
+
+sasl_config_dir=$builddir/sasl_config
+
+tmp_root=${builddir}/sasl_fed_ex_temp
+print "results dir is ${tmp_root}"
+rm -rf ${tmp_root}
+mkdir -p $tmp_root
+
+SRC_SSL_PORT=6667
+DST_SSL_PORT=6666
+
+SRC_SSL_PORT_2=6668
+DST_SSL_PORT_2=6669
+
+SRC_TCP_PORT=5801
+DST_TCP_PORT=5807
+
+SRC_TCP_PORT_2=5802
+DST_TCP_PORT_2=5803
+
+CLUSTER_NAME_SUFFIX=`hostname | tr '.' ' ' | awk '{print $1}'`
+CLUSTER_1_NAME=sasl_fed_ex_cluster_1_${CLUSTER_NAME_SUFFIX}
+CLUSTER_2_NAME=sasl_fed_ex_cluster_2_${CLUSTER_NAME_SUFFIX}
+
+print "CLUSTER_1_NAME == ${CLUSTER_1_NAME}"
+print "CLUSTER_2_NAME == ${CLUSTER_2_NAME}"
+
+SSL_LIB=${moduledir}/ssl.so
+CLUSTER_LIB=${moduledir}/cluster.so
+
+export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
+
+export QPID_NO_MODULE_DIR=1
+export QPID_LOAD_MODULE=$SSLCONNECTOR_LIB
+export QPID_SSL_CERT_DB=${CERT_DIR}
+export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE}
+export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
+
+
+
+#######################################
+# Understanding this Plumbing
+#######################################
+# 1. when you establish the route with qpid-route,
+# here is the best termiology to use:
+#
+# qpid-route route add DST SRC
+#
+# 2. DST will connect to SRC through the ssl port of SRC.
+#
+# 3. sender client connects to the tcp port of SRC.
+#
+# 4. sender specifies mechanism ANONYMOUS.
+#
+# 5. DST pulls messages off the temp queue on SRC to itself.
+#
+
+COMMON_BROKER_OPTIONS=" \
+ --ssl-sasl-no-dict \
+ --sasl-config=$sasl_config_dir \
+ --ssl-require-client-authentication \
+ --auth yes \
+ --ssl-cert-db $CERT_DIR \
+ --ssl-cert-password-file $CERT_PW_FILE \
+ --ssl-cert-name $TEST_HOSTNAME \
+ --no-data-dir \
+ --no-module-dir \
+ --load-module ${SSL_LIB} \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --daemon "
+
+
+function start_brokers {
+ if [ $1 ]; then
+ # clustered ----------------------------------------
+ print "Starting SRC cluster"
+
+ print " src broker 1"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_1_NAME} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+
+ broker_ports[0]=${SRC_TCP_PORT}
+
+ print " src broker 2"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT_2} \
+ --ssl-port ${SRC_SSL_PORT_2} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_1_NAME} \
+ --log-to-file $tmp_root/qpidd_src_2.log 2> /dev/null
+
+ broker_ports[1]=${SRC_TCP_PORT_2}
+
+
+ print "Starting DST cluster"
+
+ print " dst broker 1"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_2_NAME} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+ broker_ports[2]=${DST_TCP_PORT}
+
+ print " dst broker 2"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT_2} \
+ --ssl-port ${DST_SSL_PORT_2} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_2_NAME} \
+ --log-to-file $tmp_root/qpidd_dst_2.log 2> /dev/null
+
+ broker_ports[3]=${DST_TCP_PORT_2}
+
+ else
+ # vanilla brokers --------------------------------
+ print "Starting SRC broker"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+
+ broker_ports[0]=${SRC_TCP_PORT}
+
+ print "Starting DST broker"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+ broker_ports[1]=${DST_TCP_PORT}
+ fi
+}
+
+
+function halt_brokers {
+ n_brokers=${#broker_ports[@]}
+ print "Halting ${n_brokers} brokers."
+ for i in $(seq 0 $((${n_brokers} - 1)))
+ do
+ halt_port=${broker_ports[$i]}
+ print "Halting broker $i on port ${halt_port}"
+ $QPIDD_EXEC --port ${halt_port} --quit
+ done
+
+}
+
+
+start_brokers $clustering_flag
+
+
+# I am not randomizing these names, because this test creates its own brokers.
+QUEUE_NAME=sasl_fed_queue
+ROUTING_KEY=sasl_fed_queue
+EXCHANGE_NAME=sasl_fedex
+
+
+print "add exchanges"
+$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME
+$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME
+
+
+print "add queues"
+$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME
+$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add queue $QUEUE_NAME
+
+
+print "create bindings"
+$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+
+
+#
+# NOTE: The SRC broker *must* be referred to as $TEST_HOSTNAME, and not as "localhost".
+# It must be referred to by the exact string given as the Common Name (CN) in the cert,
+# which was created in the function create_certs, above.
+
+
+
+#----------------------------------------------------------------
+# Use qpid-route to create the link, or the link+route, depending
+# on which of its several methods was requested.
+#----------------------------------------------------------------
+if [ ${qpid_route_method} == "dynamic" ]; then
+ print "dynamic add"
+ $QPID_ROUTE_EXEC -t ssl dynamic add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME "" "" EXTERNAL
+elif [ ${qpid_route_method} == "link" ]; then
+ print "link add"
+ $QPID_ROUTE_EXEC -t ssl link add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} EXTERNAL
+elif [ ${qpid_route_method} == "queue" ]; then
+ print "queue add"
+ $QPID_ROUTE_EXEC -t ssl queue add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY EXTERNAL
+elif [ ${qpid_route_method} == "route" ]; then
+ print "route add"
+ $QPID_ROUTE_EXEC -t ssl route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL
+else
+ echo "unknown method: |${qpid_route_method}|"
+ echo " choices are: dynamic|link|queue|route "
+ halt_brokers
+ exit 1
+fi
+
+
+# I don't know how to avoid this sleep yet. It has to come after route-creation
+# to avoid false negatives.
+sleep 5
+
+# This should work the same whether or not we are running a clustered test.
+# In the case of clustered tests, the status is not printed by qpid_route.
+# So in either case, I will look only at the transport field, which should be "ssl".
+print "check the link"
+link_status=$($QPID_ROUTE_EXEC link list localhost:${DST_TCP_PORT} | tail -1 | awk '{print $3}')
+
+halt_brokers
+
+sleep 1
+
+if [ ! ${link_status} ]; then
+ print "link_status is empty"
+ print "result: fail"
+ exit 2
+fi
+
+if [ ${link_status} == "ssl" ]; then
+ print "result: good"
+ # Only remove the tmp_root on success, to permit debugging.
+ print "Removing temporary directory $tmp_root"
+ rm -rf $tmp_root
+ exit 0
+fi
+
+print "link_status has a bad value: ${link_status}"
+print "result: fail"
+exit 3
+
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_dynamic b/qpid/cpp/src/tests/sasl_fed_ex_dynamic
new file mode 100755
index 0000000000..c20b8d69a0
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_dynamic
@@ -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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex dynamic
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster
new file mode 100755
index 0000000000..b0cceccecb
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex dynamic cluster
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_link b/qpid/cpp/src/tests/sasl_fed_ex_link
new file mode 100755
index 0000000000..7b232d4874
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_link
@@ -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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex link
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_link_cluster b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster
new file mode 100755
index 0000000000..4139300b12
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex link cluster
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_queue b/qpid/cpp/src/tests/sasl_fed_ex_queue
new file mode 100755
index 0000000000..be0c10cf63
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_queue
@@ -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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex queue
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster
new file mode 100755
index 0000000000..f251420e08
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source ${srcdir}/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex queue cluster
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_route b/qpid/cpp/src/tests/sasl_fed_ex_route
new file mode 100755
index 0000000000..dd5c4f3cac
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_route
@@ -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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex route
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_route_cluster b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster
new file mode 100755
index 0000000000..a5d1542def
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source ${srcdir}/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex route cluster
+
+
diff --git a/qpid/cpp/src/tests/sasl_test_setup.sh b/qpid/cpp/src/tests/sasl_test_setup.sh
new file mode 100755
index 0000000000..6395ba6ec3
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_test_setup.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.
+#
+
+SASL_PW=/usr/sbin/saslpasswd2
+test -x $SASL_PW || { echo Skipping SASL test, saslpasswd2 not found; exit 0; }
+
+mkdir -p sasl_config
+
+# Create configuration file.
+cat > sasl_config/qpidd.conf <<EOF
+pwcheck_method: auxprop
+auxprop_plugin: sasldb
+sasldb_path: $PWD/sasl_config/qpidd.sasldb
+sql_select: dummy select
+EOF
+
+# Populate temporary sasl db.
+SASLTEST_DB=./sasl_config/qpidd.sasldb
+rm -f $SASLTEST_DB
+echo guest | $SASL_PW -c -p -f $SASLTEST_DB -u QPID guest
+echo zig | $SASL_PW -c -p -f $SASLTEST_DB -u QPID zig
+echo zag | $SASL_PW -c -p -f $SASLTEST_DB -u QPID zag
+
diff --git a/qpid/cpp/src/tests/sasl_version.cpp b/qpid/cpp/src/tests/sasl_version.cpp
new file mode 100644
index 0000000000..db3efe4181
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_version.cpp
@@ -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.
+ *
+ */
+
+#include <iostream>
+
+#include "sasl/sasl.h"
+
+
+/*
+ Some tests need to distinguish between different versions of
+ SASL. This encodes and outputs the version number as an integer
+ for easy use in testing scripts.
+*/
+
+int
+main ( )
+{
+ // I assume that these are 8-bit quantities....
+ int sasl_version = (SASL_VERSION_MAJOR << 16) +
+ (SASL_VERSION_MINOR << 8) +
+ SASL_VERSION_STEP;
+
+ std::cout << sasl_version << std::endl;
+
+ return 0;
+}
+
+
+
+
diff --git a/qpid/cpp/src/tests/sender.cpp b/qpid/cpp/src/tests/sender.cpp
new file mode 100644
index 0000000000..063b5e87dc
--- /dev/null
+++ b/qpid/cpp/src/tests/sender.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/client/QueueOptions.h>
+#include <qpid/Exception.h>
+#include "TestOptions.h"
+
+#include "qpid/messaging/Message.h" // Only for Statistics
+#include "Statistics.h"
+
+#include <fstream>
+#include <iostream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string destination;
+ string key;
+ uint sendEos;
+ bool durable;
+ uint ttl;
+ string lvqMatchValue;
+ string lvqMatchFile;
+ bool reportTotal;
+ int reportEvery;
+ bool reportHeader;
+
+ Args() :
+ key("test-queue"), sendEos(0), durable(false), ttl(0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true)
+ {
+ addOptions()
+ ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to")
+ ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages")
+ ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("lvq-match-value", qpid::optValue(lvqMatchValue, "KEY"), "The value to set for the LVQ match key property")
+ ("lvq-match-file", qpid::optValue(lvqMatchFile, "FILE"), "A file containing values to set for the LVQ match key property")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ;
+ }
+};
+
+const string EOS("eos");
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(Reporter<Throughput>& reporter, const std::string& destination, const std::string& key, uint sendEos, bool durable, uint ttl,
+ const std::string& lvqMatchValue, const std::string& lvqMatchFile);
+ void execute(AsyncSession& session, bool isRetry);
+
+ private:
+ Reporter<Throughput>& reporter;
+ messaging::Message dummyMessage;
+ const std::string destination;
+ MessageReplayTracker sender;
+ Message message;
+ const uint sendEos;
+ uint sent;
+ std::ifstream lvqMatchValues;
+};
+
+Sender::Sender(Reporter<Throughput>& rep, const std::string& dest, const std::string& key, uint eos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile) :
+ reporter(rep), destination(dest), sender(10), message("", key), sendEos(eos), sent(0) , lvqMatchValues(lvqMatchFile.c_str())
+{
+ if (durable){
+ message.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+
+ if (ttl) {
+ message.getDeliveryProperties().setTtl(ttl);
+ }
+
+ if (!lvqMatchValue.empty()) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqMatchValue);
+ }
+}
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ string data;
+ while (getline(std::cin, data)) {
+ message.setData(data);
+ //message.getHeaders().setInt("SN", ++sent);
+ string matchKey;
+ if (lvqMatchValues && getline(lvqMatchValues, matchKey)) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, matchKey);
+ }
+ reporter.message(dummyMessage); // For statistics
+ sender.send(message, destination);
+ }
+ for (uint i = sendEos; i > 0; --i) {
+ message.setData(EOS);
+ sender.send(message, destination);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ Reporter<Throughput> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+ FailoverManager connection(opts.con);
+ Sender sender(reporter, opts.destination, opts.key, opts.sendEos, opts.durable, opts.ttl, opts.lvqMatchValue, opts.lvqMatchFile);
+ connection.execute(sender);
+ connection.close();
+ if (opts.reportTotal) reporter.report();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << "Failed: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/shared_perftest b/qpid/cpp/src/tests/shared_perftest
new file mode 100755
index 0000000000..cc192d25bd
--- /dev/null
+++ b/qpid/cpp/src/tests/shared_perftest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_perftest 100000 --mode shared --npubs 16 --nsubs 16
diff --git a/qpid/cpp/src/tests/shlibtest.cpp b/qpid/cpp/src/tests/shlibtest.cpp
new file mode 100644
index 0000000000..5655eb7e64
--- /dev/null
+++ b/qpid/cpp/src/tests/shlibtest.cpp
@@ -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.
+ *
+ */
+
+namespace qpid {
+namespace tests {
+
+int* loaderData = 0;
+extern "C"
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+void callMe(int *i) { loaderData=i; }
+
+struct OnUnload { ~OnUnload() { *loaderData=42; } };
+OnUnload unloader; // For destructor.
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ssl.mk b/qpid/cpp/src/tests/ssl.mk
new file mode 100644
index 0000000000..435db0c55b
--- /dev/null
+++ b/qpid/cpp/src/tests/ssl.mk
@@ -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.
+#
+
+TESTS+=ssl_test
+EXTRA_DIST+=ssl_test
+CLEAN_LOCAL += test_cert_db cert.password
diff --git a/qpid/cpp/src/tests/ssl_test b/qpid/cpp/src/tests/ssl_test
new file mode 100755
index 0000000000..cbf75eb237
--- /dev/null
+++ b/qpid/cpp/src/tests/ssl_test
@@ -0,0 +1,142 @@
+#!/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.
+#
+
+# Run a simple test over SSL
+source ./test_env.sh
+
+CONFIG=$(dirname $0)/config.null
+CERT_DIR=`pwd`/test_cert_db
+CERT_PW_FILE=`pwd`/cert.password
+TEST_HOSTNAME=127.0.0.1
+TEST_CLIENT_CERT=rumplestiltskin
+COUNT=10
+
+trap cleanup EXIT
+
+error() { echo $*; exit 1; }
+
+create_certs() {
+ #create certificate and key databases with single, simple, self-signed certificate in it
+ mkdir ${CERT_DIR}
+ certutil -N -d ${CERT_DIR} -f ${CERT_PW_FILE}
+ certutil -S -d ${CERT_DIR} -n ${TEST_HOSTNAME} -s "CN=${TEST_HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil
+ certutil -S -d ${CERT_DIR} -n ${TEST_CLIENT_CERT} -s "CN=${TEST_CLIENT_CERT}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil
+}
+
+delete_certs() {
+ if [[ -e ${CERT_DIR} ]] ; then
+ rm -rf ${CERT_DIR}
+ fi
+}
+
+COMMON_OPTS="--daemon --no-data-dir --no-module-dir --auth no --config $CONFIG --load-module $SSL_LIB --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE --ssl-cert-name $TEST_HOSTNAME --require-encryption"
+start_broker() { # $1 = extra opts
+ ../qpidd --transport ssl --port 0 --ssl-port 0 $COMMON_OPTS $1;
+}
+
+stop_brokers() {
+ test -n "$PORT" && ../qpidd --no-module-dir -qp $PORT
+ test -n "$PORT2" && ../qpidd --no-module-dir -qp $PORT2
+ PORT=""
+ PORT2=""
+}
+
+cleanup() {
+ stop_brokers
+ delete_certs
+}
+
+CERTUTIL=$(type -p certutil)
+if [[ !(-x $CERTUTIL) ]] ; then
+ echo "No certutil, skipping ssl test";
+ exit 0;
+fi
+
+if [[ !(-e ${CERT_PW_FILE}) ]] ; then
+ echo password > ${CERT_PW_FILE}
+fi
+delete_certs
+create_certs || error "Could not create test certificate"
+PORT=`start_broker` || error "Could not start broker"
+echo "Running SSL test on port $PORT"
+export QPID_NO_MODULE_DIR=1
+export QPID_LOAD_MODULE=$SSLCONNECTOR_LIB
+export QPID_SSL_CERT_DB=${CERT_DIR}
+export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE}
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary
+
+## Test connection with a URL
+URL=amqp:ssl:$TEST_HOSTNAME:$PORT
+./qpid-send -b $URL --content-string=hello -a "foo;{create:always}"
+MSG=`./qpid-receive -b $URL -a "foo;{create:always}" --messages 1`
+test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; }
+
+#### Client Authentication tests
+
+PORT2=`start_broker --ssl-require-client-authentication` || error "Could not start broker"
+echo "Running SSL client authentication test on port $PORT2"
+URL=amqp:ssl:$TEST_HOSTNAME:$PORT2
+
+## See if you can set the SSL cert-name for the connection
+./qpid-send -b $URL --connection-options "{ssl-cert-name: $TEST_CLIENT_CERT }" --content-string=hello -a "bar;{create:always}"
+MSG2=`./qpid-receive -b $URL --connection-options "{ssl-cert-name: $TEST_CLIENT_CERT }" -a "bar;{create:always}" --messages 1`
+test "$MSG2" = "hello" || { echo "receive failed '$MSG2' != 'hello'"; exit 1; }
+
+## Make sure that connect fails with an invalid SSL cert-name
+./qpid-send -b $URL --connection-options "{ssl-cert-name: pignose }" --content-string=hello -a "baz;{create:always}" 2>/dev/null 1>/dev/null
+MSG3=`./qpid-receive -b $URL --connection-options "{ssl-cert-name: pignose }" -a "baz;{create:always}" --messages 1 2>/dev/null`
+test "$MSG3" = "" || { echo "receive succeeded without valid ssl cert '$MSG3' != ''"; exit 1; }
+
+stop_brokers
+
+test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
+
+## Test failover in a cluster using SSL only
+. $srcdir/ais_check # Will exit if clustering not enabled.
+
+pick_port() {
+ # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
+ PICK=`../qpidd --no-module-dir -dp0`
+ ../qpidd --no-module-dir -qp $PICK
+ echo $PICK
+}
+ssl_cluster_broker() { # $1 = port
+ ../qpidd $COMMON_OPTS --load-module $CLUSTER_LIB --cluster-name ssl_test.$HOSTNAME.$$ --cluster-url amqp:ssl:$TEST_HOSTNAME:$1 --port 0 --ssl-port $1 --transport ssl > /dev/null
+ # Wait for broker to be ready
+ qpid-ping -Pssl -b $TEST_HOSTNAME -qp $1 || { echo "Cannot connect to broker on $1"; exit 1; }
+ echo "Running SSL cluster broker on port $1"
+}
+
+PORT1=`pick_port`; ssl_cluster_broker $PORT1
+PORT2=`pick_port`; ssl_cluster_broker $PORT2
+
+# Pipe receive output to uniq to remove duplicates
+./qpid-receive --connection-options "{reconnect:true, reconnect-timeout:5}" --failover-updates -b amqp:ssl:$TEST_HOSTNAME:$PORT1 -a "foo;{create:always}" -f | uniq > ssl_test_receive.tmp &
+./qpid-send -b amqp:ssl:$TEST_HOSTNAME:$PORT2 --content-string=one -a "foo;{create:always}"
+../qpidd --no-module-dir -qp $PORT1 # Kill broker 1 receiver should fail-over.
+./qpid-send -b amqp:ssl:$TEST_HOSTNAME:$PORT2 --content-string=two -a "foo;{create:always}" --send-eos 1
+wait # Wait for qpid-receive
+{ echo one; echo two; } > ssl_test_receive.cmp
+diff ssl_test_receive.tmp ssl_test_receive.cmp || { echo "Failover failed"; exit 1; }
+rm -f ssl_test_receive.*
+
diff --git a/qpid/cpp/src/tests/start_broker b/qpid/cpp/src/tests/start_broker
new file mode 100755
index 0000000000..093c44051a
--- /dev/null
+++ b/qpid/cpp/src/tests/start_broker
@@ -0,0 +1,24 @@
+#!/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.
+#
+
+# Start a test broker.
+srcdir=`dirname $0`
+exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-to-file qpidd.log "$@" > qpidd.port
diff --git a/qpid/cpp/src/tests/start_broker.ps1 b/qpid/cpp/src/tests/start_broker.ps1
new file mode 100644
index 0000000000..9263262b9f
--- /dev/null
+++ b/qpid/cpp/src/tests/start_broker.ps1
@@ -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.
+#
+
+# Get the directory where this script resides.
+function Get-ScriptPath
+ { Split-Path $myInvocation.ScriptName }
+
+# Start a test broker and capture it's port (from stdout) to qpidd.port
+# This script will exit immediately after spawning the broker process. To avoid
+# running more tests before the broker is initialized, wait for the qpidd.port
+# file to appear before exiting.
+if (Test-Path qpidd.port) {
+ Remove-Item qpidd.port
+}
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+$cmdline = "$prog --auth=no --no-module-dir --port=0 --log-to-file qpidd.log $args | foreach { set-content qpidd.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+$srcdir = Get-ScriptPath
+. $srcdir\background.ps1 $cmdblock
+
+$wait_time = 0
+while (!(Test-Path qpidd.port) -and ($wait_time -lt 10)) {
+ Start-Sleep 2
+ $wait_time += 2
+}
+if (Test-Path qpidd.port) {
+ exit 0
+}
+"Time out waiting for broker to start"
+exit 1
diff --git a/qpid/cpp/src/tests/start_cluster b/qpid/cpp/src/tests/start_cluster
new file mode 100755
index 0000000000..bc35a2eddc
--- /dev/null
+++ b/qpid/cpp/src/tests/start_cluster
@@ -0,0 +1,42 @@
+#!/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.
+#
+
+# Start a cluster of brokers on local host, put the list of ports for cluster members in cluster.ports
+#
+
+# Execute command with the ais group set.
+source ./test_env.sh
+. `dirname $0`/ais_check
+
+rm -f cluster*.log cluster.ports qpidd.port
+
+SIZE=${1:-3}; shift
+CLUSTER=$HOSTNAME.$$
+OPTS="-d --no-module-dir --load-module $CLUSTER_LIB --cluster-name=$CLUSTER --auth=no --log-enable notice+ --log-enable debug+:cluster $@"
+
+for (( i=0; i<SIZE; ++i )); do
+ DDIR=`mktemp -d /tmp/start_cluster.XXXXXXXXXX`
+ PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS --data-dir=$DDIR` || exit 1
+ echo $PORT >> cluster.ports
+done
+
+head -n 1 cluster.ports > qpidd.port # First member's port for tests.
+
diff --git a/qpid/cpp/src/tests/start_cluster_hosts b/qpid/cpp/src/tests/start_cluster_hosts
new file mode 100755
index 0000000000..778b4248da
--- /dev/null
+++ b/qpid/cpp/src/tests/start_cluster_hosts
@@ -0,0 +1,70 @@
+#!/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.
+#
+
+#
+# Start a cluster of brokers on local host, put the list of host port addresses
+# in cluster.ports
+#
+# Arguments: [-k] [-p port] HOST [HOST...]
+# -p port to start broker on, can be 0. Actual ports recorded in cluster.addr.
+# -k kill any qpidd processes owned by this user before starting.
+#
+# Start a broker on each named host. Name a host twice to start multiple brokers.
+#
+# You must be able to ssh to each host and be in group ais.
+# $QPIDD must be executable on each host.
+# Logs go to syslog on each host, with a unique prefix per broker.
+#
+
+QPIDD=${QPIDD:-$PWD/../qpidd}
+LIBQPIDCLUSTER=${LIBQPIDCLUSTER:-$PWD/../.libs/cluster.so}
+NAME=$USER # User name is default cluster name.
+RESTART=NO
+
+while getopts "kp:n:q:r" ARG ; do
+ case $ARG in
+ k) KILL=yes ;;
+ p) PORT="$OPTARG" ;;
+ n) NAME=$OPTARG ;;
+ q) QPIDD=$OPTARG ;;
+ l) LIBQPIDCLUSTER=$OPTARG ;;
+ r) RESTART=yes ;;
+ *) echo "Error parsing options: $ARG"; exit 1 ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+test -n "$PORT" && PORTOPT="-p $PORT"
+test "$KILL" = yes && KILL="$QPIDD --no-module-dir -q $PORTOPT ;"
+CLUSTER=${*:-$CLUSTER} # Use args or env
+test -z "$CLUSTER" && { echo Must specify at least one host; exit 1; }
+
+
+OPTS="-d $PORTOPT --load-module $LIBQPIDCLUSTER --cluster-name=$NAME --no-data-dir --auth=no --log-to-syslog --log-enable=info+"
+
+num=0
+for h in $CLUSTER; do
+ num=`expr $num + 1` # Give a unique log prefix to each node.
+ cmd="$KILL $QPIDD $OPTS --log-prefix $num.$h"
+ out=`echo "$cmd" | ssh $h newgrp ais` || { echo == $h error: $out ; exit 1; }
+ if [ "$PORT" = 0 ] ; then p=$out; else p=$PORT; fi
+ echo "$h $p"
+done
+
diff --git a/qpid/cpp/src/tests/stop_broker b/qpid/cpp/src/tests/stop_broker
new file mode 100755
index 0000000000..248fd1fc5c
--- /dev/null
+++ b/qpid/cpp/src/tests/stop_broker
@@ -0,0 +1,41 @@
+#!/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.
+#
+
+# Stop the broker, check for errors.
+#
+QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+rm -f qpidd.port
+
+../qpidd --no-module-dir --quit || ERROR=1
+
+# Check qpidd.log.
+egrep 'warning\|error\|critical' qpidd.log && {
+ echo "WARNING: Suspicious broker log entries in qpidd.log, above."
+}
+
+# Check valgrind log.
+if test -n "$VALGRIND"; then
+ . `dirname $0`/vg_check $VG_LOG*
+ vg_check qpidd.vglog* || ERROR=1
+fi
+
+exit $ERROR
diff --git a/qpid/cpp/src/tests/stop_broker.ps1 b/qpid/cpp/src/tests/stop_broker.ps1
new file mode 100644
index 0000000000..4fdeb26e2b
--- /dev/null
+++ b/qpid/cpp/src/tests/stop_broker.ps1
@@ -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.
+#
+
+# Stop the broker, check for errors.
+Get-Content -path qpidd.port -totalCount 1 | Set-Variable -name qpid_port
+Remove-Item qpidd.port
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+
+# Piping the output makes the script wait for qpidd to finish.
+Invoke-Expression "$prog --quit --port $qpid_port" | Write-Output
+$stopped = $?
+
+# Check qpidd.log.
+filter bad_stuff {
+ $_ -match "( warning | error | critical )"
+}
+
+$qpidd_errors = $false
+Get-Content -path qpidd.log | where { bad_stuff } | Out-Default | Set-Variable -name qpidd_errors -value $true
+if ($qpidd_errors -eq $true) {
+ "WARNING: Suspicious broker log entries in qpidd.log, above."
+}
+if ($stopped -eq $true) {
+ exit 0
+}
+exit 1
diff --git a/qpid/cpp/src/tests/stop_cluster b/qpid/cpp/src/tests/stop_cluster
new file mode 100755
index 0000000000..d598a2255a
--- /dev/null
+++ b/qpid/cpp/src/tests/stop_cluster
@@ -0,0 +1,33 @@
+#!/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.
+#
+
+# Stop brokers on ports listed in cluster.ports
+
+PORTS=`cat cluster.ports`
+for PORT in $PORTS ; do
+ $QPIDD_EXEC --no-module-dir -qp $PORT || ERROR="$ERROR $PORT"
+done
+rm -f cluster.ports qpidd.port
+
+if [ -n "$ERROR" ]; then
+ echo "Errors stopping brokers on ports: $ERROR"
+ exit 1
+fi
diff --git a/qpid/cpp/src/tests/store.py b/qpid/cpp/src/tests/store.py
new file mode 100755
index 0000000000..77e8a78e5d
--- /dev/null
+++ b/qpid/cpp/src/tests/store.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import errno, os, time
+from brokertest import *
+from qpid import compat, session
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import Message, uuid4
+from qpid.queue import Empty
+
+class StoreTests(BrokerTest):
+
+ XA_RBROLLBACK = 1
+ XA_RBTIMEOUT = 2
+ XA_OK = 0
+ tx_counter = 0
+
+ def configure(self, config):
+ self.config = config
+ self.defines = self.config.defines
+ BrokerTest.configure(self, config)
+
+ def setup_connection(self):
+ socket = connect(self._broker.host(), self._broker.port())
+ return Connection(sock=socket)
+
+ def setup_session(self):
+ self.conn.start()
+ return self.conn.session(str(uuid4()))
+
+ def start_session(self):
+ self.conn = self.setup_connection()
+ self.ssn = self.setup_session()
+
+ def setUp(self):
+ BrokerTest.setUp(self)
+ self._broker = self.broker()
+ self.start_session()
+
+ def cycle_broker(self):
+ # tearDown resets working dir; change it back after.
+ d = os.getcwd()
+ BrokerTest.tearDown(self)
+ os.chdir(d)
+ self._broker = None
+ self._broker = self.broker()
+ self.conn = self.setup_connection()
+ self.ssn = self.setup_session()
+
+ def xid(self, txid):
+ StoreTests.tx_counter += 1
+ branchqual = "v%s" % StoreTests.tx_counter
+ return self.ssn.xid(format=0, global_id=txid, branch_id=branchqual)
+
+ def testDurableExchange(self):
+ try:
+ self.ssn.exchange_delete(exchange="DE1")
+ except:
+ # restart the session busted from the exception
+ self.start_session()
+
+ self.ssn.exchange_declare(exchange="DE1", type="direct", durable=True)
+ response = self.ssn.exchange_query(name="DE1")
+ self.assert_(response.durable)
+ self.assert_(not response.not_found)
+
+ # Cycle the broker and make sure the exchange recovers
+ self.cycle_broker()
+ response = self.ssn.exchange_query(name="DE1")
+ self.assert_(response.durable)
+ self.assert_(not response.not_found)
+
+ self.ssn.exchange_delete(exchange="DE1")
+
+ def testDurableQueues(self):
+ try:
+ self.ssn.queue_delete(queue="DQ1")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="DQ1", durable=True)
+ response = self.ssn.queue_query(queue="DQ1")
+ self.assertEqual("DQ1", response.queue)
+ self.assert_(response.durable)
+
+ # Cycle the broker and make sure the queue recovers
+ self.cycle_broker()
+ response = self.ssn.queue_query(queue="DQ1")
+ self.assertEqual("DQ1", response.queue)
+ self.assert_(response.durable)
+
+ self.ssn.queue_delete(queue="DQ1")
+
+ def testDurableBindings(self):
+ try:
+ self.ssn.exchange_unbind(queue="DB_Q1", exchange="DB_E1", binding_key="K1")
+ except:
+ self.start_session()
+ try:
+ self.ssn.exchange_delete(exchange="DB_E1")
+ except:
+ self.start_session()
+ try:
+ self.ssn.queue_delete(queue="DB_Q1")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="DB_Q1", durable=True)
+ self.ssn.exchange_declare(exchange="DB_E1", type="direct", durable=True)
+ self.ssn.exchange_bind(exchange="DB_E1", queue="DB_Q1", binding_key="K1")
+
+ # Cycle the broker and make sure the binding recovers
+ self.cycle_broker()
+ response = self.ssn.exchange_bound(exchange="DB_E1", queue="DB_Q1", binding_key="K1")
+ self.assert_(not response.exchange_not_found)
+ self.assert_(not response.queue_not_found)
+ self.assert_(not response.queue_not_matched)
+ self.assert_(not response.key_not_matched)
+
+ self.ssn.exchange_unbind(queue="DB_Q1", exchange="DB_E1", binding_key="K1")
+ self.ssn.exchange_delete(exchange="DB_E1")
+ self.ssn.queue_delete(queue="DB_Q1")
+
+ def testDtxRecoverPrepared(self):
+ try:
+ self.ssn.exchange_unbind(queue="Dtx_Q", exchange="Dtx_E", binding_key="Dtx")
+ except:
+ self.start_session()
+ try:
+ self.ssn.exchange_delete(exchange="Dtx_E")
+ except:
+ self.start_session()
+ try:
+ self.ssn.queue_delete(queue="Dtx_Q")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="Dtx_Q", auto_delete=False, durable=True)
+ self.ssn.exchange_declare(exchange="Dtx_E", type="direct", durable=True)
+ self.ssn.exchange_bind(exchange="Dtx_E", queue="Dtx_Q", binding_key="Dtx")
+ txid = self.xid("DtxRecoverPrepared")
+ self.ssn.dtx_select()
+ self.ssn.dtx_start(xid=txid)
+ # 2 = delivery_mode.persistent
+ dp = self.ssn.delivery_properties(routing_key="Dtx_Q", delivery_mode=2)
+ self.ssn.message_transfer(message=Message(dp, "transactional message"))
+ self.ssn.dtx_end(xid=txid)
+ self.assertEqual(self.XA_OK, self.ssn.dtx_prepare(xid=txid).status)
+ # Cycle the broker and make sure the xid is there, the message is not
+ # queued.
+ self.cycle_broker()
+ # The txid should be recovered and in doubt
+ xids = self.ssn.dtx_recover().in_doubt
+ xid_matched = False
+ for x in xids:
+ self.assertEqual(txid.format, x.format)
+ self.assertEqual(txid.global_id, x.global_id)
+ self.assertEqual(txid.branch_id, x.branch_id)
+ xid_matched = True
+ self.assert_(xid_matched)
+ self.ssn.message_subscribe(destination="dtx_msgs", queue="Dtx_Q", accept_mode=1, acquire_mode=0)
+ self.ssn.message_flow(unit = 1, value = 0xFFFFFFFFL, destination = "dtx_msgs")
+ self.ssn.message_flow(unit = 0, value = 10, destination = "dtx_msgs")
+ message_arrivals = self.ssn.incoming("dtx_msgs")
+ try:
+ message_arrivals.get(timeout=1)
+ assert False, 'Message present in queue before commit'
+ except Empty: pass
+ self.ssn.dtx_select()
+ self.assertEqual(self.XA_OK, self.ssn.dtx_commit(xid=txid, one_phase=False).status)
+ try:
+ msg = message_arrivals.get(timeout=1)
+ self.assertEqual("transactional message", msg.body)
+ except Empty:
+ assert False, 'Message should be present after dtx commit but is not'
+
+ self.ssn.exchange_unbind(queue="Dtx_Q", exchange="Dtx_E", binding_key="Dtx")
+ self.ssn.exchange_delete(exchange="Dtx_E")
+ self.ssn.queue_delete(queue="Dtx_Q")
diff --git a/qpid/cpp/src/tests/test.xquery b/qpid/cpp/src/tests/test.xquery
new file mode 100644
index 0000000000..4cfe3af02d
--- /dev/null
+++ b/qpid/cpp/src/tests/test.xquery
@@ -0,0 +1,6 @@
+ let $w := ./weather
+ return $w/station = 'Raleigh-Durham International Airport (KRDU)'
+ and $w/temperature_f > 50
+ and $w/temperature_f - $w/dewpoint > 5
+ and $w/wind_speed_mph > 7
+ and $w/wind_speed_mph < 20
diff --git a/qpid/cpp/src/tests/test_env.sh.in b/qpid/cpp/src/tests/test_env.sh.in
new file mode 100644
index 0000000000..842d7729cb
--- /dev/null
+++ b/qpid/cpp/src/tests/test_env.sh.in
@@ -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.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+# Environment variables substituted by configure/cmake.
+srcdir=`absdir @abs_srcdir@`
+builddir=`absdir @abs_builddir@`
+top_srcdir=`absdir @abs_top_srcdir@`
+top_builddir=`absdir @abs_top_builddir@`
+moduledir=$top_builddir/src@builddir_lib_suffix@
+testmoduledir=$builddir@builddir_lib_suffix@
+export QPID_INSTALL_PREFIX=@prefix@
+
+# Python paths and directories
+export PYTHON_DIR=$builddir/python
+export QPID_PYTHON_TEST=$PYTHON_DIR/commands/qpid-python-test
+if [ ! -d $PYTHON_DIR -a -d $top_srcdir/../python ]; then
+ export PYTHON_DIR=$top_srcdir/../python
+ export QPID_PYTHON_TEST=$PYTHON_DIR/qpid-python-test
+fi
+export QPID_TESTS=$top_srcdir/../tests
+export QPID_TESTS_PY=$QPID_TESTS/src/py
+export QPID_TOOLS=$top_srcdir/../tools
+export QMF_LIB=$top_srcdir/../extras/qmf/src/py
+export PYTHON_COMMANDS=$QPID_TOOLS/src/py
+export PYTHONPATH=$srcdir:$PYTHON_DIR:$PYTHON_COMMANDS:$QPID_TESTS_PY:$QMF_LIB:$PYTHONPATH
+export QPID_CONFIG_EXEC=$PYTHON_COMMANDS/qpid-config
+export QPID_ROUTE_EXEC=$PYTHON_COMMANDS/qpid-route
+export QPID_CLUSTER_EXEC=$PYTHON_COMMANDS/qpid-cluster
+
+# Executables
+export QPIDD_EXEC=$top_builddir/src/qpidd
+export QPID_WATCHDOG_EXEC=$top_builddir/src/qpidd_watchdog
+
+# Test executables
+export QPID_TEST_EXEC_DIR=$builddir
+export RECEIVER_EXEC=$QPID_TEST_EXEC_DIR/receiver
+export SENDER_EXEC=$QPID_TEST_EXEC_DIR/sender
+
+# Path
+export PATH=$top_builddir/src:$builddir:$srcdir:$PYTHON_COMMANDS:$QPID_TEST_EXEC_DIR:$PATH
+
+# Modules
+export TEST_STORE_LIB=$testmoduledir/test_store.so
+
+exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
+exportmodule ACL_LIB acl.so
+exportmodule CLUSTER_LIB cluster.so
+exportmodule REPLICATING_LISTENER_LIB replicating_listener.so
+exportmodule REPLICATION_EXCHANGE_LIB replication_exchange.so
+exportmodule SSLCONNECTOR_LIB sslconnector.so
+exportmodule SSL_LIB ssl.so
+exportmodule WATCHDOG_LIB watchdog.so
+exportmodule XML_LIB xml.so
+
+# Qpid options
+export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules
+export QPID_DATA_DIR= # Default to no data dir, not ~/.qpidd
+
+# Options for boost test framework
+export BOOST_TEST_SHOW_PROGRESS=yes
+export BOOST_TEST_CATCH_SYSTEM_ERRORS=no
diff --git a/qpid/cpp/src/tests/test_store.cpp b/qpid/cpp/src/tests/test_store.cpp
new file mode 100644
index 0000000000..257e77b6b4
--- /dev/null
+++ b/qpid/cpp/src/tests/test_store.cpp
@@ -0,0 +1,178 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**@file
+ * Plug-in message store for tests.
+ *
+ * Add functionality as required, build up a comprehensive set of
+ * features to support persistent behavior tests.
+ *
+ * Current features special "action" messages can:
+ * - raise exception from enqueue.
+ * - force host process to exit.
+ * - do async completion after a delay.
+ */
+
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include <boost/cast.hpp>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+#include <fstream>
+
+using namespace qpid;
+using namespace broker;
+using namespace std;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct TestStoreOptions : public Options {
+
+ string name;
+ string dump;
+
+ TestStoreOptions() : Options("Test Store Options") {
+ addOptions()
+ ("test-store-name", optValue(name, "NAME"), "Name of test store instance.")
+ ("test-store-dump", optValue(dump, "FILE"), "File to dump enqueued messages.")
+ ;
+ }
+};
+
+struct Completer : public Runnable {
+ boost::intrusive_ptr<PersistableMessage> message;
+ int usecs;
+ Completer(boost::intrusive_ptr<PersistableMessage> m, int u) : message(m), usecs(u) {}
+ void run() {
+ qpid::sys::usleep(usecs);
+ message->enqueueComplete();
+ delete this;
+ }
+};
+
+class TestStore : public NullMessageStore {
+ public:
+ TestStore(const TestStoreOptions& opts, Broker& broker_)
+ : options(opts), name(opts.name), broker(broker_)
+ {
+ QPID_LOG(info, "TestStore name=" << name << " dump=" << options.dump);
+ if (!options.dump.empty())
+ dump.reset(new ofstream(options.dump.c_str()));
+ }
+
+ ~TestStore() {
+ for_each(threads.begin(), threads.end(), boost::bind(&Thread::join, _1));
+ }
+
+ virtual bool isNull() const { return false; }
+
+ void enqueue(TransactionContext* ,
+ const boost::intrusive_ptr<PersistableMessage>& pmsg,
+ const PersistableQueue& )
+ {
+ Message* msg = dynamic_cast<Message*>(pmsg.get());
+ assert(msg);
+
+ // Dump the message if there is a dump file.
+ if (dump.get()) {
+ msg->getFrames().getMethod()->print(*dump);
+ *dump << endl << " ";
+ msg->getFrames().getHeaders()->print(*dump);
+ *dump << endl << " ";
+ *dump << msg->getFrames().getContentSize() << endl;
+ }
+
+ // Check the message for special instructions.
+ string data = msg->getFrames().getContent();
+ size_t i = string::npos;
+ size_t j = string::npos;
+ if (strncmp(data.c_str(), TEST_STORE_DO.c_str(), strlen(TEST_STORE_DO.c_str())) == 0
+ && (i = data.find(name+"[")) != string::npos
+ && (j = data.find("]", i)) != string::npos)
+ {
+ size_t start = i+name.size()+1;
+ string action = data.substr(start, j-start);
+
+ if (action == EXCEPTION) {
+ throw Exception(QPID_MSG("TestStore " << name << " throwing exception for: " << data));
+ }
+ else if (action == EXIT_PROCESS) {
+ // FIXME aconway 2009-04-10: this is a dubious way to
+ // close the process at best, it can cause assertions or seg faults
+ // rather than clean exit.
+ QPID_LOG(critical, "TestStore " << name << " forcing process exit for: " << data);
+ exit(0);
+ }
+ else if (strncmp(action.c_str(), ASYNC.c_str(), strlen(ASYNC.c_str())) == 0) {
+ std::string delayStr(action.substr(ASYNC.size()));
+ int delay = boost::lexical_cast<int>(delayStr);
+ threads.push_back(Thread(*new Completer(msg, delay)));
+ }
+ else {
+ QPID_LOG(error, "TestStore " << name << " unknown action " << action);
+ msg->enqueueComplete();
+ }
+ }
+ else
+ msg->enqueueComplete();
+ }
+
+ private:
+ static const string TEST_STORE_DO, EXCEPTION, EXIT_PROCESS, ASYNC;
+ TestStoreOptions options;
+ string name;
+ Broker& broker;
+ vector<Thread> threads;
+ std::auto_ptr<ofstream> dump;
+};
+
+const string TestStore::TEST_STORE_DO = "TEST_STORE_DO: ";
+const string TestStore::EXCEPTION = "exception";
+const string TestStore::EXIT_PROCESS = "exit_process";
+const string TestStore::ASYNC="async ";
+
+struct TestStorePlugin : public Plugin {
+
+ TestStoreOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize (Plugin::Target& target)
+ {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ boost::shared_ptr<MessageStore> p(new TestStore(options, *broker));
+ broker->setStore (p);
+ }
+
+ void initialize(qpid::Plugin::Target&) {}
+};
+
+static TestStorePlugin pluginInstance;
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/test_tools.h b/qpid/cpp/src/tests/test_tools.h
new file mode 100644
index 0000000000..de672f938a
--- /dev/null
+++ b/qpid/cpp/src/tests/test_tools.h
@@ -0,0 +1,106 @@
+#ifndef TEST_TOOLS_H
+#define TEST_TOOLS_H
+
+/*
+ *
+ * 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.
+ *
+ */
+#include "qpid/log/Logger.h"
+
+#include <limits.h> // Include before boost/test headers.
+#include <boost/test/test_tools.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/assign/list_of.hpp>
+#include <vector>
+#include <set>
+#include <ostream>
+#include <sstream>
+#include <exception>
+
+// Print a sequence
+template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) {
+ std::copy(seq.begin(), seq.end(), std::ostream_iterator<typename T::value_type>(o, " "));
+ return o;
+}
+
+// Compare sequences
+template <class T, class U>
+bool seqEqual(const T& a, const U& b) {
+ typename T::const_iterator i = a.begin();
+ typename U::const_iterator j = b.begin();
+ while (i != a.end() && j != b.end() && *i == *j) { ++i; ++j; }
+ return (i == a.end()) && (j == b.end());
+}
+
+// ostream and == operators so we can compare vectors and sets with
+// boost::assign::list_of with BOOST_CHECK_EQUALS
+namespace std { // In namespace std so boost can find them.
+
+template <class T>
+ostream& operator<<(ostream& o, const vector<T>& v) { return seqPrint(o, v); }
+
+template <class T>
+ostream& operator<<(ostream& o, const set<T>& v) { return seqPrint(o, v); }
+
+template <class T>
+ostream& operator<<(ostream& o, const boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); }
+
+template <class T>
+bool operator == (const vector<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const boost::assign_detail::generic_list<T>& b, const vector<T>& a) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const set<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const boost::assign_detail::generic_list<T>& b, const set<T>& a) { return seqEqual(a, b); }
+}
+
+namespace qpid {
+namespace tests {
+
+/** Check if types of two objects (as given by typeinfo::name()) match. */
+#define BOOST_CHECK_TYPEID_EQUAL(a,b) BOOST_CHECK_EQUAL(typeid(a).name(),typeid(b).name())
+
+/**
+ * Supress all logging in a scope, restore to previous configuration in destructor.
+ */
+struct ScopedSuppressLogging {
+ typedef qpid::log::Logger Logger;
+ ScopedSuppressLogging(Logger& l=Logger::instance()) : logger(l), opts(l.getOptions()) { l.clear(); }
+ ~ScopedSuppressLogging() { logger.configure(opts); }
+ Logger& logger;
+ qpid::log::Options opts;
+};
+
+inline std::string getLibPath(const char* envName, const char* defaultPath = 0) {
+ const char* p = std::getenv(envName);
+ if (p != 0)
+ return p;
+ if (defaultPath == 0) {
+ std::ostringstream msg;
+ msg << "Environment variable " << envName << " not set.";
+ throw std::runtime_error(msg.str());
+ }
+ return defaultPath;
+}
+
+}} // namespace qpid::tests
+
+#endif /*!TEST_TOOLS_H*/
+
diff --git a/qpid/cpp/src/tests/test_watchdog b/qpid/cpp/src/tests/test_watchdog
new file mode 100755
index 0000000000..2b4ae9246e
--- /dev/null
+++ b/qpid/cpp/src/tests/test_watchdog
@@ -0,0 +1,36 @@
+#!/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.
+#
+
+# Tests for the watchdog plug-in
+
+source ./test_env.sh
+# Start a broker with watchdog, freeze it with kill -STOP, verify that it is killed.
+PORT=`$QPIDD_EXEC -dp0 --no-data-dir --auth=no --no-module-dir --load-module $WATCHDOG_LIB --log-to-file=qpidd_watchdog.log --watchdog-interval 2` || exit 1
+PID=`$QPIDD_EXEC --no-module-dir -cp $PORT` || exit 1
+kill -STOP $PID
+sleep 3
+
+if kill -0 $PID 2>/dev/null; then
+ echo "Hung process did not die."
+ kill $PID
+else
+ true
+fi
diff --git a/qpid/cpp/src/tests/test_wrap b/qpid/cpp/src/tests/test_wrap
new file mode 100755
index 0000000000..dd43c5a2e2
--- /dev/null
+++ b/qpid/cpp/src/tests/test_wrap
@@ -0,0 +1,48 @@
+#!/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.
+#
+
+# Read the started broker port, set appropriate env vars
+# then run the program under test
+
+QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+program=$1
+shift
+
+QPID_LOG_TO_FILE=`basename $program`.log
+export QPID_LOG_TO_FILE
+
+ERROR=0
+$program $* || ERROR=1
+
+# Check qpidd.log.
+egrep 'warning\|error\|critical' $QPID_LOG_TO_FILE && {
+ echo "WARNING: Suspicious broker log entries in $QPID_LOG_TO_FILE, above."
+}
+
+# Check valgrind log.
+#if test -n "$VALGRIND"; then
+# . `dirname $0`/vg_check $VG_LOG*
+# vg_check qpidd.vglog* || ERROR=1
+#fi
+
+exit $ERROR
diff --git a/qpid/cpp/src/tests/testagent.cpp b/qpid/cpp/src/tests/testagent.cpp
new file mode 100644
index 0000000000..98520b424a
--- /dev/null
+++ b/qpid/cpp/src/tests/testagent.cpp
@@ -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.
+ *
+ */
+
+#include <qpid/management/Manageable.h>
+#include <qpid/management/ManagementObject.h>
+#include <qpid/agent/ManagementAgent.h>
+#include <qpid/sys/Mutex.h>
+#include <qpid/sys/Time.h>
+#include "qmf/org/apache/qpid/agent/example/Parent.h"
+#include "qmf/org/apache/qpid/agent/example/Child.h"
+#include "qmf/org/apache/qpid/agent/example/ArgsParentCreate_child.h"
+#include "qmf/org/apache/qpid/agent/example/EventChildCreated.h"
+#include "qmf/org/apache/qpid/agent/example/Package.h"
+
+#include <signal.h>
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+static bool running = true;
+
+using namespace std;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+using qpid::management::Manageable;
+using qpid::management::Args;
+using qpid::sys::Mutex;
+namespace _qmf = qmf::org::apache::qpid::agent::example;
+
+class ChildClass;
+
+//==============================================================
+// CoreClass is the operational class that corresponds to the
+// "Parent" class in the management schema.
+//==============================================================
+class CoreClass : public Manageable
+{
+ string name;
+ ManagementAgent* agent;
+ _qmf::Parent* mgmtObject;
+ std::vector<ChildClass*> children;
+ Mutex vectorLock;
+
+public:
+
+ CoreClass(ManagementAgent* agent, string _name);
+ ~CoreClass() { mgmtObject->resourceDestroy(); }
+
+ ManagementObject* GetManagementObject(void) const
+ { return mgmtObject; }
+
+ void doLoop();
+ status_t ManagementMethod (uint32_t methodId, Args& args, string& text);
+};
+
+class ChildClass : public Manageable
+{
+ string name;
+ _qmf::Child* mgmtObject;
+
+public:
+
+ ChildClass(ManagementAgent* agent, CoreClass* parent, string name);
+ ~ChildClass() { mgmtObject->resourceDestroy(); }
+
+ ManagementObject* GetManagementObject(void) const
+ { return mgmtObject; }
+
+ void doWork()
+ {
+ mgmtObject->inc_count(2);
+ }
+};
+
+CoreClass::CoreClass(ManagementAgent* _agent, string _name) : name(_name), agent(_agent)
+{
+ static uint64_t persistId = 0x111222333444555LL;
+ mgmtObject = new _qmf::Parent(agent, this, name);
+
+ agent->addObject(mgmtObject, persistId++);
+ mgmtObject->set_state("IDLE");
+}
+
+void CoreClass::doLoop()
+{
+ // Periodically bump a counter to provide a changing statistical value
+ while (running) {
+ qpid::sys::sleep(1);
+ mgmtObject->inc_count();
+ mgmtObject->set_state("IN_LOOP");
+
+ {
+ Mutex::ScopedLock _lock(vectorLock);
+
+ for (std::vector<ChildClass*>::iterator iter = children.begin();
+ iter != children.end();
+ iter++) {
+ (*iter)->doWork();
+ }
+ }
+ }
+}
+
+Manageable::status_t CoreClass::ManagementMethod(uint32_t methodId, Args& args, string& /*text*/)
+{
+ Mutex::ScopedLock _lock(vectorLock);
+
+ switch (methodId) {
+ case _qmf::Parent::METHOD_CREATE_CHILD:
+ _qmf::ArgsParentCreate_child& ioArgs = (_qmf::ArgsParentCreate_child&) args;
+
+ ChildClass *child = new ChildClass(agent, this, ioArgs.i_name);
+ ioArgs.o_childRef = child->GetManagementObject()->getObjectId();
+
+ children.push_back(child);
+
+ agent->raiseEvent(_qmf::EventChildCreated(ioArgs.i_name));
+
+ return STATUS_OK;
+ }
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+ChildClass::ChildClass(ManagementAgent* agent, CoreClass* parent, string name)
+{
+ mgmtObject = new _qmf::Child(agent, this, parent, name);
+
+ agent->addObject(mgmtObject);
+}
+
+
+//==============================================================
+// Main program
+//==============================================================
+
+ManagementAgent::Singleton* singleton;
+
+void shutdown(int)
+{
+ running = false;
+}
+
+int main_int(int argc, char** argv)
+{
+ singleton = new ManagementAgent::Singleton();
+ const char* host = argc>1 ? argv[1] : "127.0.0.1";
+ int port = argc>2 ? atoi(argv[2]) : 5672;
+
+ signal(SIGINT, shutdown);
+
+ // Create the qmf management agent
+ ManagementAgent* agent = singleton->getInstance();
+
+ // Register the Qmf_example schema with the agent
+ _qmf::Package packageInit(agent);
+
+ // Start the agent. It will attempt to make a connection to the
+ // management broker
+ agent->init(host, port, 5, false, ".magentdata");
+
+ // Allocate some core objects
+ CoreClass core1(agent, "Example Core Object #1");
+ CoreClass core2(agent, "Example Core Object #2");
+ CoreClass core3(agent, "Example Core Object #3");
+
+ core1.doLoop();
+
+ // done, cleanup and exit
+ delete singleton;
+
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ try {
+ return main_int(argc, argv);
+ } catch(std::exception& e) {
+ cerr << "Top Level Exception: " << e.what() << endl;
+ return 1;
+ }
+}
+
diff --git a/qpid/cpp/src/tests/testagent.mk b/qpid/cpp/src/tests/testagent.mk
new file mode 100644
index 0000000000..19d91ccab9
--- /dev/null
+++ b/qpid/cpp/src/tests/testagent.mk
@@ -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.
+#
+
+# Build a simple qmf agent for test purposes.
+
+TESTAGENT_GEN_SRC= \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Parent.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Child.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Parent.cpp \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Child.cpp \
+ testagent_gen/qmf/org/apache/qpid/agent/example/ArgsParentCreate_child.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/EventChildCreated.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/EventChildCreated.cpp \
+ testagent_gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.cpp \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Package.h \
+ testagent_gen/qmf/org/apache/qpid/agent/example/Package.cpp
+
+$(TESTAGENT_GEN_SRC): testagent_gen.timestamp
+if GENERATE
+TESTAGENT_DEPS=../mgen.timestamp
+endif # GENERATE
+testagent_gen.timestamp: testagent.xml ${TESTAGENT_DEPS}
+ $(QMF_GEN) -o testagent_gen/qmf $(srcdir)/testagent.xml
+ touch $@
+
+CLEANFILES+=$(TESTAGENT_GEN_SRC) testagent_gen.timestamp
+
+testagent-testagent.$(OBJEXT): $(TESTAGENT_GEN_SRC)
+qpidtest_PROGRAMS+=testagent
+testagent_CXXFLAGS=$(CXXFLAGS) -Itestagent_gen
+testagent_SOURCES=testagent.cpp $(TESTAGENT_GEN_SRC)
+testagent_LDADD=$(top_builddir)/src/libqmf.la
+
+EXTRA_DIST+=testagent.xml
diff --git a/qpid/cpp/src/tests/testagent.xml b/qpid/cpp/src/tests/testagent.xml
new file mode 100644
index 0000000000..0b1436f999
--- /dev/null
+++ b/qpid/cpp/src/tests/testagent.xml
@@ -0,0 +1,64 @@
+<schema package="org.apache.qpid.agent.example">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+ <!--
+ ===============================================================
+ Parent
+ ===============================================================
+ -->
+ <class name="Parent">
+
+ This class represents a parent object
+
+ <property name="name" type="lstr" access="RC" index="y"/>
+
+ <statistic name="state" type="sstr" desc="Operational state of the link"/>
+ <statistic name="count" type="count64" unit="tick" desc="Counter that increases monotonically"/>
+
+ <method name="create_child" desc="Create child object">
+ <arg name="name" dir="I" type="lstr"/>
+ <arg name="childRef" dir="O" type="objId"/>
+ </method>
+ </class>
+
+
+ <!--
+ ===============================================================
+ Child
+ ===============================================================
+ -->
+ <class name="Child">
+ <property name="ParentRef" type="objId" references="Parent" access="RC" index="y" parentRef="y"/>
+ <property name="name" type="lstr" access="RC" index="y"/>
+
+ <statistic name="count" type="count64" unit="tick" desc="Counter that increases monotonically"/>
+
+ <method name="delete"/>
+ </class>
+
+ <eventArguments>
+ <arg name="childName" type="lstr"/>
+ </eventArguments>
+
+ <event name="ChildCreated" args="childName"/>
+ <event name="ChildDestroyed" args="childName"/>
+</schema>
+
diff --git a/qpid/cpp/src/tests/testlib.py b/qpid/cpp/src/tests/testlib.py
new file mode 100644
index 0000000000..fe57a84a81
--- /dev/null
+++ b/qpid/cpp/src/tests/testlib.py
@@ -0,0 +1,766 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# Support library for qpid python tests.
+#
+
+import os, re, signal, subprocess, time, unittest
+
+class TestBase(unittest.TestCase):
+ """
+ Base class for qpid tests. Provides broker start/stop/kill methods
+ """
+
+ """
+ The following environment vars control if and how the test is run, and determine where many of the helper
+ executables/libs are to be found.
+ """
+ _storeLib = os.getenv("STORE_LIB")
+ _storeEnable = _storeLib != None # Must be True for durability to be enabled during the test
+ _qpiddExec = os.getenv("QPIDD_EXEC", "/usr/sbin/qpidd")
+ _tempStoreDir = os.path.abspath(os.getenv("TMP_DATA_DIR", "/tmp/qpid"))
+
+ """Global message counter ensures unique messages"""
+ _msgCnt = 0
+
+ # --- Helper functions for parameter handling ---
+
+ def _paramBool(self, key, val, keyOnly = False):
+ if val == None:
+ return ""
+ if keyOnly:
+ if val:
+ return " --%s" % key
+ else:
+ return ""
+ else:
+ if val:
+ return " --%s yes" % key
+ else:
+ return " --%s no" % key
+
+ # --- Helper functions for message creation ---
+
+ def _makeMessage(self, msgSize):
+ msg = "Message-%04d" % self._msgCnt
+ self._msgCnt = self._msgCnt + 1
+ msgLen = len(msg)
+ if msgSize > msgLen:
+ for i in range(msgLen, msgSize):
+ if i == msgLen:
+ msg += "-"
+ else:
+ msg += chr(ord('a') + (i % 26))
+ return msg
+
+ def _makeMessageList(self, numMsgs, msgSize):
+ if msgSize == None:
+ msgSize = 12
+ msgs = ""
+ for m in range(0, numMsgs):
+ msgs += "%s\n" % self._makeMessage(msgSize)
+ return msgs
+
+ # --- Starting and stopping a broker ---
+
+ def startBroker(self, qpiddArgs, logFile = None):
+ """Start a single broker daemon, returns tuple (pid, port)"""
+ if self._qpiddExec == None:
+ raise Exception("Environment variable QPIDD is not set")
+ cmd = "%s --daemon --port=0 %s" % (self._qpiddExec, qpiddArgs)
+ portStr = os.popen(cmd).read()
+ if len(portStr) == 0:
+ err = "Broker daemon startup failed."
+ if logFile != None:
+ err += " See log file %s" % logFile
+ raise Exception(err)
+ port = int(portStr)
+ pidStr = os.popen("%s -p %d -c" % (self._qpiddExec, port)).read()
+ try:
+ pid = int(pidStr)
+ except:
+ raise Exception("Unable to get pid: \"%s -p %d -c\" returned %s" % (self._qpiddExec, port, pidStr))
+ #print "started broker: pid=%d, port=%d args: %s" % (pid, port, qpiddArgs)
+ return (pid, port)
+
+ def killBroker(self, nodeTuple, ignoreFailures = False):
+ """Kill a broker using kill -9"""
+ try:
+ os.kill(nodeTuple[self.PID], signal.SIGKILL)
+ try:
+ os.waitpid(nodeTuple[self.PID], 0)
+ except:
+ pass
+ #print "killed broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ except:
+ if ignoreFailures:
+ print "WARNING: killBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ else:
+ raise
+
+ def stopBroker(self, nodeTuple, ignoreFailures = False):
+ """Stop a broker using qpidd -q"""
+ try:
+ ret = os.spawnl(os.P_WAIT, self._qpiddExec, self._qpiddExec, "--port=%d" % nodeTuple[self.PORT], "--quit", "--no-module-dir")
+ if ret != 0:
+ raise Exception("stopBroker(): port=%d: qpidd -q returned %d" % (nodeTuple[self.PORT], ret))
+ try:
+ os.waitpid(nodeTuple[self.PID], 0)
+ except:
+ pass
+ #print "stopped broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ except:
+ if ignoreFailures:
+ print "WARNING: stopBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ else:
+ raise
+
+
+
+class TestBaseCluster(TestBase):
+ """
+ Base class for cluster tests. Provides methods for starting and stopping clusters and cluster nodes.
+ """
+
+ """
+ The following environment vars control if and how the test is run, and determine where many of the helper
+ executables/libs are to be found.
+ """
+ _clusterLib = os.getenv("CLUSTER_LIB")
+ _clusterTestEnable = _clusterLib != None # Must be True for these cluster tests to run
+ _xmlLib = os.getenv("XML_LIB")
+ _xmlEnable = _xmlLib != None
+ _qpidConfigExec = os.getenv("QPID_CONFIG_EXEC", "/usr/bin/qpid-config")
+ _qpidRouteExec = os.getenv("QPID_ROUTE_EXEC", "/usr/bin/qpid-route")
+ _receiverExec = os.getenv("RECEIVER_EXEC", "/usr/libexec/qpid/test/receiver")
+ _senderExec = os.getenv("SENDER_EXEC", "/usr/libexec/qpid/test/sender")
+
+
+ """
+ _clusterDict is a dictionary of clusters:
+ key = cluster name (string)
+ val = dictionary of node numbers:
+ key = integer node number
+ val = tuple containing (pid, port)
+ For example, two clusters "TestCluster0" and "TestCluster1" containing several nodes would look as follows:
+ {"TestCluster0": {0: (pid0-0, port0-0), 1: (pid0-1, port0-1), ...}, "TestCluster1": {0: (pid1-0, port1-0), 1: (pid1-1, port1-1), ...}}
+ where pidm-n and portm-n are the int pid and port for TestCluster m node n respectively.
+ """
+ _clusterDict = {}
+
+ """Index for (pid, port) tuple"""
+ PID = 0
+ PORT = 1
+
+ def run(self, res):
+ """ Skip cluster testing if env var RUN_CLUSTER_TESTS is not defined."""
+ if not self._clusterTestEnable:
+ return
+ unittest.TestCase.run(self, res)
+
+ # --- Private helper / convenience functions ---
+
+ def _checkPids(self, clusterName = None):
+ for pid, port in self.getTupleList():
+ try:
+ os.kill(pid, 0)
+ except:
+ raise Exception("_checkPids(): Broker with pid %d expected but does not exist! (crashed?)" % pid)
+
+
+ # --- Starting cluster node(s) ---
+
+ def createClusterNode(self, nodeNumber, clusterName):
+ """Create a node and add it to the named cluster"""
+ if self._tempStoreDir == None:
+ raise Exception("Environment variable TMP_DATA_DIR is not set")
+ if self._clusterLib == None:
+ raise Exception("Environment variable LIBCLUSTER is not set")
+ name = "%s-%d" % (clusterName, nodeNumber)
+ dataDir = os.path.join(self._tempStoreDir, "cluster", name)
+ logFile = "%s.log" % dataDir
+ args = "--no-module-dir --load-module=%s --data-dir=%s --cluster-name=%s --auth=no --log-enable=notice+ --log-to-file=%s" % \
+ (self._clusterLib, dataDir, clusterName, logFile)
+ if self._storeEnable:
+ if self._storeLib == None:
+ raise Exception("Environment variable LIBSTORE is not set")
+ args += " --load-module %s" % self._storeLib
+ self._clusterDict[clusterName][nodeNumber] = self.startBroker(args, logFile)
+
+ def createCluster(self, clusterName, numberNodes = 0):
+ """Create a cluster containing an initial number of nodes"""
+ self._clusterDict[clusterName] = {}
+ for n in range(0, numberNodes):
+ self.createClusterNode(n, clusterName)
+
+ def waitForNodes(self, clusterName):
+ """Wait for all nodes to become active (ie finish cluster sync)"""
+ # TODO - connect to each known node in cluster
+ # Until this is done, wait a bit (hack)
+ time.sleep(1)
+
+ # --- Cluster and node status ---
+
+ def getTupleList(self, clusterName = None):
+ """Get list of (pid, port) tuples of all known cluster brokers"""
+ tList = []
+ for c, l in self._clusterDict.iteritems():
+ if clusterName == None or c == clusterName:
+ for t in l.itervalues():
+ tList.append(t)
+ return tList
+
+ def getNumBrokers(self):
+ """Get total number of brokers in all known clusters"""
+ return len(self.getTupleList())
+
+ def checkNumBrokers(self, expected = None, checkPids = True):
+ """Check that the total number of brokers in all known clusters is the expected value"""
+ if expected != None and self.getNumBrokers() != expected:
+ raise Exception("Unexpected number of brokers: expected %d, found %d" % (expected, self.getNumBrokers()))
+ if checkPids:
+ self._checkPids()
+
+ def getClusterTupleList(self, clusterName):
+ """Get list of (pid, port) tuples of all nodes in named cluster"""
+ if clusterName in self._clusterDict:
+ return self._clusterDict[clusterName].values()
+ return []
+
+ def getNumClusterBrokers(self, clusterName):
+ """Get total number of brokers in named cluster"""
+ return len(self.getClusterTupleList(clusterName))
+
+ def getNodeTuple(self, nodeNumber, clusterName):
+ """Get the (pid, port) tuple for the given cluster node"""
+ return self._clusterDict[clusterName][nodeNumber]
+
+ def checkNumClusterBrokers(self, clusterName, expected = None, checkPids = True, waitForNodes = True):
+ """Check that the total number of brokers in the named cluster is the expected value"""
+ if expected != None and self.getNumClusterBrokers(clusterName) != expected:
+ raise Exception("Unexpected number of brokers in cluster %s: expected %d, found %d" % \
+ (clusterName, expected, self.getNumClusterBrokers(clusterName)))
+ if checkPids:
+ self._checkPids(clusterName)
+ if waitForNodes:
+ self.waitForNodes(clusterName)
+
+ def clusterExists(self, clusterName):
+ """ Return True if clusterName exists, False otherwise"""
+ return clusterName in self._clusterDict.keys()
+
+ def clusterNodeExists(self, clusterName, nodeNumber):
+ """ Return True if nodeNumber in clusterName exists, False otherwise"""
+ if clusterName in self._clusterDict.keys():
+ return nodeNumber in self._clusterDict[nodeName]
+ return False
+
+ def createCheckCluster(self, clusterName, size):
+ """Create a cluster using the given name and size, then check the number of brokers"""
+ self.createCluster(clusterName, size)
+ self.checkNumClusterBrokers(clusterName, size)
+
+ # --- Kill cluster nodes using signal 9 ---
+
+ def killNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
+ """Kill the given node in the named cluster using kill -9"""
+ self.killBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName][nodeNumber])
+
+ def killCluster(self, clusterName, updateDict = True, ignoreFailures = False):
+ """Kill all nodes in the named cluster"""
+ for n in self._clusterDict[clusterName].iterkeys():
+ self.killNode(n, clusterName, False, ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName])
+
+ def killClusterCheck(self, clusterName):
+ """Kill the named cluster and check that the name is removed from the cluster dictionary"""
+ self.killCluster(clusterName)
+ if self.clusterExists(clusterName):
+ raise Exception("Unable to kill cluster %s; %d nodes still exist" % \
+ (clusterName, self.getNumClusterBrokers(clusterName)))
+
+ def killAllClusters(self, ignoreFailures = False):
+ """Kill all known clusters"""
+ for n in self._clusterDict.iterkeys():
+ self.killCluster(n, False, ignoreFailures)
+ self._clusterDict.clear()
+
+ def killAllClustersCheck(self, ignoreFailures = False):
+ """Kill all known clusters and check that the cluster dictionary is empty"""
+ self.killAllClusters(ignoreFailures)
+ self.checkNumBrokers(0)
+
+ # --- Stop cluster nodes using qpidd -q ---
+
+ def stopNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
+ """Stop the given node in the named cluster using qpidd -q"""
+ self.stopBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName][nodeNumber])
+
+ def stopAllClusters(self, ignoreFailures = False):
+ """Stop all known clusters"""
+ for n in self._clusterDict.iterkeys():
+ self.stopCluster(n, False, ignoreFailures)
+ self._clusterDict.clear()
+
+
+ def stopCluster(self, clusterName, updateDict = True, ignoreFailures = False):
+ """Stop all nodes in the named cluster"""
+ for n in self._clusterDict[clusterName].iterkeys():
+ self.stopNode(n, clusterName, False, ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName])
+
+ def stopCheckCluster(self, clusterName, ignoreFailures = False):
+ """Stop the named cluster and check that the name is removed from the cluster dictionary"""
+ self.stopCluster(clusterName, True, ignoreFailures)
+ if self.clusterExists(clusterName):
+ raise Exception("Unable to kill cluster %s; %d nodes still exist" % (clusterName, self.getNumClusterBrokers(clusterName)))
+
+ def stopAllCheck(self, ignoreFailures = False):
+ """Kill all known clusters and check that the cluster dictionary is empty"""
+ self.stopAllClusters()
+ self.checkNumBrokers(0)
+
+ # --- qpid-config functions ---
+
+ def _qpidConfig(self, nodeNumber, clusterName, action):
+ """Configure some aspect of a qpid broker using the qpid_config executable"""
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ #print "%s -a localhost:%d %s" % (self._qpidConfigExec, port, action)
+ ret = os.spawnl(os.P_WAIT, self._qpidConfigExec, self._qpidConfigExec, "-a", "localhost:%d" % port, *action.split())
+ if ret != 0:
+ raise Exception("_qpidConfig(): cluster=\"%s\" nodeNumber=%d port=%d action=\"%s\" returned %d" % \
+ (clusterName, nodeNumber, port, action, ret))
+
+ def addExchange(self, nodeNumber, clusterName, exchangeType, exchangeName, durable = False, sequence = False, \
+ ive = False):
+ """Add a named exchange."""
+ action = "add exchange %s %s" % (exchangeType, exchangeName)
+ action += self._paramBool("durable", durable, True)
+ action += self._paramBool("sequence", sequence, True)
+ action += self._paramBool("ive", ive, True)
+ self._qpidConfig(nodeNumber, clusterName, action)
+
+ def deleteExchange(self, nodeNumber, clusterName, exchangeName):
+ """Delete a named exchange"""
+ self._qpidConfig(nodeNumber, clusterName, "del exchange %s" % exchangeName)
+
+ def addQueue(self, nodeNumber, clusterName, queueName, configArgs = None):
+ """Add a queue using qpid-config."""
+ action = "add queue %s" % queueName
+ if self._storeEnable:
+ action += " --durable"
+ if configArgs != None:
+ action += " %s" % configArgs
+ self._qpidConfig(nodeNumber, clusterName, action)
+
+ def delQueue(self, nodeNumber, clusterName, queueName):
+ """Delete a named queue using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "del queue %s" % queueName)
+
+ def bind(self, nodeNumber, clusterName, exchangeName, queueName, key):
+ """Create an exchange-queue binding using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "bind %s %s %s" % (exchangeName, queueName, key))
+
+ def unbind(self, nodeNumber, clusterName, exchangeName, queueName, key):
+ """Remove an exchange-queue binding using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "unbind %s %s %s" % (exchangeName, queueName, key))
+
+ # --- qpid-route functions (federation) ---
+
+ def brokerDict(self, nodeNumber, clusterName, host = "localhost", user = None, password = None):
+ """Returns a dictionary containing the broker info to be passed to route functions"""
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ return {"cluster": clusterName, "node":nodeNumber, "port":port, "host":host, "user":user, "password":password}
+
+ def _brokerStr(self, brokerDict):
+ """Set up a broker string in the format [user/password@]host:port"""
+ str = ""
+ if brokerDict["user"] !=None and brokerDict["password"] != None:
+ str = "%s@%s" % (brokerDict["user"], brokerDict["password"])
+ str += "%s:%d" % (brokerDict["host"], brokerDict["port"])
+ return str
+
+ def _qpidRoute(self, action):
+ """Set up a route using qpid-route"""
+ #print "%s %s" % (self._qpidRouteExec, action)
+ ret = os.spawnl(os.P_WAIT, self._qpidRouteExec, self._qpidRouteExec, *action.split())
+ if ret != 0:
+ raise Exception("_qpidRoute(): action=\"%s\" returned %d" % (action, ret))
+
+ def routeDynamicAdd(self, destBrokerDict, srcBrokerDict, exchangeName):
+ self._qpidRoute("dynamic add %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
+
+ def routeDynamicDelete(self, destBrokerDict, srcBrokerDict, exchangeName):
+ self._qpidRoute("dynamic del %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
+
+ def routeAdd(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
+ self._qpidRoute("route add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
+
+ def routeDelete(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
+ self._qpidRoute("route del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
+
+ def routeQueueAdd(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
+ self._qpidRoute("queue add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
+
+ def routeQueueDelete(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
+ self._qpidRoute("queue del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
+
+ def routeLinkAdd(self, destBrokerDict, srcBrokerDict):
+ self._qpidRoute("link add %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
+
+ def routeLinkDelete(self, destBrokerDict, srcBrokerDict):
+ self._qpidRoute("link del %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
+
+ # --- Message send and receive functions ---
+
+ def _receiver(self, action):
+ if self._receiverExec == None:
+ raise Exception("Environment variable RECEIVER is not set")
+ cmd = "%s %s" % (self._receiverExec, action)
+ #print cmd
+ return subprocess.Popen(cmd.split(), stdout = subprocess.PIPE)
+
+ def _sender(self, action):
+ if self._senderExec == None:
+ raise Exception("Environment variable SENDER is not set")
+ cmd = "%s %s" % (self._senderExec, action)
+ #print cmd
+ return subprocess.Popen(cmd.split(), stdin = subprocess.PIPE)
+
+ def createReciever(self, nodeNumber, clusterName, queueName, numMsgs = None, receiverArgs = None):
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ action = "--port %d --queue %s" % (port, queueName)
+ if numMsgs != None:
+ action += " --messages %d" % numMsgs
+ if receiverArgs != None:
+ action += " %s" % receiverArgs
+ return self._receiver(action)
+
+ def createSender(self, nodeNumber, clusterName, exchangeName, routingKey, senderArgs = None):
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ action = "--port %d --exchange %s" % (port, exchangeName)
+ if routingKey != None and len(routingKey) > 0:
+ action += " --routing-key %s" % routingKey
+ if self._storeEnable:
+ action += " --durable yes"
+ if senderArgs != None:
+ action += " %s" % senderArgs
+ return self._sender(action)
+
+ def createBindDirectExchangeQueue(self, nodeNumber, clusterName, exchangeName, queueName):
+ self.addExchange(nodeNumber, clusterName, "direct", exchangeName)
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, queueName)
+
+ def createBindTopicExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameKeyList):
+ self.addExchange(nodeNumber, clusterName, "topic", exchangeName)
+ for queueName, key in queueNameKeyList.iteritems():
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, key)
+
+ def createBindFanoutExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameList):
+ self.addExchange(nodeNumber, clusterName, "fanout", exchangeName)
+ for queueName in queueNameList:
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, "")
+
+ def sendMsgs(self, nodeNumber, clusterName, exchangeName, routingKey, numMsgs, msgSize = None, wait = True):
+ msgs = self._makeMessageList(numMsgs, msgSize)
+ sender = self.createSender(nodeNumber, clusterName, exchangeName, routingKey)
+ sender.stdin.write(msgs)
+ sender.stdin.close()
+ if wait:
+ sender.wait()
+ return msgs
+
+ def receiveMsgs(self, nodeNumber, clusterName, queueName, numMsgs, wait = True):
+ receiver = self.createReciever(nodeNumber, clusterName, queueName, numMsgs)
+ cnt = 0
+ msgs = ""
+ while cnt < numMsgs:
+ rx = receiver.stdout.readline()
+ if rx == "" and receiver.poll() != None: break
+ msgs += rx
+ cnt = cnt + 1
+ if wait:
+ receiver.wait()
+ return msgs
+
+
+ # --- Exchange-specific helper inner classes ---
+
+ class TestHelper:
+ """
+ This is a "virtual" superclass for test helpers, and is not useful on its own, but the
+ per-exchange subclasses are designed to keep track of the messages sent to and received
+ from queues which have bindings to that exchange type.
+ """
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+
+ """Dictionary of queues and lists of messages sent to them."""
+ self._txMsgs = {}
+ """Dictionary of queues and lists of messages received from them."""
+ self._rxMsgs = {}
+ """List of node numbers currently in the cluster"""
+ self._nodes = []
+ """List of node numbers which have been killed and can therefore be recovered"""
+ self._deadNodes = []
+ """Last node to be used"""
+ self._lastNode = None
+
+ self._testBaseCluster = testBaseCluster
+ self._clusterName = clusterName
+ self._exchangeName = exchangeName
+ self._queueNameList = queueNameList
+ self._addQueues(queueNameList)
+ self._testBaseCluster.createCheckCluster(clusterName, numNodes)
+ self._nodes.extend(range(0, numNodes))
+
+ def _addQueues(self, queueNameList):
+ for qn in queueNameList:
+ if not qn in self._txMsgs:
+ self._txMsgs[qn] = []
+ if not qn in self._rxMsgs:
+ self._rxMsgs[qn] = []
+
+ def _bindQueue(self, queueName, bindingKey, nodeNumber = None):
+ """Bind a queue to an exchange using a binding key."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # first available node
+ self._testBaseCluster.addQueue(nodeNumber, self._clusterName, queueName)
+ self._testBaseCluster.bind(nodeNumber, self._clusterName, self._exchangeName, queueName, bindingKey)
+
+ def _highestNodeNumber(self):
+ """Find the highest node number used so far between the current nodes and those stopped/killed."""
+ highestNode = self._nodes[-1]
+ if len(self._deadNodes) == 0:
+ return highestNode
+ highestDeadNode = self._deadNodes[-1]
+ if highestNode > highestDeadNode:
+ return highestNode
+ return highestDeadNode
+
+ def killCluster(self):
+ """Kill all nodes in the cluster"""
+ self._testBaseCluster.killCluster(self._clusterName)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, 0)
+ self._deadNodes.extend(self._nodes)
+ self._deadNodes.sort()
+ del self._nodes[:]
+
+ def restoreCluster(self, lastNode = None, restoreNodes = True):
+ """Restore a previously killed cluster"""
+ self._testBaseCluster.createCluster(self._clusterName)
+ if restoreNodes:
+ numNodes = len(self._deadNodes)
+ self.restoreNodes(lastNode)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, numNodes)
+
+ def addNodes(self, numberOfNodes = 1):
+ """Add a fixed number of nodes to the cluster."""
+ nodeStart = self._highestNodeNumber() + 1
+ for i in range(0, numberOfNodes):
+ nodeNumber = nodeStart + i
+ self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
+ self._nodes.append(nodeNumber)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, len(self._nodes))
+ self._testBaseCluster.waitForNodes(self._clusterName)
+
+ def restoreNode(self, nodeNumber):
+ """Restore a cluster node that has been previously killed"""
+ if nodeNumber not in self._deadNodes:
+ raise Exception("restoreNode(): Node number %d not in dead node list %s" % (nodeNumber, self._deadNodes))
+ self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
+ self._deadNodes.remove(nodeNumber)
+ self._nodes.append(nodeNumber)
+ self._nodes.sort()
+
+ def restoreNodes(self, lastNode = None):
+ """Restore all known cluster nodes that have been previously killed starting with a known last-used node"""
+ if len(self._nodes) == 0: # restore last-used node first
+ if lastNode == None:
+ lastNode = self._lastNode
+ self.restoreNode(lastNode)
+ while len(self._deadNodes) > 0:
+ self.restoreNode(self._deadNodes[0])
+ self._testBaseCluster.waitForNodes(self._clusterName)
+
+ def killNode(self, nodeNumber):
+ """Kill a cluster node (if it is in the _nodes list)."""
+ if nodeNumber not in self._nodes:
+ raise Exception("killNode(): Node number %d not in node list %s" % (nodeNumber, self._nodes))
+ self._testBaseCluster.killNode(nodeNumber, self._clusterName)
+ self._nodes.remove(nodeNumber)
+ self._deadNodes.append(nodeNumber)
+ self._deadNodes.sort()
+
+ def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ """Send a fixed number of messages using the given routing key."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # Use first available node
+ msgs = self._testBaseCluster._makeMessageList(numMsgs, msgSize)
+ sender = self._testBaseCluster.createSender(nodeNumber, self._clusterName, self._exchangeName, routingKey)
+ sender.stdin.write(msgs)
+ sender.stdin.close()
+ if wait:
+ sender.wait()
+ self._lastNode = nodeNumber
+ return msgs.split()
+
+ # TODO - this i/f is messy: one mumMsgs can be given, but a list of queues
+ # so assuming numMsgs for each queue
+ # A mechanism is needed to specify a different numMsgs per queue
+ def receiveMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, wait = True):
+ """Receive a fixed number of messages from a named queue. If numMsgs == None, get all remaining messages."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # Use first available node
+ if queueNameList == None:
+ queueNameList = self._txMsgs.iterkeys()
+ for qn in queueNameList:
+ nm = numMsgs
+ if nm == None:
+ nm = len(self._txMsgs[qn]) - len(self._rxMsgs[qn]) # get all remaining messages
+ if nm > 0:
+ while nm > 0:
+ receiver = self._testBaseCluster.createReciever(nodeNumber, self._clusterName, qn, nm)
+ cnt = 0
+ while cnt < nm:
+ rx = receiver.stdout.readline().strip()
+ if rx == "":
+ if receiver.poll() != None: break
+ elif rx not in self._rxMsgs[qn]:
+ self._rxMsgs[qn].append(rx)
+ cnt = cnt + 1
+ nm = nm - cnt
+ if wait:
+ receiver.wait()
+ self._rxMsgs[qn].sort()
+ self._lastNode = nodeNumber
+
+ def receiveRemainingMsgs(self, nodeNumber = None, queueNameList = None, wait = True):
+ """Receive all remaining messages on named queue."""
+ self.receiveMsgs(None, nodeNumber, queueNameList, wait)
+
+ def checkMsgs(self):
+ """Return True if all expected messages have been received (ie the transmit and receive list are identical)."""
+ txMsgTot = 0
+ rxMsgTot = 0
+ for qn, txMsgList in self._txMsgs.iteritems():
+ rxMsgList = self._rxMsgs[qn]
+ txMsgTot = txMsgTot + len(txMsgList)
+ rxMsgTot = rxMsgTot + len(rxMsgList)
+ if len(txMsgList) != len(rxMsgList):
+ return False
+ for i, m in enumerate(txMsgList):
+ if m != rxMsgList[i]:
+ return False
+ if txMsgTot == 0 and rxMsgTot == 0:
+ print "WARNING: No messages were either sent or received"
+ return True
+
+ def finalizeTest(self):
+ """Recover all the remaining messages on all queues, then check that all expected messages were received."""
+ self.receiveRemainingMsgs()
+ self._testBaseCluster.stopAllCheck()
+ if not self.checkMsgs():
+ self.printMsgs()
+ self._testBaseCluster.fail("Send - receive message mismatch")
+
+ def printMsgs(self, txMsgs = True, rxMsgs = True):
+ """Print all messages transmitted and received."""
+ for qn, txMsgList in self._txMsgs.iteritems():
+ print "Queue: %s" % qn
+ if txMsgs:
+ print " txMsgList = %s" % txMsgList
+ if rxMsgs:
+ rxMsgList = self._rxMsgs[qn]
+ print " rxMsgList = %s" % rxMsgList
+
+
+ class DirectExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
+ self._testBaseCluster.addExchange(0, clusterName, "direct", exchangeName)
+ for qn in queueNameList:
+ self._bindQueue(qn, qn)
+
+ def addQueues(self, queueNameList):
+ self._addQueues(queueNameList)
+ for qn in queueNameList:
+ self._bindQueue(qn, qn)
+
+ def sendMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, msgSize = None, wait = True):
+ if queueNameList == None:
+ queueNameList = self._txMsgs.iterkeys()
+ for qn in queueNameList:
+ self._txMsgs[qn].extend(TestBaseCluster.TestHelper.sendMsgs(self, qn, numMsgs, nodeNumber, msgSize, wait))
+
+
+ class TopicExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList):
+ self._queueNameKeyList = queueNameKeyList
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList.iterkeys())
+ self._testBaseCluster.addExchange(0, clusterName, "topic", exchangeName)
+ for qn, bk in queueNameKeyList.iteritems():
+ self._bindQueue(qn, bk)
+
+ def addQueues(self, queueNameKeyList):
+ self._addQueues(queueNameKeyList.iterkeys())
+ for qn, bk in queueNameKeyList.iteritems():
+ self._bindQueue(qn, bk)
+
+ def _prepareRegex(self, bk):
+ # This regex conversion is not very complete - there are other chars that should be escaped too
+ return "^%s$" % bk.replace(".", r"\.").replace("*", r"[^.]*").replace("#", ".*")
+
+ def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ msgList = TestBaseCluster.TestHelper.sendMsgs(self, routingKey, numMsgs, nodeNumber, msgSize, wait)
+ for qn, bk in self._queueNameKeyList.iteritems():
+ if re.match(self._prepareRegex(bk), routingKey):
+ self._txMsgs[qn].extend(msgList)
+
+
+ class FanoutExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
+ self._testBaseCluster.addExchange(0, clusterName, "fanout", exchangeName)
+ for qn in queueNameList:
+ self._bindQueue(qn, "")
+
+ def addQueues(self, queueNameList):
+ self._addQueues(queueNameList)
+ for qn in queueNameList:
+ self._bindQueue(qn, "")
+
+ def sendMsgs(self, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ msgList = TestBaseCluster.TestHelper.sendMsgs(self, "", numMsgs, nodeNumber, msgSize, wait)
+ for ml in self._txMsgs.itervalues():
+ ml.extend(msgList)
+
diff --git a/qpid/cpp/src/tests/topic_perftest b/qpid/cpp/src/tests/topic_perftest
new file mode 100755
index 0000000000..cd440b2458
--- /dev/null
+++ b/qpid/cpp/src/tests/topic_perftest
@@ -0,0 +1,22 @@
+#!/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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode topic --qt 16
diff --git a/qpid/cpp/src/tests/topictest b/qpid/cpp/src/tests/topictest
new file mode 100755
index 0000000000..257c24bd81
--- /dev/null
+++ b/qpid/cpp/src/tests/topictest
@@ -0,0 +1,61 @@
+#!/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.
+#
+
+# Run the C++ topic test
+
+# Clean up old log files
+rm -f subscriber_*.log
+
+# Defaults values
+SUBSCRIBERS=10
+MESSAGES=2000
+BATCHES=10
+
+while getopts "s:m:b:h:t" opt ; do
+ case $opt in
+ s) SUBSCRIBERS=$OPTARG ;;
+ m) MESSAGES=$OPTARG ;;
+ b) BATCHES=$OPTARG ;;
+ h) HOST=-h$OPTARG ;;
+ t) TRANSACTIONAL="--transactional --durable" ;;
+ ?)
+ echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]"
+ exit 1
+ ;;
+ esac
+done
+
+subscribe() {
+ echo Start subscriber $1
+ LOG="subscriber_$1.log"
+ ./qpid-topic-listener $TRANSACTIONAL > $LOG 2>&1 && rm -f $LOG
+}
+
+publish() {
+ ./qpid-topic-publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS $HOST $TRANSACTIONAL
+}
+
+for ((i=$SUBSCRIBERS ; i--; )); do
+ subscribe $i &
+done
+# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test.
+sleep 2
+publish 2>&1 || exit 1
diff --git a/qpid/cpp/src/tests/topictest.ps1 b/qpid/cpp/src/tests/topictest.ps1
new file mode 100644
index 0000000000..59a483c2d5
--- /dev/null
+++ b/qpid/cpp/src/tests/topictest.ps1
@@ -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.
+#
+
+# Parameters with default values: s (subscribers) m (messages) b (batches)
+# h (host) t (false; use transactions)
+param (
+ [int]$subscribers = 10,
+ [int]$message_count = 2000,
+ [int]$batches = 10,
+ [string]$broker,
+ [switch] $t # transactional
+)
+
+# Run the C++ topic test
+[string]$me = $myInvocation.InvocationName
+$srcdir = Split-Path $me
+#$srcdir = Split-Path $myInvocation.InvocationName
+
+# Clean up old log files
+Get-Item subscriber_*.log | Remove-Item
+
+if ($t) {
+ $transactional = "--transactional --durable"
+}
+
+# Find which subdir the exes are in
+. $srcdir\find_prog.ps1 .\topic_listener.exe
+
+function subscribe {
+ param ([int]$num, [string]$sub)
+ "Start subscriber $num"
+ $LOG = "subscriber_$num.log"
+ $cmdline = ".\$sub\topic_listener $transactional > $LOG 2>&1
+ if (`$LastExitCode -ne 0) { Remove-Item $LOG }"
+ $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+ . $srcdir\background.ps1 $cmdblock
+}
+
+function publish {
+ param ([string]$sub)
+ Invoke-Expression ".\$sub\topic_publisher --messages $message_count --batches $batches --subscribers $subscribers $host $transactional" 2>&1
+}
+
+if ($broker.length) {
+ $broker = "-h$broker"
+}
+
+$i = $subscribers
+while ($i -gt 0) {
+ subscribe $i $sub
+ $i--
+}
+
+# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test.
+Start-Sleep 2
+publish $sub
+exit $LastExitCode
diff --git a/qpid/cpp/src/tests/txjob.cpp b/qpid/cpp/src/tests/txjob.cpp
new file mode 100644
index 0000000000..a7a905c1b7
--- /dev/null
+++ b/qpid/cpp/src/tests/txjob.cpp
@@ -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.
+ *
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string workQueue;
+ string source;
+ string dest;
+ uint messages;
+ uint jobs;
+ bool quit;
+ bool declareQueues;
+
+ Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0),
+ quit(false), declareQueues(false)
+ {
+ addOptions()
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to shift")
+ ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request")
+ ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted")
+ ("dest", qpid::optValue(dest, "QUEUE NAME"), "dest queue to which messages will be shifted")
+ ("work-queue", qpid::optValue(workQueue, "QUEUE NAME"), "work queue from which to take instructions")
+ ("add-quit", qpid::optValue(quit), "add a 'quit' instruction to the queue (after any other jobs)")
+ ("declare-queues", qpid::optValue(declareQueues), "issue a declare for all queues");
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+//TODO: might be nice to make this capable of failover as well at some
+//point; for now its just for the setup phase.
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ Connection connection;
+ connection.open(opts.con);
+ Session session = connection.newSession();
+ if (opts.declareQueues) {
+ session.queueDeclare(arg::queue=opts.workQueue);
+ session.queueDeclare(arg::queue=opts.source);
+ session.queueDeclare(arg::queue=opts.dest);
+ }
+ for (uint i = 0; i < opts.jobs; ++i) {
+ Message job("transfer", opts.workQueue);
+ job.getHeaders().setString("src", opts.source);
+ job.getHeaders().setString("dest", opts.dest);
+ job.getHeaders().setInt("count", opts.messages);
+ async(session).messageTransfer(arg::content=job);
+ }
+
+ if (opts.quit) {
+ async(session).messageTransfer(arg::content=Message("quit", opts.workQueue));
+ }
+
+ session.sync();
+ session.close();
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/txshift.cpp b/qpid/cpp/src/tests/txshift.cpp
new file mode 100644
index 0000000000..882d3716d8
--- /dev/null
+++ b/qpid/cpp/src/tests/txshift.cpp
@@ -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.
+ *
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string workQueue;
+ size_t workers;
+
+ Args() : workQueue("txshift-control"), workers(1)
+ {
+ addOptions()
+ ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start")
+ ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions");
+ }
+};
+
+struct Transfer : MessageListener
+{
+ std::string control;
+ std::string source;
+ std::string destination;
+ uint expected;
+ uint transfered;
+ SubscriptionSettings controlSettings;
+ Subscription controlSubscription;
+ SubscriptionSettings sourceSettings;
+ Subscription sourceSubscription;
+
+ Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {}
+
+ void subscribeToSource(SubscriptionManager manager)
+ {
+ sourceSettings.autoAck = 0;//will accept once at the end of the batch
+ sourceSettings.flowControl = FlowControl::messageCredit(expected);
+ sourceSubscription = manager.subscribe(*this, source, sourceSettings);
+ QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected);
+ }
+
+ void subscribeToControl(SubscriptionManager manager)
+ {
+ controlSettings.flowControl = FlowControl::messageCredit(1);
+ controlSubscription = manager.subscribe(*this, control, controlSettings);
+ QPID_LOG(info, "Subscribed to job queue");
+ }
+
+ void received(Message& message)
+ {
+ QPID_LOG(debug, "received: " << message.getData() << " for " << message.getDestination());
+ if (message.getDestination() == source) {
+ receivedFromSource(message);
+ } else if (message.getDestination() == control) {
+ receivedFromControl(message);
+ } else {
+ QPID_LOG(error, "Unexpected message: " << message.getData() << " to " << message.getDestination());
+ }
+ }
+
+ void receivedFromSource(Message& message)
+ {
+ QPID_LOG(debug, "transfering " << (transfered+1) << " of " << expected);
+ message.getDeliveryProperties().setRoutingKey(destination);
+ async(sourceSubscription.getSession()).messageTransfer(arg::content=message);
+ if (++transfered == expected) {
+ QPID_LOG(info, "completed job: " << transfered << " messages shifted from " <<
+ source << " to " << destination);
+ sourceSubscription.accept(sourceSubscription.getUnaccepted());
+ sourceSubscription.getSession().txCommit();
+ sourceSubscription.cancel();
+ //grant credit to allow broker to send us another control message
+ controlSubscription.grantMessageCredit(1);
+ }
+ }
+
+ void receivedFromControl(Message& message)
+ {
+ if (message.getData() == "transfer") {
+ source = message.getHeaders().getAsString("src");
+ destination = message.getHeaders().getAsString("dest");
+ expected = message.getHeaders().getAsInt("count");
+ transfered = 0;
+ QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " <<
+ source << " to " << destination);
+ subscribeToSource(controlSubscription.getSubscriptionManager());
+ } else if (message.getData() == "quit") {
+ QPID_LOG(info, "received quit request");
+ controlSubscription.cancel();
+ } else {
+ std::cerr << "Rejecting invalid message: " << message.getData() << std::endl;
+ controlSubscription.getSession().messageReject(SequenceSet(message.getId()));
+ }
+ }
+
+};
+
+struct Worker : FailoverManager::Command, Runnable
+{
+ FailoverManager& connection;
+ Transfer transfer;
+ Thread runner;
+
+ Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {}
+
+ void run()
+ {
+ connection.execute(*this);
+ }
+
+ void start()
+ {
+ runner = Thread(this);
+ }
+
+ void join()
+ {
+ runner.join();
+ }
+
+ void execute(AsyncSession& session, bool isRetry)
+ {
+ if (isRetry) QPID_LOG(info, "Retrying...");
+ session.txSelect();
+ SubscriptionManager subs(session);
+ transfer.subscribeToControl(subs);
+ subs.run();
+ session.txCommit();//commit accept of control messages
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ connection.connect();
+ if (opts.workers == 1) {
+ Worker worker(connection, opts.workQueue);
+ worker.run();
+ } else {
+ boost::ptr_vector<Worker> workers;
+ for (size_t i = 0; i < opts.workers; i++) {
+ workers.push_back(new Worker(connection, opts.workQueue));
+ }
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1));
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::join, _1));
+ }
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/unit_test.cpp b/qpid/cpp/src/tests/unit_test.cpp
new file mode 100644
index 0000000000..00c61242e4
--- /dev/null
+++ b/qpid/cpp/src/tests/unit_test.cpp
@@ -0,0 +1,23 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+// Defines test_main function to link with actual unit test code.
+#define BOOST_AUTO_TEST_MAIN // Boost 1.33
+#define BOOST_TEST_MAIN
+#include "unit_test.h"
+
diff --git a/qpid/cpp/src/tests/unit_test.h b/qpid/cpp/src/tests/unit_test.h
new file mode 100644
index 0000000000..ed9623bcc0
--- /dev/null
+++ b/qpid/cpp/src/tests/unit_test.h
@@ -0,0 +1,70 @@
+#ifndef QPIPD_TEST_UNIT_TEST_H_
+#define QPIPD_TEST_UNIT_TEST_H_
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Workaround so we can build against boost 1.33 and boost 1.34.
+// Remove when we no longer need to support 1.33.
+//
+#include <boost/version.hpp>
+#include <limits.h> // Must be inclued beofre boost/test headers.
+
+// #include the correct header file.
+//
+#if (BOOST_VERSION < 103400)
+# include <boost/test/auto_unit_test.hpp>
+#else
+# include <boost/test/unit_test.hpp>
+#endif // BOOST_VERSION
+
+// Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END
+//
+#if (BOOST_VERSION < 103300)
+
+# define QPID_AUTO_TEST_SUITE(name)
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_UNIT_TEST(name)
+# define QPID_AUTO_TEST_SUITE_END()
+
+#elif (BOOST_VERSION < 103400)
+// Note the trailing ';'
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name);
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END();
+
+#endif // Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END
+
+//
+// Default definitions for latest version of boost.
+//
+
+#ifndef QPID_AUTO_TEST_SUITE
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name)
+#endif
+
+#ifndef QPID_AUTO_TEST_CASE
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
+#endif
+
+#ifndef QPID_AUTO_TEST_SUITE_END
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
+#endif
+
+#endif // !QPIPD_TEST_UNIT_TEST_H_
diff --git a/qpid/cpp/src/tests/verify_cluster_objects b/qpid/cpp/src/tests/verify_cluster_objects
new file mode 100755
index 0000000000..94661cf6b9
--- /dev/null
+++ b/qpid/cpp/src/tests/verify_cluster_objects
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Verify managment objects are consistent in a cluster.
+# Arguments: url of one broker in the cluster.
+
+import qmf.console, sys, re
+
+class Session(qmf.console.Session):
+ """A qmf.console.Session that caches useful values"""
+
+ def __init__(self):
+ qmf.console.Session.__init__(self)
+ self.classes = None
+
+ def all_classes(self):
+ if self.classes is None:
+ self.classes = [c for p in self.getPackages() for c in self.getClasses(p)]
+ return self.classes
+
+class Broker:
+ def __init__(self, url, qmf):
+ self.url = url
+ self.qmf = qmf
+ self.broker = self.qmf.addBroker(url)
+ self.broker._waitForStable()
+ self.objects = None
+ self.ignore_list = [ re.compile("org.apache.qpid.broker:system:") ]
+
+ def get_objects(self):
+ def ignore(name):
+ for m in self.ignore_list:
+ if m.match(name): return True
+ if self.objects is None:
+ obj_list = []
+ ignored=0
+ for c in self.qmf.all_classes():
+ for o in self.qmf.getObjects(_key=c, _broker=self.broker):
+ name=o.getObjectId().getObject()
+ if not ignore(name): obj_list.append(name)
+ else: ignored += 1
+ self.objects = set(obj_list)
+ if (len(obj_list) != len(self.objects)):
+ raise Exception("Duplicates in object list for %s"%(self.url))
+ print "%d objects on %s, ignored %d."%(len(self.objects), self.url, ignored)
+ return self.objects
+
+ def compare(self,other):
+ def compare1(x,y):
+ diff = x.get_objects() - y.get_objects()
+ if diff:
+ print "ERROR: found on %s but not %s"%(x, y)
+ for o in diff: print " %s"%(o)
+ return False
+ return True
+
+ so = compare1(self, other)
+ os = compare1(other, self)
+ return so and os
+
+ def __str__(self): return self.url
+
+ def get_cluster(self):
+ """Given one Broker, return list of all brokers in its cluster"""
+ clusters = self.qmf.getObjects(_class="cluster")
+ if not clusters: raise ("%s is not a cluster member"%(self.url))
+ def first_address(url):
+ """Python doesn't understand the brokers URL syntax. Extract a simple addres"""
+ return re.compile("amqp:tcp:([^,]*)").match(url).group(1)
+ return [Broker(first_address(url), self.qmf)
+ for url in clusters[0].members.split(";")]
+
+ def __del__(self): self.qmf.delBroker(self.broker)
+
+def main(argv=None):
+ if argv is None: argv = sys.argv
+ qmf = Session()
+ brokers = Broker(argv[1], qmf).get_cluster()
+ print "%d members in cluster."%(len(brokers))
+ base = brokers.pop(0)
+ try:
+ for b in brokers:
+ if not base.compare(b): return 1
+ print "No differences."
+ return 0
+ finally:
+ del base
+ del brokers
+
+if __name__ == "__main__": sys.exit(main())
diff --git a/qpid/cpp/src/tests/vg_check b/qpid/cpp/src/tests/vg_check
new file mode 100644
index 0000000000..462f4cb5e4
--- /dev/null
+++ b/qpid/cpp/src/tests/vg_check
@@ -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.
+#
+
+# Check for valgrind errors. Sourced by test scripts.
+
+vg_failed() {
+ echo "Valgrind error log in $VG_LOG." 1>&2
+ cat $VG_LOG 1>&2
+ echo $1 1>&2
+ exit 1
+}
+
+vg_check()
+{
+ test -z "$1" || VG_LOG=$1
+ test -f $VG_LOG || vg_failed Valgrind log file $VG_LOG missing.
+ # Ensure there is an ERROR SUMMARY line.
+ grep -E '^==[0-9]+== ERROR SUMMARY:' $VG_LOG > /dev/null || \
+ vg_failed "No valgrind ERROR SUMMARY line in $VG_LOG."
+ # Ensure that the number of errors is 0.
+ grep -E '^==[0-9]+== ERROR SUMMARY: [^0]' $VG_LOG > /dev/null && \
+ vg_failed "Valgrind reported errors in $VG_LOG; see above."
+ # Check for leaks.
+ grep -E '^==[0-9]+== +.* lost: [^0]' $VG_LOG && \
+ vg_failed "Found memory leaks (see log file, $VG_LOG); see above."
+ true
+}
diff --git a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
new file mode 100644
index 0000000000..024f20b147
--- /dev/null
+++ b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
@@ -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.
+ *
+ */
+
+// This file intends to prevent Windows from throwing up error boxes and
+// offering to debug when serious errors happen. The errors are displayed
+// on stderr instead. The purpose of this is to allow the tests to proceed
+// scripted and catch the text for logging. If this behavior is desired,
+// include this file with the executable being built. If the default
+// behaviors are desired, don't include this file in the build.
+
+#if defined(_MSC_VER)
+#include <crtdbg.h>
+#endif
+#include <windows.h>
+#include <iostream>
+
+namespace {
+
+// Instead of popping up a window for exceptions, just print something out
+LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo)
+{
+ DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
+
+ if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ std::cerr << "\nERROR: ACCESS VIOLATION\n" << std::endl;
+ else
+ std::cerr << "\nERROR: UNHANDLED EXCEPTION\n" << std::endl;
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+struct redirect_errors_to_stderr {
+ redirect_errors_to_stderr ();
+};
+
+static redirect_errors_to_stderr block;
+
+redirect_errors_to_stderr::redirect_errors_to_stderr()
+{
+#if defined(_MSC_VER)
+ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif
+
+ // Prevent the system from displaying the critical-error-handler
+ // and can't-open-file message boxes.
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ SetErrorMode(SEM_NOOPENFILEERRORBOX);
+
+ // And this will catch all unhandled exceptions.
+ SetUnhandledExceptionFilter (&UnhandledExceptionFilter);
+}
+
+} // namespace
diff --git a/qpid/cpp/src/windows/QpiddBroker.cpp b/qpid/cpp/src/windows/QpiddBroker.cpp
new file mode 100644
index 0000000000..50bb45979c
--- /dev/null
+++ b/qpid/cpp/src/windows/QpiddBroker.cpp
@@ -0,0 +1,310 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#else
+// These need to be made something sensible, like reading a value from
+// the registry. But for now, get things going with a local definition.
+namespace {
+const char *QPIDD_CONF_FILE = "qpid_broker.conf";
+const char *QPIDD_MODULE_DIR = ".";
+}
+#endif
+#include "qpidd.h"
+#include "qpid/Exception.h"
+#include "qpid/Options.h"
+#include "qpid/Plugin.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/sys/windows/check.h"
+#include "qpid/broker/Broker.h"
+
+#include <iostream>
+#include <windows.h>
+
+using namespace qpid::broker;
+
+BootstrapOptions::BootstrapOptions(const char* argv0)
+ : qpid::Options("Options"),
+ common("", QPIDD_CONF_FILE),
+ module(QPIDD_MODULE_DIR),
+ log(argv0)
+{
+ add(common);
+ add(module);
+ add(log);
+}
+
+// Local functions to set and get the pid via a LockFile.
+namespace {
+
+const std::string TCP = "tcp";
+
+// ShutdownEvent maintains an event that can be used to ask the broker
+// to stop. Analogous to sending SIGTERM/SIGINT to the posix broker.
+// The signal() method signals the event.
+class ShutdownEvent {
+ public:
+ ShutdownEvent(int port);
+ ~ShutdownEvent();
+
+ void create();
+ void open();
+ void signal();
+
+ private:
+ std::string eventName;
+
+ protected:
+ HANDLE event;
+};
+
+class ShutdownHandler : public ShutdownEvent, public qpid::sys::Runnable {
+ public:
+ ShutdownHandler(int port, const boost::intrusive_ptr<Broker>& b)
+ : ShutdownEvent(port) { broker = b; }
+
+ private:
+ virtual void run(); // Inherited from Runnable
+ boost::intrusive_ptr<Broker> broker;
+};
+
+ShutdownEvent::ShutdownEvent(int port) : event(NULL) {
+ std::ostringstream name;
+ name << "qpidd_" << port << std::ends;
+ eventName = name.str();
+}
+
+void ShutdownEvent::create() {
+ // Auto-reset event in case multiple processes try to signal a
+ // broker that doesn't respond for some reason. Initially not signaled.
+ event = ::CreateEvent(NULL, false, false, eventName.c_str());
+ QPID_WINDOWS_CHECK_NULL(event);
+}
+
+void ShutdownEvent::open() {
+ // TODO: Might need to search Global\\ name if unadorned name fails
+ event = ::OpenEvent(EVENT_MODIFY_STATE, false, eventName.c_str());
+ QPID_WINDOWS_CHECK_NULL(event);
+}
+
+ShutdownEvent::~ShutdownEvent() {
+ ::CloseHandle(event);
+ event = NULL;
+}
+
+void ShutdownEvent::signal() {
+ QPID_WINDOWS_CHECK_NOT(::SetEvent(event), 0);
+}
+
+
+void ShutdownHandler::run() {
+ if (event == NULL)
+ return;
+ ::WaitForSingleObject(event, INFINITE);
+ if (broker.get()) {
+ broker->shutdown();
+ broker = 0; // Release the broker reference
+ }
+}
+
+// Console control handler to properly handle ctl-c.
+int ourPort;
+BOOL CtrlHandler(DWORD ctl)
+{
+ ShutdownEvent shutter(ourPort); // We have to have set up the port before interrupting
+ shutter.open();
+ shutter.signal();
+ return ((ctl == CTRL_C_EVENT || ctl == CTRL_CLOSE_EVENT) ? TRUE : FALSE);
+}
+
+template <typename T>
+class NamedSharedMemory {
+ std::string name;
+ HANDLE memory;
+ T* data;
+
+public:
+ NamedSharedMemory(const std::string&);
+ ~NamedSharedMemory();
+
+ T& create();
+ T& get();
+};
+
+template <typename T>
+NamedSharedMemory<T>::NamedSharedMemory(const std::string& n) :
+ name(n),
+ memory(NULL),
+ data(0)
+{}
+
+template <typename T>
+NamedSharedMemory<T>::~NamedSharedMemory() {
+ if (data)
+ ::UnmapViewOfFile(data);
+ if (memory != NULL)
+ ::CloseHandle(memory);
+}
+
+template <typename T>
+T& NamedSharedMemory<T>::create() {
+ assert(memory == NULL);
+
+ // Create named shared memory file
+ memory = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(T), name.c_str());
+ QPID_WINDOWS_CHECK_NULL(memory);
+
+ // Map file into memory
+ data = static_cast<T*>(::MapViewOfFile(memory, FILE_MAP_WRITE, 0, 0, 0));
+ QPID_WINDOWS_CHECK_NULL(data);
+
+ return *data;
+}
+
+template <typename T>
+T& NamedSharedMemory<T>::get() {
+ if (memory == NULL) {
+ // TODO: Might need to search Global\\ name if unadorned name fails
+ memory = ::OpenFileMapping(FILE_MAP_WRITE, FALSE, name.c_str());
+ QPID_WINDOWS_CHECK_NULL(memory);
+
+ data = static_cast<T*>(::MapViewOfFile(memory, FILE_MAP_WRITE, 0, 0, 0));
+ QPID_WINDOWS_CHECK_NULL(data);
+ }
+
+ return *data;
+}
+
+std::string brokerInfoName(uint16_t port)
+{
+ std::ostringstream path;
+ path << "qpidd_info_" << port;
+ return path.str();
+}
+
+struct BrokerInfo {
+ DWORD pid;
+};
+
+}
+
+struct ProcessControlOptions : public qpid::Options {
+ bool quit;
+ bool check;
+ std::string transport;
+
+ ProcessControlOptions()
+ : qpid::Options("Process control options"),
+ quit(false),
+ check(false),
+ transport(TCP)
+ {
+ addOptions()
+ ("check,c", qpid::optValue(check), "Prints the broker's process ID to stdout and returns 0 if the broker is running, otherwise returns 1")
+ ("transport", qpid::optValue(transport, "TRANSPORT"), "The transport for which to return the port")
+ ("quit,q", qpid::optValue(quit), "Tells the broker to shut down");
+ }
+};
+
+struct QpiddWindowsOptions : public QpiddOptionsPrivate {
+ ProcessControlOptions control;
+ QpiddWindowsOptions(QpiddOptions *parent) : QpiddOptionsPrivate(parent) {
+ parent->add(control);
+ }
+};
+
+QpiddOptions::QpiddOptions(const char* argv0)
+ : qpid::Options("Options"),
+ common("", QPIDD_CONF_FILE),
+ module(QPIDD_MODULE_DIR),
+ log(argv0)
+{
+ add(common);
+ add(module);
+ add(broker);
+ add(log);
+
+ platform.reset(new QpiddWindowsOptions(this));
+ qpid::Plugin::addOptions(*this);
+}
+
+void QpiddOptions::usage() const {
+ std::cout << "Usage: qpidd [OPTIONS]" << std::endl << std::endl
+ << *this << std::endl;
+}
+
+int QpiddBroker::execute (QpiddOptions *options) {
+ // Options that affect a running daemon.
+ QpiddWindowsOptions *myOptions =
+ reinterpret_cast<QpiddWindowsOptions *>(options->platform.get());
+ if (myOptions == 0)
+ throw qpid::Exception("Internal error obtaining platform options");
+
+ if (myOptions->control.check || myOptions->control.quit) {
+ // Relies on port number being set via --port or QPID_PORT env variable.
+ NamedSharedMemory<BrokerInfo> info(brokerInfoName(options->broker.port));
+ int pid = info.get().pid;
+ if (pid < 0)
+ return 1;
+ if (myOptions->control.check)
+ std::cout << pid << std::endl;
+ if (myOptions->control.quit) {
+ ShutdownEvent shutter(options->broker.port);
+ shutter.open();
+ shutter.signal();
+ HANDLE brokerHandle = ::OpenProcess(SYNCHRONIZE, false, pid);
+ QPID_WINDOWS_CHECK_NULL(brokerHandle);
+ ::WaitForSingleObject(brokerHandle, INFINITE);
+ ::CloseHandle(brokerHandle);
+ }
+ return 0;
+ }
+
+ boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker));
+
+ // Need the correct port number to use in the pid file name.
+ if (options->broker.port == 0)
+ options->broker.port = brokerPtr->getPort(myOptions->control.transport);
+
+ BrokerInfo info;
+ info.pid = ::GetCurrentProcessId();
+
+ NamedSharedMemory<BrokerInfo> sharedInfo(brokerInfoName(options->broker.port));
+ sharedInfo.create() = info;
+
+ // Allow the broker to receive a shutdown request via a qpidd --quit
+ // command. Note that when the broker is run as a service this operation
+ // should not be allowed.
+ ourPort = options->broker.port;
+ ShutdownHandler waitShut(ourPort, brokerPtr);
+ waitShut.create();
+ qpid::sys::Thread waitThr(waitShut); // Wait for shutdown event
+ ::SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
+ brokerPtr->accept();
+ std::cout << options->broker.port << std::endl;
+ brokerPtr->run();
+ waitShut.signal(); // In case we shut down some other way
+ waitThr.join();
+
+ // CloseHandle(h);
+ return 0;
+}
diff --git a/qpid/cpp/src/windows/resources/qpid-icon.ico b/qpid/cpp/src/windows/resources/qpid-icon.ico
new file mode 100644
index 0000000000..112f5d8f1f
--- /dev/null
+++ b/qpid/cpp/src/windows/resources/qpid-icon.ico
Binary files differ
diff --git a/qpid/cpp/src/windows/resources/template-resource.rc b/qpid/cpp/src/windows/resources/template-resource.rc
new file mode 100644
index 0000000000..725d1c9391
--- /dev/null
+++ b/qpid/cpp/src/windows/resources/template-resource.rc
@@ -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.
+//
+
+#include "version-resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "version-resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION ${winverFileVersionBinary}
+ PRODUCTVERSION ${winverProductVersionBinary}
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "${winverFileDescription}"
+ VALUE "FileVersion", "${winverFileVersionString}"
+ VALUE "LegalCopyright", "${winverLegalCopyright}"
+ VALUE "InternalName", "${winverInternalName}"
+ VALUE "OriginalFilename", "${winverOriginalFilename}"
+ VALUE "ProductName", "${winverProductName}"
+ VALUE "ProductVersion", "${winverProductVersionString}"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "qpid-icon.ico"
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/qpid/cpp/src/windows/resources/version-resource.h b/qpid/cpp/src/windows/resources/version-resource.h
new file mode 100644
index 0000000000..bf942abbaf
--- /dev/null
+++ b/qpid/cpp/src/windows/resources/version-resource.h
@@ -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.
+//
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Preserved for common usage by any Qpid exe/dll.
+
+#define IDI_ICON1 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/qpid/cpp/src/xml.mk b/qpid/cpp/src/xml.mk
new file mode 100644
index 0000000000..baf3803647
--- /dev/null
+++ b/qpid/cpp/src/xml.mk
@@ -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.
+#
+dmoduleexec_LTLIBRARIES += xml.la
+
+xml_la_SOURCES = \
+ qpid/xml/XmlExchange.cpp \
+ qpid/xml/XmlExchange.h \
+ qpid/xml/XmlExchangePlugin.cpp
+
+xml_la_LIBADD = -lxerces-c -lxqilla libqpidbroker.la
+
+xml_la_LDFLAGS = $(PLUGINLDFLAGS)
+