summaryrefslogtreecommitdiff
path: root/cpp/src/qpid
diff options
context:
space:
mode:
authorRafael H. Schloming <rhs@apache.org>2009-12-26 12:42:57 +0000
committerRafael H. Schloming <rhs@apache.org>2009-12-26 12:42:57 +0000
commit248f1fe188fe2307b9dcf2c87a83b653eaa1920c (patch)
treed5d0959a70218946ff72e107a6c106e32479a398 /cpp/src/qpid
parent3c83a0e3ec7cf4dc23e83a340b25f5fc1676f937 (diff)
downloadqpid-python-248f1fe188fe2307b9dcf2c87a83b653eaa1920c.tar.gz
synchronized with trunk except for ruby dir
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qpid.rnr@893970 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp/src/qpid')
-rw-r--r--cpp/src/qpid/Address.cpp58
-rw-r--r--cpp/src/qpid/DataDir.cpp25
-rw-r--r--cpp/src/qpid/DataDir.h14
-rw-r--r--cpp/src/qpid/Exception.cpp4
-rw-r--r--cpp/src/qpid/Exception.h85
-rw-r--r--cpp/src/qpid/InlineAllocator.h84
-rw-r--r--cpp/src/qpid/InlineVector.h68
-rw-r--r--cpp/src/qpid/Modules.cpp95
-rw-r--r--cpp/src/qpid/Modules.h44
-rw-r--r--cpp/src/qpid/Msg.h61
-rw-r--r--cpp/src/qpid/Options.cpp5
-rw-r--r--cpp/src/qpid/Options.h257
-rw-r--r--cpp/src/qpid/Plugin.cpp22
-rw-r--r--cpp/src/qpid/Plugin.h33
-rw-r--r--cpp/src/qpid/RangeSet.h324
-rw-r--r--cpp/src/qpid/RefCounted.h4
-rw-r--r--cpp/src/qpid/RefCountedBuffer.cpp53
-rw-r--r--cpp/src/qpid/RefCountedBuffer.h89
-rw-r--r--cpp/src/qpid/SessionId.cpp4
-rw-r--r--cpp/src/qpid/SessionId.h59
-rw-r--r--cpp/src/qpid/SessionState.cpp66
-rw-r--r--cpp/src/qpid/SessionState.h91
-rw-r--r--cpp/src/qpid/StringUtils.cpp2
-rw-r--r--cpp/src/qpid/StringUtils.h6
-rw-r--r--cpp/src/qpid/Url.cpp215
-rw-r--r--cpp/src/qpid/Url.h109
-rwxr-xr-xcpp/src/qpid/Version.h44
-rw-r--r--cpp/src/qpid/acl/Acl.cpp180
-rw-r--r--cpp/src/qpid/acl/Acl.h32
-rw-r--r--cpp/src/qpid/acl/AclData.cpp236
-rw-r--r--cpp/src/qpid/acl/AclData.h83
-rw-r--r--cpp/src/qpid/acl/AclPlugin.cpp33
-rw-r--r--cpp/src/qpid/acl/AclReader.cpp507
-rw-r--r--cpp/src/qpid/acl/AclReader.h95
-rw-r--r--cpp/src/qpid/acl/management-schema.xml44
-rw-r--r--cpp/src/qpid/agent/ManagementAgent.h127
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.cpp881
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.h177
-rw-r--r--cpp/src/qpid/amqp_0_10/Array.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/Codec.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/Command.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/Connection.cpp93
-rw-r--r--cpp/src/qpid/amqp_0_10/Connection.h40
-rw-r--r--cpp/src/qpid/amqp_0_10/Control.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/Exception.h98
-rw-r--r--cpp/src/qpid/amqp_0_10/FrameHeader.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/Header.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/Holder.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/Map.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/SessionHandler.cpp121
-rw-r--r--cpp/src/qpid/amqp_0_10/SessionHandler.h75
-rw-r--r--cpp/src/qpid/amqp_0_10/Struct.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/Struct32.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/Unit.cpp4
-rw-r--r--cpp/src/qpid/amqp_0_10/UnknownType.cpp2
-rw-r--r--cpp/src/qpid/amqp_0_10/UnknownType.h2
-rw-r--r--cpp/src/qpid/amqp_0_10/built_in_types.h7
-rw-r--r--cpp/src/qpid/assert.cpp2
-rw-r--r--cpp/src/qpid/broker/AclModule.h244
-rw-r--r--cpp/src/qpid/broker/Bridge.cpp230
-rw-r--r--cpp/src/qpid/broker/Bridge.h45
-rw-r--r--cpp/src/qpid/broker/Broker.cpp360
-rw-r--r--cpp/src/qpid/broker/Broker.h184
-rw-r--r--cpp/src/qpid/broker/BrokerImportExport.h33
-rw-r--r--cpp/src/qpid/broker/BrokerSingleton.h52
-rw-r--r--cpp/src/qpid/broker/Connection.cpp230
-rw-r--r--cpp/src/qpid/broker/Connection.h117
-rw-r--r--cpp/src/qpid/broker/ConnectionFactory.cpp29
-rw-r--r--cpp/src/qpid/broker/ConnectionFactory.h9
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.cpp202
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.h49
-rw-r--r--cpp/src/qpid/broker/ConnectionState.h68
-rw-r--r--cpp/src/qpid/broker/ConnectionToken.h2
-rw-r--r--cpp/src/qpid/broker/Consumer.h62
-rw-r--r--cpp/src/qpid/broker/Daemon.cpp145
-rw-r--r--cpp/src/qpid/broker/Daemon.h4
-rw-r--r--cpp/src/qpid/broker/Deliverable.h6
-rw-r--r--cpp/src/qpid/broker/DeliverableMessage.cpp6
-rw-r--r--cpp/src/qpid/broker/DeliverableMessage.h15
-rw-r--r--cpp/src/qpid/broker/DeliveryAdapter.h39
-rw-r--r--cpp/src/qpid/broker/DeliveryRecord.cpp152
-rw-r--r--cpp/src/qpid/broker/DeliveryRecord.h120
-rw-r--r--cpp/src/qpid/broker/DirectExchange.cpp189
-rw-r--r--cpp/src/qpid/broker/DirectExchange.h63
-rw-r--r--cpp/src/qpid/broker/DtxAck.cpp27
-rw-r--r--cpp/src/qpid/broker/DtxAck.h9
-rw-r--r--cpp/src/qpid/broker/DtxBuffer.cpp2
-rw-r--r--cpp/src/qpid/broker/DtxBuffer.h9
-rw-r--r--cpp/src/qpid/broker/DtxManager.cpp15
-rw-r--r--cpp/src/qpid/broker/DtxManager.h14
-rw-r--r--cpp/src/qpid/broker/DtxTimeout.cpp4
-rw-r--r--cpp/src/qpid/broker/DtxTimeout.h6
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.cpp4
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.h20
-rw-r--r--cpp/src/qpid/broker/Exchange.cpp291
-rw-r--r--cpp/src/qpid/broker/Exchange.h240
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.cpp46
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.h99
-rw-r--r--cpp/src/qpid/broker/ExpiryPolicy.cpp (renamed from cpp/src/qpid/client/AsyncSession.h)26
-rw-r--r--cpp/src/qpid/broker/ExpiryPolicy.h46
-rw-r--r--cpp/src/qpid/broker/FanOutExchange.cpp130
-rw-r--r--cpp/src/qpid/broker/FanOutExchange.h38
-rw-r--r--cpp/src/qpid/broker/HandlerImpl.h6
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.cpp107
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.h53
-rw-r--r--cpp/src/qpid/broker/IncomingExecutionContext.cpp143
-rw-r--r--cpp/src/qpid/broker/IncomingExecutionContext.h61
-rw-r--r--cpp/src/qpid/broker/IncompleteMessageList.cpp45
-rw-r--r--cpp/src/qpid/broker/IncompleteMessageList.h24
-rw-r--r--cpp/src/qpid/broker/Link.cpp273
-rw-r--r--cpp/src/qpid/broker/Link.h30
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.cpp164
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.h49
-rw-r--r--cpp/src/qpid/broker/Message.cpp219
-rw-r--r--cpp/src/qpid/broker/Message.h102
-rw-r--r--cpp/src/qpid/broker/MessageAdapter.cpp2
-rw-r--r--cpp/src/qpid/broker/MessageBuilder.cpp38
-rw-r--r--cpp/src/qpid/broker/MessageBuilder.h8
-rw-r--r--cpp/src/qpid/broker/MessageDelivery.cpp89
-rw-r--r--cpp/src/qpid/broker/MessageDelivery.h55
-rw-r--r--cpp/src/qpid/broker/MessageStore.h64
-rw-r--r--cpp/src/qpid/broker/MessageStoreModule.cpp33
-rw-r--r--cpp/src/qpid/broker/MessageStoreModule.h25
-rw-r--r--cpp/src/qpid/broker/NameGenerator.cpp2
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.cpp95
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.h90
-rw-r--r--cpp/src/qpid/broker/PersistableConfig.h2
-rw-r--r--cpp/src/qpid/broker/PersistableExchange.h2
-rw-r--r--cpp/src/qpid/broker/PersistableMessage.cpp139
-rw-r--r--cpp/src/qpid/broker/PersistableMessage.h164
-rw-r--r--cpp/src/qpid/broker/PersistableQueue.h2
-rw-r--r--cpp/src/qpid/broker/Queue.cpp785
-rw-r--r--cpp/src/qpid/broker/Queue.h238
-rw-r--r--cpp/src/qpid/broker/QueueBindings.cpp11
-rw-r--r--cpp/src/qpid/broker/QueueBindings.h30
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.cpp57
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.h59
-rw-r--r--cpp/src/qpid/broker/QueueEvents.cpp122
-rw-r--r--cpp/src/qpid/broker/QueueEvents.h84
-rw-r--r--cpp/src/qpid/broker/QueueListeners.cpp81
-rw-r--r--cpp/src/qpid/broker/QueueListeners.h75
-rw-r--r--cpp/src/qpid/broker/QueuePolicy.cpp257
-rw-r--r--cpp/src/qpid/broker/QueuePolicy.h116
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.cpp30
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.h49
-rw-r--r--cpp/src/qpid/broker/QueuedMessage.h48
-rw-r--r--cpp/src/qpid/broker/RateFlowcontrol.h105
-rw-r--r--cpp/src/qpid/broker/RateTracker.cpp51
-rw-r--r--cpp/src/qpid/broker/RateTracker.h57
-rw-r--r--cpp/src/qpid/broker/RecoverableExchange.h4
-rw-r--r--cpp/src/qpid/broker/RecoverableMessage.h1
-rw-r--r--cpp/src/qpid/broker/RecoverableQueue.h2
-rw-r--r--cpp/src/qpid/broker/RecoverableTransaction.h4
-rw-r--r--cpp/src/qpid/broker/RecoveredDequeue.cpp17
-rw-r--r--cpp/src/qpid/broker/RecoveredDequeue.h14
-rw-r--r--cpp/src/qpid/broker/RecoveredEnqueue.cpp8
-rw-r--r--cpp/src/qpid/broker/RecoveredEnqueue.h15
-rw-r--r--cpp/src/qpid/broker/RecoveryManager.h12
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.cpp66
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.h10
-rw-r--r--cpp/src/qpid/broker/RetryList.cpp60
-rw-r--r--cpp/src/qpid/broker/RetryList.h54
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.cpp183
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.h10
-rw-r--r--cpp/src/qpid/broker/SecureConnection.cpp87
-rw-r--r--cpp/src/qpid/broker/SecureConnection.h60
-rw-r--r--cpp/src/qpid/broker/SecureConnectionFactory.cpp73
-rw-r--r--cpp/src/qpid/broker/SecureConnectionFactory.h50
-rw-r--r--cpp/src/qpid/broker/SemanticState.cpp436
-rw-r--r--cpp/src/qpid/broker/SemanticState.h147
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.cpp390
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.h25
-rw-r--r--cpp/src/qpid/broker/SessionContext.h9
-rw-r--r--cpp/src/qpid/broker/SessionHandler.cpp60
-rw-r--r--cpp/src/qpid/broker/SessionHandler.h29
-rw-r--r--cpp/src/qpid/broker/SessionManager.cpp15
-rw-r--r--cpp/src/qpid/broker/SessionState.cpp259
-rw-r--r--cpp/src/qpid/broker/SessionState.h71
-rw-r--r--cpp/src/qpid/broker/SignalHandler.cpp9
-rw-r--r--cpp/src/qpid/broker/SignalHandler.h3
-rw-r--r--cpp/src/qpid/broker/System.cpp36
-rw-r--r--cpp/src/qpid/broker/System.h8
-rw-r--r--cpp/src/qpid/broker/Timer.cpp104
-rw-r--r--cpp/src/qpid/broker/Timer.h79
-rw-r--r--cpp/src/qpid/broker/TopicExchange.cpp343
-rw-r--r--cpp/src/qpid/broker/TopicExchange.h82
-rw-r--r--cpp/src/qpid/broker/TxAccept.cpp65
-rw-r--r--cpp/src/qpid/broker/TxAccept.h38
-rw-r--r--cpp/src/qpid/broker/TxBuffer.cpp7
-rw-r--r--cpp/src/qpid/broker/TxBuffer.h18
-rw-r--r--cpp/src/qpid/broker/TxOp.h10
-rw-r--r--cpp/src/qpid/broker/TxOpVisitor.h97
-rw-r--r--cpp/src/qpid/broker/TxPublish.cpp60
-rw-r--r--cpp/src/qpid/broker/TxPublish.h45
-rw-r--r--cpp/src/qpid/broker/Vhost.cpp24
-rw-r--r--cpp/src/qpid/broker/Vhost.h11
-rw-r--r--cpp/src/qpid/broker/XmlExchange.cpp277
-rw-r--r--cpp/src/qpid/broker/posix/BrokerDefaults.cpp (renamed from cpp/src/qpid/broker/Prefetch.h)30
-rw-r--r--cpp/src/qpid/broker/windows/BrokerDefaults.cpp (renamed from cpp/src/qpid/broker/DeliveryToken.h)32
-rw-r--r--cpp/src/qpid/broker/windows/SaslAuthenticator.cpp190
-rw-r--r--cpp/src/qpid/client/Bounds.cpp25
-rw-r--r--cpp/src/qpid/client/Completion.cpp (renamed from cpp/src/qpid/client/AckPolicy.cpp)34
-rw-r--r--cpp/src/qpid/client/Completion.h71
-rw-r--r--cpp/src/qpid/client/CompletionImpl.h51
-rw-r--r--cpp/src/qpid/client/Connection.cpp94
-rw-r--r--cpp/src/qpid/client/Connection.h149
-rw-r--r--cpp/src/qpid/client/ConnectionAccess.h (renamed from cpp/src/qpid/client/FutureCompletion.h)32
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.cpp188
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.h34
-rw-r--r--cpp/src/qpid/client/ConnectionImpl.cpp202
-rw-r--r--cpp/src/qpid/client/ConnectionImpl.h25
-rw-r--r--cpp/src/qpid/client/ConnectionSettings.cpp15
-rw-r--r--cpp/src/qpid/client/ConnectionSettings.h114
-rw-r--r--cpp/src/qpid/client/Connector.cpp241
-rw-r--r--cpp/src/qpid/client/Connector.h121
-rw-r--r--cpp/src/qpid/client/Demux.cpp2
-rw-r--r--cpp/src/qpid/client/Demux.h21
-rw-r--r--cpp/src/qpid/client/Dispatcher.cpp91
-rw-r--r--cpp/src/qpid/client/Dispatcher.h42
-rw-r--r--cpp/src/qpid/client/Execution.h2
-rw-r--r--cpp/src/qpid/client/FailoverListener.cpp89
-rw-r--r--cpp/src/qpid/client/FailoverManager.cpp131
-rw-r--r--cpp/src/qpid/client/FlowControl.h73
-rw-r--r--cpp/src/qpid/client/Future.cpp3
-rw-r--r--cpp/src/qpid/client/Future.h64
-rw-r--r--cpp/src/qpid/client/FutureCompletion.cpp2
-rw-r--r--cpp/src/qpid/client/FutureResult.cpp4
-rw-r--r--cpp/src/qpid/client/LoadPlugins.cpp52
-rw-r--r--cpp/src/qpid/client/LocalQueue.cpp56
-rw-r--r--cpp/src/qpid/client/LocalQueue.h93
-rw-r--r--cpp/src/qpid/client/LocalQueueImpl.cpp78
-rw-r--r--cpp/src/qpid/client/LocalQueueImpl.h108
-rw-r--r--cpp/src/qpid/client/Message.cpp65
-rw-r--r--cpp/src/qpid/client/MessageImpl.cpp71
-rw-r--r--cpp/src/qpid/client/MessageImpl.h (renamed from cpp/src/qpid/client/Message.h)27
-rw-r--r--cpp/src/qpid/client/MessageListener.cpp2
-rw-r--r--cpp/src/qpid/client/MessageReplayTracker.cpp78
-rw-r--r--cpp/src/qpid/client/PrivateImplRef.h94
-rw-r--r--cpp/src/qpid/client/QueueOptions.cpp123
-rw-r--r--cpp/src/qpid/client/RdmaConnector.cpp387
-rw-r--r--cpp/src/qpid/client/Results.cpp9
-rw-r--r--cpp/src/qpid/client/Results.h1
-rw-r--r--cpp/src/qpid/client/Sasl.h70
-rw-r--r--cpp/src/qpid/client/SaslFactory.cpp379
-rw-r--r--cpp/src/qpid/client/SaslFactory.h48
-rw-r--r--cpp/src/qpid/client/SessionBase_0_10.cpp27
-rw-r--r--cpp/src/qpid/client/SessionBase_0_10.h106
-rw-r--r--cpp/src/qpid/client/SessionBase_0_10Access.h2
-rw-r--r--cpp/src/qpid/client/SessionImpl.cpp364
-rw-r--r--cpp/src/qpid/client/SessionImpl.h102
-rw-r--r--cpp/src/qpid/client/SslConnector.cpp400
-rw-r--r--cpp/src/qpid/client/StateManager.cpp14
-rw-r--r--cpp/src/qpid/client/StateManager.h1
-rw-r--r--cpp/src/qpid/client/Subscription.cpp55
-rw-r--r--cpp/src/qpid/client/SubscriptionImpl.cpp169
-rw-r--r--cpp/src/qpid/client/SubscriptionImpl.h125
-rw-r--r--cpp/src/qpid/client/SubscriptionManager.cpp146
-rw-r--r--cpp/src/qpid/client/SubscriptionManager.h210
-rw-r--r--cpp/src/qpid/client/SubscriptionManagerImpl.cpp162
-rw-r--r--cpp/src/qpid/client/SubscriptionManagerImpl.h278
-rw-r--r--cpp/src/qpid/client/TCPConnector.cpp327
-rw-r--r--cpp/src/qpid/client/TCPConnector.h117
-rw-r--r--cpp/src/qpid/client/TypedResult.h65
-rw-r--r--cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp111
-rw-r--r--cpp/src/qpid/client/amqp0_10/AcceptTracker.h85
-rw-r--r--cpp/src/qpid/client/amqp0_10/AddressResolution.cpp819
-rw-r--r--cpp/src/qpid/client/amqp0_10/AddressResolution.h64
-rw-r--r--cpp/src/qpid/client/amqp0_10/Codecs.cpp299
-rw-r--r--cpp/src/qpid/client/amqp0_10/CodecsInternal.h41
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp208
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.h72
-rw-r--r--cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp303
-rw-r--r--cpp/src/qpid/client/amqp0_10/IncomingMessages.h98
-rw-r--r--cpp/src/qpid/client/amqp0_10/MessageSink.h (renamed from cpp/src/qpid/client/AckPolicy.h)54
-rw-r--r--cpp/src/qpid/client/amqp0_10/MessageSource.h47
-rw-r--r--cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp67
-rw-r--r--cpp/src/qpid/client/amqp0_10/OutgoingMessage.h48
-rw-r--r--cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp194
-rw-r--r--cpp/src/qpid/client/amqp0_10/ReceiverImpl.h148
-rw-r--r--cpp/src/qpid/client/amqp0_10/SenderImpl.cpp144
-rw-r--r--cpp/src/qpid/client/amqp0_10/SenderImpl.h140
-rw-r--r--cpp/src/qpid/client/amqp0_10/SessionImpl.cpp433
-rw-r--r--cpp/src/qpid/client/amqp0_10/SessionImpl.h212
-rw-r--r--cpp/src/qpid/client/windows/SaslFactory.cpp146
-rw-r--r--cpp/src/qpid/cluster/ClassifierHandler.cpp51
-rw-r--r--cpp/src/qpid/cluster/ClassifierHandler.h50
-rw-r--r--cpp/src/qpid/cluster/Cluster.cpp1089
-rw-r--r--cpp/src/qpid/cluster/Cluster.h318
-rw-r--r--cpp/src/qpid/cluster/ClusterMap.cpp175
-rw-r--r--cpp/src/qpid/cluster/ClusterMap.h105
-rw-r--r--cpp/src/qpid/cluster/ClusterPlugin.cpp154
-rw-r--r--cpp/src/qpid/cluster/ClusterSettings.h (renamed from cpp/src/qpid/sys/posix/PrivatePosix.h)42
-rw-r--r--cpp/src/qpid/cluster/Connection.cpp479
-rw-r--r--cpp/src/qpid/cluster/Connection.h210
-rw-r--r--cpp/src/qpid/cluster/ConnectionCodec.cpp82
-rw-r--r--cpp/src/qpid/cluster/ConnectionCodec.h82
-rw-r--r--cpp/src/qpid/cluster/ConnectionInterceptor.cpp108
-rw-r--r--cpp/src/qpid/cluster/ConnectionInterceptor.h82
-rw-r--r--cpp/src/qpid/cluster/Cpg.cpp223
-rw-r--r--cpp/src/qpid/cluster/Cpg.h157
-rw-r--r--cpp/src/qpid/cluster/Decoder.cpp65
-rw-r--r--cpp/src/qpid/cluster/Decoder.h59
-rw-r--r--cpp/src/qpid/cluster/ErrorCheck.cpp155
-rw-r--r--cpp/src/qpid/cluster/ErrorCheck.h90
-rw-r--r--cpp/src/qpid/cluster/Event.cpp136
-rw-r--r--cpp/src/qpid/cluster/Event.h116
-rw-r--r--cpp/src/qpid/cluster/EventFrame.cpp41
-rw-r--r--cpp/src/qpid/cluster/EventFrame.h60
-rw-r--r--cpp/src/qpid/cluster/ExpiryPolicy.cpp85
-rw-r--r--cpp/src/qpid/cluster/ExpiryPolicy.h89
-rw-r--r--cpp/src/qpid/cluster/FailoverExchange.cpp99
-rw-r--r--cpp/src/qpid/cluster/FailoverExchange.h68
-rw-r--r--cpp/src/qpid/cluster/InitialStatusMap.cpp197
-rw-r--r--cpp/src/qpid/cluster/InitialStatusMap.h75
-rw-r--r--cpp/src/qpid/cluster/LockedConnectionMap.h65
-rw-r--r--cpp/src/qpid/cluster/McastFrameHandler.h46
-rw-r--r--cpp/src/qpid/cluster/MemberSet.cpp52
-rw-r--r--cpp/src/qpid/cluster/MemberSet.h43
-rw-r--r--cpp/src/qpid/cluster/Multicaster.cpp103
-rw-r--r--cpp/src/qpid/cluster/Multicaster.h87
-rw-r--r--cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h (renamed from cpp/src/qpid/cluster/ShadowConnectionOutputHandler.h)13
-rw-r--r--cpp/src/qpid/cluster/Numbering.h70
-rw-r--r--cpp/src/qpid/cluster/OutputInterceptor.cpp116
-rw-r--r--cpp/src/qpid/cluster/OutputInterceptor.h79
-rw-r--r--cpp/src/qpid/cluster/PollableCondition.cpp100
-rw-r--r--cpp/src/qpid/cluster/PollableQueue.h95
-rw-r--r--cpp/src/qpid/cluster/PollerDispatch.cpp67
-rw-r--r--cpp/src/qpid/cluster/PollerDispatch.h60
-rw-r--r--cpp/src/qpid/cluster/ProxyInputHandler.h57
-rw-r--r--cpp/src/qpid/cluster/Quorum.h (renamed from cpp/src/qpid/sys/Condition.h)13
-rw-r--r--cpp/src/qpid/cluster/Quorum_cman.cpp103
-rw-r--r--cpp/src/qpid/cluster/Quorum_cman.h64
-rw-r--r--cpp/src/qpid/cluster/Quorum_null.h (renamed from cpp/src/qpid/client/FutureResult.h)33
-rw-r--r--cpp/src/qpid/cluster/RetractClient.cpp61
-rw-r--r--cpp/src/qpid/cluster/RetractClient.h49
-rw-r--r--cpp/src/qpid/cluster/StoreStatus.cpp110
-rw-r--r--cpp/src/qpid/cluster/StoreStatus.h64
-rw-r--r--cpp/src/qpid/cluster/UpdateClient.cpp484
-rw-r--r--cpp/src/qpid/cluster/UpdateClient.h119
-rw-r--r--cpp/src/qpid/cluster/UpdateExchange.cpp47
-rw-r--r--cpp/src/qpid/cluster/UpdateExchange.h45
-rw-r--r--cpp/src/qpid/cluster/UpdateReceiver.h42
-rw-r--r--cpp/src/qpid/cluster/WatchDogPlugin.cpp136
-rw-r--r--cpp/src/qpid/cluster/management-schema.xml61
-rw-r--r--cpp/src/qpid/cluster/qpidd_watchdog.cpp63
-rw-r--r--cpp/src/qpid/cluster/types.h83
-rw-r--r--cpp/src/qpid/console/Agent.cpp (renamed from cpp/src/qpid/client/Session.h)23
-rw-r--r--cpp/src/qpid/console/Broker.cpp328
-rw-r--r--cpp/src/qpid/console/ClassKey.cpp105
-rw-r--r--cpp/src/qpid/console/Event.cpp205
-rw-r--r--cpp/src/qpid/console/Object.cpp384
-rw-r--r--cpp/src/qpid/console/ObjectId.cpp91
-rw-r--r--cpp/src/qpid/console/Package.cpp (renamed from cpp/src/qpid/sys/Runnable.h)43
-rw-r--r--cpp/src/qpid/console/Schema.cpp165
-rw-r--r--cpp/src/qpid/console/SequenceManager.cpp48
-rw-r--r--cpp/src/qpid/console/SessionManager.cpp481
-rw-r--r--cpp/src/qpid/console/Value.cpp169
-rw-r--r--cpp/src/qpid/doxygen_mainpage.h28
-rw-r--r--cpp/src/qpid/framing/AMQBody.h20
-rw-r--r--cpp/src/qpid/framing/AMQCommandControlBody.h2
-rw-r--r--cpp/src/qpid/framing/AMQContentBody.cpp10
-rw-r--r--cpp/src/qpid/framing/AMQContentBody.h28
-rw-r--r--cpp/src/qpid/framing/AMQDataBlock.h4
-rw-r--r--cpp/src/qpid/framing/AMQFrame.cpp80
-rw-r--r--cpp/src/qpid/framing/AMQFrame.h62
-rw-r--r--cpp/src/qpid/framing/AMQHeaderBody.cpp8
-rw-r--r--cpp/src/qpid/framing/AMQHeaderBody.h29
-rw-r--r--cpp/src/qpid/framing/AMQHeartbeatBody.cpp2
-rw-r--r--cpp/src/qpid/framing/AMQHeartbeatBody.h14
-rw-r--r--cpp/src/qpid/framing/AMQMethodBody.cpp2
-rw-r--r--cpp/src/qpid/framing/AMQMethodBody.h12
-rw-r--r--cpp/src/qpid/framing/AccumulatedAck.cpp2
-rw-r--r--cpp/src/qpid/framing/AccumulatedAck.h21
-rw-r--r--cpp/src/qpid/framing/Array.cpp59
-rw-r--r--cpp/src/qpid/framing/Array.h78
-rw-r--r--cpp/src/qpid/framing/Blob.cpp2
-rw-r--r--cpp/src/qpid/framing/Blob.h197
-rw-r--r--cpp/src/qpid/framing/BodyFactory.h (renamed from cpp/src/qpid/sys/Thread.h)45
-rw-r--r--cpp/src/qpid/framing/BodyHandler.cpp10
-rw-r--r--cpp/src/qpid/framing/BodyHolder.cpp76
-rw-r--r--cpp/src/qpid/framing/BodyHolder.h88
-rw-r--r--cpp/src/qpid/framing/Buffer.cpp26
-rw-r--r--cpp/src/qpid/framing/Buffer.h132
-rw-r--r--cpp/src/qpid/framing/ChannelHandler.h4
-rw-r--r--cpp/src/qpid/framing/Endian.cpp52
-rw-r--r--cpp/src/qpid/framing/Endian.h (renamed from cpp/src/qpid/sys/IOHandle.h)35
-rw-r--r--cpp/src/qpid/framing/FieldTable.cpp121
-rw-r--r--cpp/src/qpid/framing/FieldTable.h101
-rw-r--r--cpp/src/qpid/framing/FieldValue.cpp181
-rw-r--r--cpp/src/qpid/framing/FieldValue.h241
-rw-r--r--cpp/src/qpid/framing/FrameDecoder.cpp81
-rw-r--r--cpp/src/qpid/framing/FrameDecoder.h52
-rw-r--r--cpp/src/qpid/framing/FrameHandler.h2
-rw-r--r--cpp/src/qpid/framing/FrameSet.cpp9
-rw-r--r--cpp/src/qpid/framing/FrameSet.h29
-rw-r--r--cpp/src/qpid/framing/Handler.h2
-rw-r--r--cpp/src/qpid/framing/HeaderProperties.h6
-rw-r--r--cpp/src/qpid/framing/InitiationHandler.cpp2
-rw-r--r--cpp/src/qpid/framing/InitiationHandler.h2
-rw-r--r--cpp/src/qpid/framing/InputHandler.h2
-rw-r--r--cpp/src/qpid/framing/IsInSequenceSet.h51
-rw-r--r--cpp/src/qpid/framing/List.cpp83
-rw-r--r--cpp/src/qpid/framing/MethodBodyFactory.h (renamed from cpp/src/qpid/sys/Monitor.h)35
-rw-r--r--cpp/src/qpid/framing/MethodContent.h2
-rw-r--r--cpp/src/qpid/framing/ModelMethod.h4
-rw-r--r--cpp/src/qpid/framing/OutputHandler.h2
-rw-r--r--cpp/src/qpid/framing/ProtocolInitiation.cpp2
-rw-r--r--cpp/src/qpid/framing/ProtocolInitiation.h25
-rw-r--r--cpp/src/qpid/framing/ProtocolVersion.cpp2
-rw-r--r--cpp/src/qpid/framing/ProtocolVersion.h57
-rw-r--r--cpp/src/qpid/framing/Proxy.cpp15
-rw-r--r--cpp/src/qpid/framing/Proxy.h26
-rw-r--r--cpp/src/qpid/framing/SendContent.cpp13
-rw-r--r--cpp/src/qpid/framing/SendContent.h5
-rw-r--r--cpp/src/qpid/framing/SequenceNumber.cpp66
-rw-r--r--cpp/src/qpid/framing/SequenceNumber.h75
-rw-r--r--cpp/src/qpid/framing/SequenceNumberSet.cpp3
-rw-r--r--cpp/src/qpid/framing/SequenceNumberSet.h13
-rw-r--r--cpp/src/qpid/framing/SequenceSet.cpp7
-rw-r--r--cpp/src/qpid/framing/SequenceSet.h68
-rw-r--r--cpp/src/qpid/framing/StructHelper.h56
-rw-r--r--cpp/src/qpid/framing/TransferContent.cpp11
-rw-r--r--cpp/src/qpid/framing/TransferContent.h33
-rw-r--r--cpp/src/qpid/framing/Uuid.cpp37
-rw-r--r--cpp/src/qpid/framing/Uuid.h88
-rw-r--r--cpp/src/qpid/framing/amqp_framing.h24
-rw-r--r--cpp/src/qpid/framing/amqp_types.h72
-rw-r--r--cpp/src/qpid/framing/frame_functors.h4
-rw-r--r--cpp/src/qpid/log/Logger.cpp92
-rw-r--r--cpp/src/qpid/log/Logger.h117
-rw-r--r--cpp/src/qpid/log/Options.cpp148
-rw-r--r--cpp/src/qpid/log/Options.h56
-rw-r--r--cpp/src/qpid/log/OstreamOutput.cpp (renamed from cpp/src/qpid/framing/amqp_types_full.h)39
-rw-r--r--cpp/src/qpid/log/OstreamOutput.h41
-rw-r--r--cpp/src/qpid/log/Selector.cpp10
-rw-r--r--cpp/src/qpid/log/Selector.h70
-rw-r--r--cpp/src/qpid/log/Statement.cpp14
-rw-r--r--cpp/src/qpid/log/Statement.h128
-rw-r--r--cpp/src/qpid/log/posix/SinkOptions.cpp215
-rw-r--r--cpp/src/qpid/log/posix/SinkOptions.h64
-rw-r--r--cpp/src/qpid/log/windows/SinkOptions.cpp148
-rw-r--r--cpp/src/qpid/log/windows/SinkOptions.h54
-rw-r--r--cpp/src/qpid/management/Args.h44
-rw-r--r--cpp/src/qpid/management/IdAllocator.h (renamed from cpp/src/qpid/client/MessageQueue.h)32
-rw-r--r--cpp/src/qpid/management/Manageable.cpp13
-rw-r--r--cpp/src/qpid/management/Manageable.h68
-rw-r--r--cpp/src/qpid/management/ManagementAgent.cpp1295
-rw-r--r--cpp/src/qpid/management/ManagementAgent.h (renamed from cpp/src/qpid/management/ManagementBroker.h)170
-rw-r--r--cpp/src/qpid/management/ManagementBroker.cpp933
-rw-r--r--cpp/src/qpid/management/ManagementExchange.cpp18
-rw-r--r--cpp/src/qpid/management/ManagementExchange.h10
-rw-r--r--cpp/src/qpid/management/ManagementObject.cpp151
-rw-r--r--cpp/src/qpid/management/ManagementObject.h132
-rw-r--r--cpp/src/qpid/messaging/Address.cpp362
-rw-r--r--cpp/src/qpid/messaging/Connection.cpp96
-rw-r--r--cpp/src/qpid/messaging/ConnectionImpl.h46
-rw-r--r--cpp/src/qpid/messaging/ListContent.cpp98
-rw-r--r--cpp/src/qpid/messaging/ListView.cpp63
-rw-r--r--cpp/src/qpid/messaging/MapContent.cpp88
-rw-r--r--cpp/src/qpid/messaging/MapView.cpp63
-rw-r--r--cpp/src/qpid/messaging/Message.cpp58
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.cpp64
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.h82
-rw-r--r--cpp/src/qpid/messaging/Receiver.cpp53
-rw-r--r--cpp/src/qpid/messaging/ReceiverImpl.h55
-rw-r--r--cpp/src/qpid/messaging/Sender.cpp50
-rw-r--r--cpp/src/qpid/messaging/SenderImpl.h (renamed from cpp/src/qpid/client/MessageListener.h)53
-rw-r--r--cpp/src/qpid/messaging/Session.cpp109
-rw-r--r--cpp/src/qpid/messaging/SessionImpl.h64
-rw-r--r--cpp/src/qpid/messaging/Variant.cpp659
-rw-r--r--cpp/src/qpid/replication/ReplicatingEventListener.cpp201
-rw-r--r--cpp/src/qpid/replication/ReplicatingEventListener.h78
-rw-r--r--cpp/src/qpid/replication/ReplicationExchange.cpp232
-rw-r--r--cpp/src/qpid/replication/ReplicationExchange.h67
-rw-r--r--cpp/src/qpid/replication/constants.h34
-rw-r--r--cpp/src/qpid/shared_ptr.h51
-rw-r--r--cpp/src/qpid/store/CMakeLists.txt80
-rw-r--r--cpp/src/qpid/store/MessageStorePlugin.cpp447
-rw-r--r--cpp/src/qpid/store/MessageStorePlugin.h284
-rw-r--r--cpp/src/qpid/store/StorageProvider.h324
-rw-r--r--cpp/src/qpid/store/StoreException.h49
-rw-r--r--cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp89
-rw-r--r--cpp/src/qpid/store/ms-sql/AmqpTransaction.h84
-rw-r--r--cpp/src/qpid/store/ms-sql/BindingRecordset.cpp165
-rw-r--r--cpp/src/qpid/store/ms-sql/BindingRecordset.h88
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobAdapter.cpp64
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobAdapter.h62
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobEncoder.cpp133
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobEncoder.h61
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobRecordset.cpp86
-rw-r--r--cpp/src/qpid/store/ms-sql/BlobRecordset.h54
-rw-r--r--cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp70
-rw-r--r--cpp/src/qpid/store/ms-sql/DatabaseConnection.h62
-rw-r--r--cpp/src/qpid/store/ms-sql/Exception.h62
-rw-r--r--cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp1061
-rw-r--r--cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp155
-rw-r--r--cpp/src/qpid/store/ms-sql/MessageMapRecordset.h77
-rw-r--r--cpp/src/qpid/store/ms-sql/MessageRecordset.cpp184
-rw-r--r--cpp/src/qpid/store/ms-sql/MessageRecordset.h85
-rw-r--r--cpp/src/qpid/store/ms-sql/Recordset.cpp121
-rw-r--r--cpp/src/qpid/store/ms-sql/Recordset.h101
-rw-r--r--cpp/src/qpid/store/ms-sql/State.cpp45
-rw-r--r--cpp/src/qpid/store/ms-sql/State.h (renamed from cpp/src/qpid/client/AckMode.h)47
-rw-r--r--cpp/src/qpid/store/ms-sql/VariantHelper.cpp59
-rw-r--r--cpp/src/qpid/store/ms-sql/VariantHelper.h61
-rw-r--r--cpp/src/qpid/sys/AggregateOutput.cpp83
-rw-r--r--cpp/src/qpid/sys/AggregateOutput.h77
-rw-r--r--cpp/src/qpid/sys/AsynchIO.h144
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.cpp90
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.h43
-rw-r--r--cpp/src/qpid/sys/AtomicCount.h2
-rw-r--r--cpp/src/qpid/sys/AtomicValue_gcc.h2
-rw-r--r--cpp/src/qpid/sys/AtomicValue_mutex.h2
-rw-r--r--cpp/src/qpid/sys/BlockingQueue.h13
-rw-r--r--cpp/src/qpid/sys/Codec.h52
-rw-r--r--cpp/src/qpid/sys/ConnectionCodec.h41
-rw-r--r--cpp/src/qpid/sys/ConnectionInputHandler.h5
-rw-r--r--cpp/src/qpid/sys/ConnectionInputHandlerFactory.h3
-rw-r--r--cpp/src/qpid/sys/ConnectionOutputHandler.h3
-rw-r--r--cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h56
-rw-r--r--cpp/src/qpid/sys/CopyOnWriteArray.h138
-rw-r--r--cpp/src/qpid/sys/DeletionManager.h36
-rw-r--r--cpp/src/qpid/sys/DispatchHandle.cpp337
-rw-r--r--cpp/src/qpid/sys/DispatchHandle.h150
-rw-r--r--cpp/src/qpid/sys/Dispatcher.cpp403
-rw-r--r--cpp/src/qpid/sys/Dispatcher.h130
-rw-r--r--cpp/src/qpid/sys/ExceptionHolder.h75
-rwxr-xr-xcpp/src/qpid/sys/FileSysDir.h62
-rw-r--r--cpp/src/qpid/sys/LockFile.h39
-rw-r--r--cpp/src/qpid/sys/LockPtr.h89
-rw-r--r--cpp/src/qpid/sys/Mutex.h89
-rw-r--r--cpp/src/qpid/sys/OutputControl.h11
-rwxr-xr-xcpp/src/qpid/sys/PipeHandle.h51
-rw-r--r--cpp/src/qpid/sys/PollableCondition.h (renamed from cpp/src/qpid/cluster/PollableCondition.h)54
-rw-r--r--cpp/src/qpid/sys/PollableQueue.h176
-rw-r--r--cpp/src/qpid/sys/Poller.h52
-rw-r--r--cpp/src/qpid/sys/ProtocolFactory.h5
-rw-r--r--cpp/src/qpid/sys/RdmaIOPlugin.cpp353
-rw-r--r--cpp/src/qpid/sys/Runnable.cpp2
-rw-r--r--cpp/src/qpid/sys/SecurityLayer.h (renamed from cpp/src/qpid/sys/SystemInfo.h)26
-rw-r--r--cpp/src/qpid/sys/Semaphore.h16
-rw-r--r--cpp/src/qpid/sys/Shlib.cpp2
-rw-r--r--cpp/src/qpid/sys/Shlib.h12
-rw-r--r--cpp/src/qpid/sys/Socket.h46
-rw-r--r--cpp/src/qpid/sys/SocketAddress.h53
-rw-r--r--cpp/src/qpid/sys/SslPlugin.cpp184
-rw-r--r--cpp/src/qpid/sys/TCPIOPlugin.cpp56
-rw-r--r--cpp/src/qpid/sys/Time.h154
-rw-r--r--cpp/src/qpid/sys/Timer.cpp179
-rw-r--r--cpp/src/qpid/sys/Timer.h94
-rw-r--r--cpp/src/qpid/sys/alloca.h39
-rw-r--r--cpp/src/qpid/sys/apr/APRBase.cpp2
-rw-r--r--cpp/src/qpid/sys/apr/APRPool.cpp4
-rw-r--r--cpp/src/qpid/sys/apr/Condition.h2
-rw-r--r--cpp/src/qpid/sys/apr/Mutex.h4
-rw-r--r--cpp/src/qpid/sys/apr/Shlib.cpp4
-rw-r--r--cpp/src/qpid/sys/apr/Socket.cpp4
-rw-r--r--cpp/src/qpid/sys/apr/Thread.cpp2
-rw-r--r--cpp/src/qpid/sys/apr/Thread.h4
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp127
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h68
-rw-r--r--cpp/src/qpid/sys/epoll/EpollPoller.cpp444
-rw-r--r--cpp/src/qpid/sys/posix/AsynchIO.cpp253
-rw-r--r--cpp/src/qpid/sys/posix/Condition.h86
-rwxr-xr-xcpp/src/qpid/sys/posix/FileSysDir.cpp54
-rw-r--r--cpp/src/qpid/sys/posix/Fork.cpp2
-rw-r--r--cpp/src/qpid/sys/posix/IOHandle.cpp4
-rwxr-xr-xcpp/src/qpid/sys/posix/LockFile.cpp108
-rw-r--r--cpp/src/qpid/sys/posix/LockFile.h58
-rw-r--r--cpp/src/qpid/sys/posix/Mutex.h158
-rw-r--r--cpp/src/qpid/sys/posix/PidFile.h62
-rwxr-xr-xcpp/src/qpid/sys/posix/PipeHandle.cpp64
-rw-r--r--cpp/src/qpid/sys/posix/PollableCondition.cpp124
-rw-r--r--cpp/src/qpid/sys/posix/Shlib.cpp6
-rw-r--r--cpp/src/qpid/sys/posix/Socket.cpp127
-rw-r--r--cpp/src/qpid/sys/posix/SocketAddress.cpp97
-rwxr-xr-xcpp/src/qpid/sys/posix/SystemInfo.cpp137
-rw-r--r--cpp/src/qpid/sys/posix/Thread.cpp2
-rw-r--r--cpp/src/qpid/sys/posix/Time.cpp69
-rw-r--r--cpp/src/qpid/sys/posix/check.h49
-rw-r--r--cpp/src/qpid/sys/rdma/RdmaClient.cpp106
-rw-r--r--cpp/src/qpid/sys/rdma/RdmaIO.cpp516
-rw-r--r--cpp/src/qpid/sys/rdma/RdmaIO.h173
-rw-r--r--cpp/src/qpid/sys/rdma/RdmaServer.cpp86
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_exception.h20
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_factories.cpp27
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_factories.h27
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.cpp185
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.h207
-rw-r--r--cpp/src/qpid/sys/solaris/ECFPoller.cpp244
-rwxr-xr-xcpp/src/qpid/sys/solaris/SystemInfo.cpp123
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.cpp187
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.h76
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.cpp439
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.h171
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.cpp297
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.h119
-rw-r--r--cpp/src/qpid/sys/ssl/check.cpp72
-rw-r--r--cpp/src/qpid/sys/ssl/check.h53
-rw-r--r--cpp/src/qpid/sys/ssl/util.cpp118
-rw-r--r--cpp/src/qpid/sys/ssl/util.h50
-rw-r--r--cpp/src/qpid/sys/uuid.h (renamed from cpp/src/qpid/broker/BrokerSingleton.cpp)26
-rw-r--r--cpp/src/qpid/sys/windows/AsynchIO.cpp751
-rwxr-xr-xcpp/src/qpid/sys/windows/AsynchIoResult.h204
-rw-r--r--cpp/src/qpid/sys/windows/FileSysDir.cpp53
-rwxr-xr-x[-rw-r--r--]cpp/src/qpid/sys/windows/IOHandle.cpp (renamed from cpp/src/qpid/sys/SystemInfo.cpp)27
-rwxr-xr-xcpp/src/qpid/sys/windows/IoHandlePrivate.h61
-rwxr-xr-xcpp/src/qpid/sys/windows/IocpPoller.cpp214
-rwxr-xr-xcpp/src/qpid/sys/windows/LockFile.cpp64
-rwxr-xr-xcpp/src/qpid/sys/windows/PipeHandle.cpp101
-rw-r--r--cpp/src/qpid/sys/windows/PollableCondition.cpp116
-rw-r--r--cpp/src/qpid/sys/windows/Shlib.cpp53
-rwxr-xr-xcpp/src/qpid/sys/windows/Socket.cpp343
-rw-r--r--cpp/src/qpid/sys/windows/SocketAddress.cpp70
-rwxr-xr-xcpp/src/qpid/sys/windows/StrError.cpp47
-rwxr-xr-xcpp/src/qpid/sys/windows/SystemInfo.cpp201
-rwxr-xr-xcpp/src/qpid/sys/windows/Thread.cpp88
-rw-r--r--cpp/src/qpid/sys/windows/Time.cpp103
-rw-r--r--cpp/src/qpid/sys/windows/uuid.cpp59
-rw-r--r--cpp/src/qpid/sys/windows/uuid.h38
-rw-r--r--cpp/src/qpid/xml/XmlBinding.h (renamed from cpp/src/qpid/sys/StrError.h)22
-rw-r--r--cpp/src/qpid/xml/XmlExchange.cpp262
-rw-r--r--cpp/src/qpid/xml/XmlExchange.h (renamed from cpp/src/qpid/broker/XmlExchange.h)17
-rw-r--r--cpp/src/qpid/xml/XmlExchangePlugin.cpp69
624 files changed, 47441 insertions, 14603 deletions
diff --git a/cpp/src/qpid/Address.cpp b/cpp/src/qpid/Address.cpp
new file mode 100644
index 0000000000..c3f6829fd3
--- /dev/null
+++ b/cpp/src/qpid/Address.cpp
@@ -0,0 +1,58 @@
+/*
+ *
+ * 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 <ostream>
+
+using namespace std;
+
+namespace qpid {
+
+TcpAddress::TcpAddress(const std::string& h, uint16_t p): host(h), port(p) {}
+
+struct AddressOstreamVisitor : public boost::static_visitor<ostream&> {
+ ostream& out;
+ AddressOstreamVisitor(ostream& o) : out(o) {}
+ template <class T> ostream& operator()(const T& data) { return out << data; }
+};
+
+ostream& operator<<(ostream& os, const Address& addr) {
+ AddressOstreamVisitor visitor(os);
+ return boost::apply_visitor(visitor, addr.value);
+}
+
+bool operator==(const TcpAddress& x, const TcpAddress& y) {
+ return y.host==x.host && y.port == x.port;
+}
+
+ostream& operator<<(ostream& os, const TcpAddress& a) {
+ return os << "tcp:" << a.host << ":" << a.port;
+}
+
+ExampleAddress::ExampleAddress(const char c) : data(c) {}
+
+bool operator==(const ExampleAddress& x, const ExampleAddress& y) {
+ return x.data == y.data;
+}
+
+ostream& operator<<(ostream& os, const ExampleAddress& ex) {
+ return os << "example:" << ex.data;
+}
+
+} // namespace qpid
diff --git a/cpp/src/qpid/DataDir.cpp b/cpp/src/qpid/DataDir.cpp
index 18b52b9b8f..ad732052ab 100644
--- a/cpp/src/qpid/DataDir.cpp
+++ b/cpp/src/qpid/DataDir.cpp
@@ -18,15 +18,11 @@
*
*/
-#include "Exception.h"
-#include "DataDir.h"
+#include "qpid/Exception.h"
+#include "qpid/DataDir.h"
#include "qpid/log/Statement.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <fcntl.h>
-#include <cerrno>
-#include <unistd.h>
+#include "qpid/sys/FileSysDir.h"
+#include "qpid/sys/LockFile.h"
namespace qpid {
@@ -40,16 +36,9 @@ DataDir::DataDir (std::string path) :
return;
}
- const char *cpath = dirPath.c_str ();
- struct stat s;
- if (::stat(cpath, &s)) {
- if (errno == ENOENT) {
- if (::mkdir(cpath, 0755))
- throw Exception ("Can't create data directory: " + path);
- }
- else
- throw Exception ("Data directory not found: " + path);
- }
+ 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));
diff --git a/cpp/src/qpid/DataDir.h b/cpp/src/qpid/DataDir.h
index 7de5ebf62d..828299f3ba 100644
--- a/cpp/src/qpid/DataDir.h
+++ b/cpp/src/qpid/DataDir.h
@@ -23,10 +23,14 @@
#include <string>
#include <memory>
-#include "qpid/sys/LockFile.h"
+#include "qpid/CommonImportExport.h"
namespace qpid {
+ namespace sys {
+ class LockFile;
+ }
+
/**
* DataDir class.
*/
@@ -38,11 +42,11 @@ class DataDir
public:
- DataDir (std::string path);
- ~DataDir ();
+ QPID_COMMON_EXTERN DataDir (std::string path);
+ QPID_COMMON_EXTERN ~DataDir ();
- bool isEnabled () { return enabled; }
- std::string getPath () { return dirPath; }
+ bool isEnabled() { return enabled; }
+ const std::string& getPath() { return dirPath; }
};
} // namespace qpid
diff --git a/cpp/src/qpid/Exception.cpp b/cpp/src/qpid/Exception.cpp
index 9e884efec0..16a3a13d17 100644
--- a/cpp/src/qpid/Exception.cpp
+++ b/cpp/src/qpid/Exception.cpp
@@ -20,7 +20,7 @@
*/
#include "qpid/log/Statement.h"
-#include "Exception.h"
+#include "qpid/Exception.h"
#include <typeinfo>
#include <assert.h>
#include <string.h>
@@ -28,7 +28,7 @@
namespace qpid {
Exception::Exception(const std::string& msg) throw() : message(msg) {
- QPID_LOG(debug, "Exception constructed: " << message);
+ QPID_LOG_IF(debug, !msg.empty(), "Exception constructed: " << message);
}
Exception::~Exception() throw() {}
diff --git a/cpp/src/qpid/Exception.h b/cpp/src/qpid/Exception.h
deleted file mode 100644
index 9cf564104d..0000000000
--- a/cpp/src/qpid/Exception.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef _Exception_
-#define _Exception_
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/constants.h"
-#include "qpid/sys/StrError.h"
-#include "qpid/Msg.h"
-
-#include <memory>
-#include <string>
-#include <errno.h>
-
-namespace qpid
-{
-
-/**
- * Base class for Qpid runtime exceptions.
- */
-class Exception : public std::exception
-{
- public:
- explicit Exception(const std::string& message=std::string()) throw();
- virtual ~Exception() throw();
- virtual const char* what() const throw(); // prefix: message
- virtual std::string getMessage() const; // Unprefixed message
- virtual std::string getPrefix() const; // Prefix
-
- private:
- std::string message;
- mutable std::string whatStr;
-};
-
-/** Exception that includes an errno message. */
-struct ErrnoException : public Exception {
- ErrnoException(const std::string& msg, int err) : Exception(msg+": "+qpid::sys::strError(err)) {}
- ErrnoException(const std::string& msg) : Exception(msg+": "+qpid::sys::strError(errno)) {}
-};
-
-struct SessionException : public Exception {
- const framing::ReplyCode code;
- SessionException(framing::ReplyCode code_, const std::string& message)
- : Exception(message), code(code_) {}
-};
-
-struct ChannelException : public Exception {
- const framing::ReplyCode code;
- ChannelException(framing::ReplyCode _code, const std::string& message)
- : Exception(message), code(_code) {}
-};
-
-struct ConnectionException : public Exception {
- const framing::ReplyCode code;
- ConnectionException(framing::ReplyCode _code, const std::string& message)
- : Exception(message), code(_code) {}
-};
-
-struct ClosedException : public Exception {
- ClosedException(const std::string& msg=std::string());
- std::string getPrefix() const;
-};
-
-} // namespace qpid
-
-#endif /*!_Exception_*/
diff --git a/cpp/src/qpid/InlineAllocator.h b/cpp/src/qpid/InlineAllocator.h
deleted file mode 100644
index bf2f570599..0000000000
--- a/cpp/src/qpid/InlineAllocator.h
+++ /dev/null
@@ -1,84 +0,0 @@
-#ifndef QPID_INLINEALLOCATOR_H
-#define QPID_INLINEALLOCATOR_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 <assert.h>
-#include <boost/type_traits/type_with_alignment.hpp>
-#include <boost/type_traits/alignment_of.hpp>
-
-namespace qpid {
-
-/**
- * An allocator that has inline storage for up to Max objects
- * of type BaseAllocator::value_type.
- */
-template <class BaseAllocator, size_t Max>
-class InlineAllocator : public BaseAllocator {
- public:
- typedef typename BaseAllocator::pointer pointer;
- typedef typename BaseAllocator::size_type size_type;
- typedef typename BaseAllocator::value_type value_type;
-
- InlineAllocator() : allocated(false) {}
- InlineAllocator(const InlineAllocator& x) : BaseAllocator(x), allocated(false) {}
-
- pointer allocate(size_type n) {
- if (n <= Max && !allocated) {
- allocated=true;
- return reinterpret_cast<value_type*>(address());
- }
- else
- return BaseAllocator::allocate(n, 0);
- }
-
- void deallocate(pointer p, size_type n) {
- if (p == address()) {
- assert(allocated);
- allocated=false;
- }
- else
- BaseAllocator::deallocate(p, n);
- }
-
- template<typename T1>
- struct rebind {
- typedef typename BaseAllocator::template rebind<T1>::other BaseOther;
- typedef InlineAllocator<BaseOther, Max> other;
- };
-
- private:
- // POD object with alignment and size to hold Max value_types.
- static const size_t ALIGNMENT=boost::alignment_of<value_type>::value;
- typedef typename boost::type_with_alignment<ALIGNMENT>::type Aligner;
- union Store {
- Aligner aligner_;
- char sizer_[sizeof(value_type)*Max];
- } store;
- value_type* address() { return reinterpret_cast<value_type*>(&store); }
- bool allocated;
-};
-
-} // namespace qpid
-
-#endif /*!QPID_INLINEALLOCATOR_H*/
diff --git a/cpp/src/qpid/InlineVector.h b/cpp/src/qpid/InlineVector.h
deleted file mode 100644
index 551b9912e7..0000000000
--- a/cpp/src/qpid/InlineVector.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef QPID_INLINEVECTOR_H
-#define QPID_INLINEVECTOR_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 "InlineAllocator.h"
-#include <vector>
-
-namespace qpid {
-
-/**
- * A vector that stores up to Max elements in inline storage,
- * otherwise uses normal vector allocation.
- *
- * NOTE: depends on some non-standard but highly probably assumptions
- * about how std::vector uses its allocator, they are true for g++.
- * - default constructor does not allocate.
- * - reserve(N) does not allocate more than N elements.
- * - vector never re-allocates when size() < capacity()
- */
-template <class T, size_t Max, class Alloc=std::allocator<T> >
-class InlineVector : public std::vector<T, InlineAllocator<Alloc, Max> >
-{
- typedef std::vector<T, InlineAllocator<Alloc, Max> > Base;
- public:
- typedef typename Base::allocator_type allocator_type;
- typedef typename Base::value_type value_type;
- typedef typename Base::size_type size_type;
-
- explicit InlineVector(const allocator_type& a=allocator_type()) : Base(a) {
- this->reserve(Max);
- }
-
- explicit InlineVector(size_type n, const value_type& x = value_type(),
- const allocator_type& a=allocator_type()) : Base(a)
- {
- this->reserve(std::max(n, Max));
- this->insert(this->end(), n, x);
- }
-
- InlineVector(const InlineVector& x) : Base() {
- this->reserve(std::max(x.size(), Max));
- *this = x;
- }
-};
-
-} // namespace qpid
-
-#endif /*!QPID_INLINEVECTOR_H*/
diff --git a/cpp/src/qpid/Modules.cpp b/cpp/src/qpid/Modules.cpp
new file mode 100644
index 0000000000..70549bd553
--- /dev/null
+++ b/cpp/src/qpid/Modules.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 "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
+#if defined (QPID_MODULE_SUFFIX)
+ std::string suffix(QPID_MODULE_SUFFIX);
+#else
+ std::string suffix(".so");
+#endif
+
+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);
+ QPID_LOG (info, "Loaded Module: " << 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/cpp/src/qpid/Modules.h b/cpp/src/qpid/Modules.h
new file mode 100644
index 0000000000..159dd156c1
--- /dev/null
+++ b/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/cpp/src/qpid/Msg.h b/cpp/src/qpid/Msg.h
deleted file mode 100644
index 7214db611f..0000000000
--- a/cpp/src/qpid/Msg.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef QPID_MSG_H
-#define QPID_MSG_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 <sstream>
-#include <iostream>
-
-namespace qpid {
-
-/** A simple wrapper for std::ostringstream that allows
- * in place construction of a message and automatic conversion
- * to string.
- * E.g.
- *@code
- * void foo(const std::string&);
- * foo(Msg() << "hello " << 32);
- *@endcode
- * Will construct the string "hello 32" and pass it to foo()
- */
-struct Msg {
- std::ostringstream os;
- Msg() {}
- Msg(const Msg& m) : os(m.str()) {}
- std::string str() const { return os.str(); }
- operator std::string() const { return str(); }
-};
-
-template <class T> const Msg& operator<<(const Msg& m, const T& t) {
- const_cast<std::ostringstream&>(m.os)<<t; return m;
-}
-
-inline std::ostream& operator<<(std::ostream& o, const Msg& m) {
- return o<<m.str();
-}
-
-/** Construct a message using operator << and append (file:line) */
-#define QPID_MSG(message) ::qpid::Msg() << message << " (" << __FILE__ << ":" << __LINE__ << ")"
-
-} // namespace qpid
-
-#endif /*!QPID_MSG_H*/
diff --git a/cpp/src/qpid/Options.cpp b/cpp/src/qpid/Options.cpp
index e521b1220a..499fb71bc3 100644
--- a/cpp/src/qpid/Options.cpp
+++ b/cpp/src/qpid/Options.cpp
@@ -16,7 +16,7 @@
*
*/
-#include "Options.h"
+#include "qpid/Options.h"
#include "qpid/Exception.h"
#include <boost/bind.hpp>
@@ -55,7 +55,8 @@ struct EnvOptMapper {
}
static bool matchStr(const string& env, boost::shared_ptr<po::option_description> desc) {
- return std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar);
+ 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) {
diff --git a/cpp/src/qpid/Options.h b/cpp/src/qpid/Options.h
deleted file mode 100644
index cb86d27241..0000000000
--- a/cpp/src/qpid/Options.h
+++ /dev/null
@@ -1,257 +0,0 @@
-#ifndef QPID_COMMONOPTIONS_H
-#define QPID_COMMONOPTIONS_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 <boost/program_options.hpp>
-#include <boost/format.hpp>
-#include <sstream>
-#include <iterator>
-#include <algorithm>
-#include <string>
-
-
-namespace qpid {
-namespace po=boost::program_options;
-
-
-
-///@internal
-std::string prettyArg(const std::string&, const std::string&);
-
-/** @internal Normally only constructed by optValue() */
-template <class T>
-class OptionValue : public po::typed_value<T> {
- public:
- OptionValue(T& value, const std::string& arg)
- : po::typed_value<T>(&value), argName(arg) {}
- std::string name() const { return argName; }
-
- private:
- std::string argName;
-};
-
-
-/** Create an option value.
- * name, value appear after the option name in help like this:
- * <name> (=<value>)
- * T must support operator <<.
- *@see Options for example of use.
- */
-template<class T>
-po::value_semantic* optValue(T& value, const char* name) {
- std::string valstr(boost::lexical_cast<std::string>(value));
- return new OptionValue<T>(value, prettyArg(name, valstr));
-}
-
-/** Create a vector value. Multiple occurences of the option are
- * accumulated into the vector
- */
-template <class T>
-po::value_semantic* optValue(std::vector<T>& value, const char* name) {
- using namespace std;
- ostringstream os;
- copy(value.begin(), value.end(), ostream_iterator<T>(os, " "));
- string val=os.str();
- if (!val.empty())
- val.erase(val.end()-1); // Remove trailing " "
- return (new OptionValue<vector<T> >(value, prettyArg(name, val)));
-}
-
-/** Create a boolean switch value. Presence of the option sets the value. */
-inline po::value_semantic* optValue(bool& value) { return po::bool_switch(&value); }
-
-/**
- * Base class for options.
- * Example of use:
- @code
- struct MySubOptions : public Options {
- int x;
- string y;
- MySubOptions() : Options("Sub options") {
- addOptions()
- ("x", optValue(x,"XUNIT"), "Option X")
- ("y", optValue(y, "YUNIT"), "Option Y");
- }
- };
-
- struct MyOptions : public Options {
- bool z;
- vector<string> foo;
- MySubOptions subOptions;
- MyOptions() : Options("My Options") {
- addOptions()
- ("z", boolSwitch(z), "Option Z")
- ("foo", optValue(foo), "Multiple option foo");
- add(subOptions);
- }
-
- main(int argc, char** argv) {
- Options opts;
- opts.parse(argc, char** argv);
- // Use values
- dosomething(opts.subOptions.x);
- if (error)
- cout << opts << end; // Help message.
- }
-
- @endcode
- */
-
-
-
-
-/*
- * ---------------------------------------------
- * Explanation for Boost 103200 conditional code
- * ---------------------------------------------
- *
- * This boost version has an implementation of the program_options library
- * that has no provision for allowing unregistered options to pass by.
- *
- * But that means that, if you have a program that loads optional modules
- * after start-up, and those modules each have their own set of options,
- * then if you parse the command line too soon, you will get spurious
- * reports of unrecognized options -- and the program will exit!
- *
- * And we must process the command-line before module-loading, because we
- * need to look at the "bootstrap" options.
- *
- * This conditional code:
- *
- * 1. implements it's own functor class, derived from the Boost
- * "options_description_easy_init" class. This functor is used
- * to process added options and do the functor chaining, so that
- * I can snoop on the arguments before doing an explicit call
- * to its parent.
- *
- * 2. It implements two static vectors, one to hold long names, and
- * one for short names, so that options declared by modules are
- * not forgotten when their options_description goes out of scope.
- *
- * I will be thrilled to personally delete this code if we ever decide
- * that qpid doesn't really need to support this antique version of Boost.
- *
- */
-
-#if ( BOOST_VERSION == 103200 )
-struct Options;
-
-
-struct
-options_description_less_easy_init
- : public po::options_description_easy_init
-{
- options_description_less_easy_init ( Options * my_owner,
- po::options_description * my_parents_owner
- )
- : po::options_description_easy_init(my_parents_owner)
- {
- owner = my_owner;
- }
-
-
- options_description_less_easy_init&
- operator()(char const * name,
- char const * description);
-
-
- options_description_less_easy_init&
- operator()(char const * name,
- const po::value_semantic* s);
-
-
- options_description_less_easy_init&
- operator()(const char* name,
- const po::value_semantic* s,
- const char* description);
-
-
- Options * owner;
-};
-#endif
-
-
-
-
-
-
-struct Options : public po::options_description {
-
- struct Exception : public qpid::Exception {
- Exception(const std::string& msg) : qpid::Exception(msg) {}
- };
-
- Options(const std::string& name=std::string());
-
- /**
- * Parses options from argc/argv, environment variables and config file.
- * Note the filename argument can reference an options variable that
- * is updated by argc/argv or environment variable parsing.
- */
- void parse(int argc, char const* const* argv,
- const std::string& configfile=std::string(),
- bool allowUnknown = false);
-
-
- #if ( BOOST_VERSION == 103200 )
- options_description_less_easy_init m_less_easy;
-
- options_description_less_easy_init addOptions() {
- return m_less_easy;
- }
-
- bool
- is_registered_option ( std::string s );
-
- void
- register_names ( std::string s );
-
- static std::vector<std::string> long_names;
- static std::vector<std::string> short_names;
- #else
- boost::program_options::options_description_easy_init addOptions() {
- return add_options();
- }
- #endif
-};
-
-
-
-/**
- * Standard options for configuration
- */
-struct CommonOptions : public Options {
- CommonOptions(const std::string& name=std::string(),
- const std::string& configfile=std::string());
- bool help;
- bool version;
- std::string config;
-};
-
-
-
-
-} // namespace qpid
-
-#endif /*!QPID_COMMONOPTIONS_H*/
diff --git a/cpp/src/qpid/Plugin.cpp b/cpp/src/qpid/Plugin.cpp
index e4b76db28a..196b5c2333 100644
--- a/cpp/src/qpid/Plugin.cpp
+++ b/cpp/src/qpid/Plugin.cpp
@@ -18,7 +18,7 @@
*
*/
-#include "Plugin.h"
+#include "qpid/Plugin.h"
#include "qpid/Options.h"
#include <boost/bind.hpp>
#include <algorithm>
@@ -42,7 +42,7 @@ void invoke(boost::function<void()> f) { f(); }
Plugin::Target::~Target() { finalize(); }
void Plugin::Target::finalize() {
- for_each(finalizers.begin(), finalizers.end(), invoke);
+ std::for_each(finalizers.begin(), finalizers.end(), invoke);
finalizers.clear();
}
@@ -50,9 +50,16 @@ 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() {}
@@ -74,7 +81,14 @@ void Plugin::addOptions(Options& opts) {
}
}
-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))); }
+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/cpp/src/qpid/Plugin.h b/cpp/src/qpid/Plugin.h
index 3c7c8031bb..4e057872b9 100644
--- a/cpp/src/qpid/Plugin.h
+++ b/cpp/src/qpid/Plugin.h
@@ -24,11 +24,12 @@
#include <boost/noncopyable.hpp>
#include <boost/function.hpp>
#include <vector>
+#include "qpid/CommonImportExport.h"
/**@file Generic plug-in framework. */
namespace qpid {
-class Options;
+struct Options;
/**
* Plug-in base class.
@@ -36,6 +37,8 @@ class Options;
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.
@@ -46,13 +49,13 @@ class Plugin : private boost::noncopyable {
{
public:
/** Calls finalize() if not already called. */
- virtual ~Target();
+ QPID_COMMON_EXTERN virtual ~Target();
/** Run all the finalizers */
- void finalize();
+ QPID_COMMON_EXTERN void finalize();
/** Add a function to run when finalize() is called */
- void addFinalizer(const boost::function<void()>&);
+ QPID_COMMON_EXTERN void addFinalizer(const boost::function<void()>&);
private:
std::vector<boost::function<void()> > finalizers;
@@ -65,9 +68,9 @@ class Plugin : private boost::noncopyable {
* member variable in a library so it is registered during
* initialization when the library is loaded.
*/
- Plugin();
+ QPID_COMMON_EXTERN Plugin();
- virtual ~Plugin();
+ QPID_COMMON_EXTERN virtual ~Plugin();
/**
* Configuration options for the plugin.
@@ -76,7 +79,7 @@ class Plugin : private boost::noncopyable {
* @return An options group or 0 for no options. Default returns 0.
* Plugin retains ownership of return value.
*/
- virtual Options* getOptions();
+ QPID_COMMON_EXTERN virtual Options* getOptions();
/**
* Initialize Plugin functionality on a Target, called before
@@ -98,19 +101,27 @@ class Plugin : private boost::noncopyable {
*/
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.
*/
- static const Plugins& getPlugins();
+ QPID_COMMON_EXTERN static const Plugins& getPlugins();
/** Call earlyInitialize() on all registered plugins */
- static void earlyInitAll(Target&);
+ QPID_COMMON_EXTERN static void earlyInitAll(Target&);
/** Call initialize() on all registered plugins */
- static void initializeAll(Target&);
+ QPID_COMMON_EXTERN static void initializeAll(Target&);
/** For each registered plugin, add plugin.getOptions() to opts. */
- static void addOptions(Options& opts);
+ QPID_COMMON_EXTERN static void addOptions(Options& opts);
};
} // namespace qpid
diff --git a/cpp/src/qpid/RangeSet.h b/cpp/src/qpid/RangeSet.h
deleted file mode 100644
index 2a88426f17..0000000000
--- a/cpp/src/qpid/RangeSet.h
+++ /dev/null
@@ -1,324 +0,0 @@
-#ifndef QPID_RANGESET_H
-#define QPID_RANGESET_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/InlineVector.h"
-#include <boost/iterator/iterator_facade.hpp>
-#include <boost/operators.hpp>
-#include <boost/bind.hpp>
-#include <algorithm>
-
-namespace qpid {
-
-/** A range of values, used in RangeSet.
- * Range(begin, end) includes begin but excludes end.
- * Range::makeClosed(first,last) includes both first and last.
- */
-template <class T>
-class Range {
- public:
- static Range makeClosed(const T& first, T last) { return Range(first, ++last); }
-
- Range() : begin_(), end_() {}
- explicit Range(const T& t) : begin_(t), end_(t) { ++end_; }
- Range(const T& b, const T& e) : begin_(b), end_(e) { assert(b <= e); }
-
- T begin() const { return begin_; }
- /** End of _open_ range, i.e. !contains(end()) */
- T end() const { return end_; }
-
- T first() const { assert(!empty()); return begin_; }
- /** Last in closed range, i.e. contains(end()) */
- T last() const { assert(!empty()); T ret=end_; return --ret; }
-
- void begin(const T& t) { begin_ = t; }
- void end(const T& t) { end_ = t; }
-
- bool empty() const { return begin_ == end_; }
-
- bool contains(const T& x) const { return begin_ <= x && x < end_; }
- bool contains(const Range& r) const { return begin_ <= r.begin_ && r.end_ <= end_; }
- bool strictContains(const Range& r) const { return begin_ < r.begin_ && r.end_ < end_; }
-
- bool operator==(const Range& x) { return begin_ == x.begin_ && end_== x.end_; }
-
- bool operator<(const T& t) const { return end_ < t; }
- bool operator<(const Range<T>& r) const { return end_ < r.begin_; }
-
- /** touching ranges can be merged into a single range. */
- bool touching(const Range& r) const {
- return std::max(begin_, r.begin_) <= std::min(end_, r.end_);
- }
-
- /** @pre touching */
- void merge(const Range& r) {
- assert(touching(r));
- begin_ = std::min(begin_, r.begin_);
- end_ = std::max(end_, r.end_);
- }
-
- operator bool() const { return !empty(); }
-
- template <class S> void serialize(S& s) { s(begin_)(end_); }
-
- private:
- T begin_, end_;
-};
-
-
-/**
- * A set implemented as a list of [begin, end) ranges.
- * T must be LessThanComparable and Incrementable.
- * RangeSet only provides const iterators.
- */
-template <class T>
-class RangeSet
- : boost::additive1<RangeSet<T>,
- boost::additive2<RangeSet<T>, Range<T>,
- boost::additive2<RangeSet<T>, T> > >
-{
- typedef InlineVector<Range<T>, 3> Ranges; // TODO aconway 2008-04-21: what's the optimial inlined value?
-
- public:
-
- class iterator : public boost::iterator_facade<
- iterator,
- const T,
- boost::forward_traversal_tag>
- {
- public:
- iterator() : ranges(), iter(), value() {}
-
- private:
- typedef typename Ranges::const_iterator RangesIter;
- iterator(const Ranges& r, const RangesIter& i, const T& t)
- : ranges(&r), iter(i), value(t) {}
-
- void increment();
- bool equal(const iterator& i) const;
- const T& dereference() const { return value; }
-
- const Ranges* ranges;
- RangesIter iter;
- T value;
-
- friend class RangeSet<T>;
- friend class boost::iterator_core_access;
- };
-
- typedef iterator const_iterator;
-
- RangeSet() {}
- explicit RangeSet(const Range<T>& r) { *this += r; }
- RangeSet(const T& a, const T& b) { *this += Range<T>(a,b); }
-
- bool contiguous() const { return ranges.size() <= 1; }
-
- bool contains(const T& t) const;
- bool contains(const Range<T>&) const;
-
- /**@pre contiguous() */
- Range<T> toRange() const;
-
- bool operator==(const RangeSet<T>&) const;
-
- void addRange (const Range<T>&);
- void addSet (const RangeSet<T>&);
-
- RangeSet<T>& operator+=(const T& t) { return *this += Range<T>(t); }
- RangeSet<T>& operator+=(const Range<T>& r) { addRange(r); return *this; }
- RangeSet<T>& operator+=(const RangeSet<T>& s) { addSet(s); return *this; }
-
- void removeRange (const Range<T>&);
- void removeSet (const RangeSet<T>&);
-
- RangeSet<T>& operator-=(const T& t) { return *this -= Range<T>(t); }
- RangeSet<T>& operator-=(const Range<T>& r) { removeRange(r); return *this; }
- RangeSet<T>& operator-=(const RangeSet<T>& s) { removeSet(s); return *this; }
-
- T front() const { return ranges.front().begin(); }
- T back() const { return ranges.back().end(); }
-
- // Iterate over elements in the set.
- iterator begin() const;
- iterator end() const;
-
- // Iterate over ranges in the set.
- typedef typename Ranges::const_iterator RangeIterator;
- RangeIterator rangesBegin() const { return ranges.begin(); }
- RangeIterator rangesEnd() const { return ranges.end(); }
- size_t rangesSize() const { return ranges.size(); }
-
- // The difference between the start and end of this range set
- uint32_t span() const;
-
- bool empty() const { return ranges.empty(); }
- void clear() { ranges.clear(); }
-
- /** Return the largest contiguous range containing x.
- * Returns the empty range [x,x) if x is not in the set.
- */
- Range<T> rangeContaining(const T&) const;
-
- template <class S> void serialize(S& s) { s.split(*this); s(ranges.begin(), ranges.end()); }
- template <class S> void encode(S& s) const { s(uint16_t(ranges.size()*sizeof(Range<T>))); }
- template <class S> void decode(S& s) { uint16_t sz; s(sz); ranges.resize(sz/sizeof(Range<T>)); }
-
- private:
- Ranges ranges;
-
- template <class U> friend std::ostream& operator<<(std::ostream& o, const RangeSet<U>& r);
-
- friend class iterator;
-};
-
-template <class T>
-std::ostream& operator<<(std::ostream& o, const Range<T>& r) {
- return o << "[" << r.begin() << "," << r.end() << ")";
-}
-
-template <class T>
-std::ostream& operator<<(std::ostream& o, const RangeSet<T>& rs) {
- std::ostream_iterator<Range<T> > i(o, " ");
- o << "{ ";
- std::copy(rs.ranges.begin(), rs.ranges.end(), i);
- return o << "}";
-}
-
-template <class T>
-bool RangeSet<T>::contains(const T& t) const {
- typename Ranges::const_iterator i =
- std::lower_bound(ranges.begin(), ranges.end(), Range<T>(t));
- return i != ranges.end() && i->contains(t);
-}
-
-template <class T>
-bool RangeSet<T>::contains(const Range<T>& r) const {
- typename Ranges::const_iterator i =
- std::lower_bound(ranges.begin(), ranges.end(), r);
- return i != ranges.end() && i->contains(r);
-}
-
-template <class T> void RangeSet<T>::addRange(const Range<T>& r) {
- if (r.empty()) return;
- typename Ranges::iterator i =
- std::lower_bound(ranges.begin(), ranges.end(), r);
- if (i == ranges.end() || !i->touching(r))
- ranges.insert(i, r);
- else {
- i->merge(r);
- typename Ranges::iterator j = i;
- if (++j != ranges.end() && i->touching(*j)) {
- i->merge(*j);
- ranges.erase(j);
- }
- }
-}
-
-
-template <class T> void RangeSet<T>::addSet(const RangeSet<T>& s) {
- typedef RangeSet<T>& (RangeSet<T>::*RangeSetRangeOp)(const Range<T>&);
- std::for_each(s.ranges.begin(), s.ranges.end(),
- boost::bind((RangeSetRangeOp)&RangeSet<T>::operator+=, this, _1));
-}
-
-template <class T> void RangeSet<T>::removeRange(const Range<T>& r) {
- if (r.empty()) return;
- typename Ranges::iterator i,j;
- i = std::lower_bound(ranges.begin(), ranges.end(), r);
- if (i == ranges.end() || i->begin() >= r.end())
- return; // Outside of set
- if (*i == r) // Erase i
- ranges.erase(i);
- else if (i->strictContains(r)) { // Split i
- Range<T> i1(i->begin(), r.begin());
- Range<T> i2(r.end(), i->end());
- *i = i2;
- ranges.insert(i, i1);
- } else {
- if (i->begin() < r.begin()) { // Truncate i
- i->end(r.begin());
- ++i;
- }
- for (j = i; j != ranges.end() && r.contains(*j); ++j)
- ; // Ranges to erase.
- if (j != ranges.end() && j->end() > r.end())
- j->begin(r.end()); // Truncate j
- ranges.erase(i,j);
- }
-}
-
-template <class T> void RangeSet<T>::removeSet(const RangeSet<T>& r) {
- std::for_each(
- r.ranges.begin(), r.ranges.end(),
- boost::bind(&RangeSet<T>::removeRange, this, _1));
-}
-
-template <class T> Range<T> RangeSet<T>::toRange() const {
- assert(contiguous());
- return empty() ? Range<T>() : ranges.front();
-}
-
-template <class T> void RangeSet<T>::iterator::increment() {
- assert(ranges && iter != ranges->end());
- if (!iter->contains(++value)) {
- ++iter;
- if (iter == ranges->end())
- *this=iterator(); // end() iterator
- else
- value=iter->begin();
- }
-}
-
-template <class T> bool RangeSet<T>::operator==(const RangeSet<T>& r) const {
- return ranges.size() == r.ranges.size() && std::equal(ranges.begin(), ranges.end(), r.ranges.begin());
-}
-
-template <class T> typename RangeSet<T>::iterator RangeSet<T>::begin() const {
- return empty() ? end() : iterator(ranges, ranges.begin(), front());
-}
-
-template <class T> typename RangeSet<T>::iterator RangeSet<T>::end() const {
- return iterator();
-}
-
-template <class T> bool RangeSet<T>::iterator::equal(const iterator& i) const {
- return ranges==i.ranges && (ranges==0 || value==i.value);
-}
-
-template <class T> Range<T> RangeSet<T>::rangeContaining(const T& t) const {
- typename Ranges::const_iterator i =
- std::lower_bound(ranges.begin(), ranges.end(), Range<T>(t));
- return (i != ranges.end() && i->contains(t)) ? *i : Range<T>(t,t);
-}
-
-template <class T> uint32_t RangeSet<T>::span() const {
- if (ranges.empty()) return 0;
- return ranges.back().last() - ranges.front().first();
-}
-
-
-} // namespace qpid
-
-
-#endif /*!QPID_RANGESET_H*/
diff --git a/cpp/src/qpid/RefCounted.h b/cpp/src/qpid/RefCounted.h
index 10b5e4afcc..e7a9bf31c1 100644
--- a/cpp/src/qpid/RefCounted.h
+++ b/cpp/src/qpid/RefCounted.h
@@ -39,11 +39,13 @@ class RefCounted : boost::noncopyable {
public:
RefCounted() : count(0) {}
void addRef() const { ++count; }
- void release() const { if (--count==0) delete this; }
+ 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; }
};
diff --git a/cpp/src/qpid/RefCountedBuffer.cpp b/cpp/src/qpid/RefCountedBuffer.cpp
new file mode 100644
index 0000000000..9b8f1ebd5e
--- /dev/null
+++ b/cpp/src/qpid/RefCountedBuffer.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/RefCountedBuffer.h"
+#include <new>
+
+namespace qpid {
+
+RefCountedBuffer::RefCountedBuffer() : count(0) {}
+
+void RefCountedBuffer::destroy() const {
+ this->~RefCountedBuffer();
+ ::delete[] reinterpret_cast<const char*>(this);
+}
+
+char* RefCountedBuffer::addr() const {
+ return const_cast<char*>(reinterpret_cast<const char*>(this)+sizeof(RefCountedBuffer));
+}
+
+RefCountedBuffer::pointer RefCountedBuffer::create(size_t n) {
+ char* store=::new char[n+sizeof(RefCountedBuffer)];
+ new(store) RefCountedBuffer;
+ return pointer(reinterpret_cast<RefCountedBuffer*>(store));
+}
+
+RefCountedBuffer::pointer::pointer() {}
+RefCountedBuffer::pointer::pointer(RefCountedBuffer* x) : p(x) {}
+RefCountedBuffer::pointer::pointer(const pointer& x) : p(x.p) {}
+RefCountedBuffer::pointer::~pointer() {}
+RefCountedBuffer::pointer& RefCountedBuffer::pointer::operator=(const RefCountedBuffer::pointer& x) { p = x.p; return *this; }
+
+char* RefCountedBuffer::pointer::cp() const { return p ? p->get() : 0; }
+} // namespace qpid
+
+
diff --git a/cpp/src/qpid/RefCountedBuffer.h b/cpp/src/qpid/RefCountedBuffer.h
new file mode 100644
index 0000000000..c332325378
--- /dev/null
+++ b/cpp/src/qpid/RefCountedBuffer.h
@@ -0,0 +1,89 @@
+#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 <boost/utility.hpp>
+#include <boost/detail/atomic_count.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+// FIXME aconway 2008-09-06: easy to add alignment
+/**
+ * Reference-counted byte buffer.
+ * No alignment guarantees.
+ */
+class RefCountedBuffer : boost::noncopyable {
+ mutable boost::detail::atomic_count count;
+ RefCountedBuffer();
+ void destroy() const;
+ char* addr() const;
+
+public:
+ /** Smart char pointer to a reference counted buffer */
+ class pointer {
+ boost::intrusive_ptr<RefCountedBuffer> p;
+ char* cp() const;
+ pointer(RefCountedBuffer* x);
+ friend class RefCountedBuffer;
+
+ public:
+ pointer();
+ pointer(const pointer&);
+ ~pointer();
+ pointer& operator=(const pointer&);
+
+ char* get() { return cp(); }
+ operator char*() { return cp(); }
+ char& operator*() { return *cp(); }
+ char& operator[](size_t i) { return cp()[i]; }
+
+ const char* get() const { return cp(); }
+ operator const char*() const { return cp(); }
+ const char& operator*() const { return *cp(); }
+ const char& operator[](size_t i) const { return cp()[i]; }
+ };
+
+ /** Create a reference counted buffer of size n */
+ static pointer create(size_t n);
+
+ /** Get a pointer to the start of the buffer. */
+ char* get() { return addr(); }
+ const char* get() const { return addr(); }
+ char& operator[](size_t i) { return get()[i]; }
+ const char& operator[](size_t i) const { return get()[i]; }
+
+ void addRef() const { ++count; }
+ void release() const { if (--count==0) destroy(); }
+ long refCount() { return count; }
+};
+
+} // namespace qpid
+
+// intrusive_ptr support.
+namespace boost {
+inline void intrusive_ptr_add_ref(const qpid::RefCountedBuffer* p) { p->addRef(); }
+inline void intrusive_ptr_release(const qpid::RefCountedBuffer* p) { p->release(); }
+}
+
+
+#endif /*!QPID_REFCOUNTEDBUFFER_H*/
diff --git a/cpp/src/qpid/SessionId.cpp b/cpp/src/qpid/SessionId.cpp
index fce6619f5d..c7e83f83d7 100644
--- a/cpp/src/qpid/SessionId.cpp
+++ b/cpp/src/qpid/SessionId.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "SessionId.h"
+#include "qpid/SessionId.h"
#include <sstream>
namespace qpid {
@@ -35,7 +35,7 @@ bool SessionId::operator==(const SessionId& id) const {
}
std::ostream& operator<<(std::ostream& o, const SessionId& id) {
- return o << id.getName() << "@" << id.getUserId();
+ return o << id.getUserId() << "." << id.getName();
}
std::string SessionId::str() const {
diff --git a/cpp/src/qpid/SessionId.h b/cpp/src/qpid/SessionId.h
deleted file mode 100644
index 291c42a2bb..0000000000
--- a/cpp/src/qpid/SessionId.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef QPID_SESSIONID_H
-#define QPID_SESSIONID_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/operators.hpp>
-#include <string>
-
-namespace qpid {
-
-/** Identifier for a session.
- * There are two parts to a session identifier:
- *
- * getUserId() returns the authentication principal associated with
- * the session's connection.
- *
- * getName() returns the session name.
- *
- * The name must be unique among sessions with the same authentication
- * principal.
- */
-class SessionId : boost::totally_ordered1<SessionId> {
- std::string userId;
- std::string name;
- public:
- SessionId(const std::string& userId=std::string(), const std::string& name=std::string());
- std::string getUserId() const { return userId; }
- std::string getName() const { return name; }
- bool operator<(const SessionId&) const ;
- bool operator==(const SessionId& id) const;
- // Convert to a string
- std::string str() const;
-};
-
-std::ostream& operator<<(std::ostream&, const SessionId&);
-
-
-} // namespace qpid
-
-#endif /*!QPID_SESSIONID_H*/
diff --git a/cpp/src/qpid/SessionState.cpp b/cpp/src/qpid/SessionState.cpp
index 1be0111489..4f370c6765 100644
--- a/cpp/src/qpid/SessionState.cpp
+++ b/cpp/src/qpid/SessionState.cpp
@@ -19,9 +19,10 @@
*
*/
-#include "SessionState.h"
+#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>
@@ -37,10 +38,10 @@ using framing::FramingErrorException;
namespace {
bool isControl(const AMQFrame& f) {
- return f.getMethod() && f.getMethod()->type() == framing::CONTROL;
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL;
}
bool isCommand(const AMQFrame& f) {
- return f.getMethod() && f.getMethod()->type() == framing::COMMAND;
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND;
}
} // namespace
@@ -60,7 +61,7 @@ void SessionPoint::advance(const AMQFrame& f) {
if (f.isLastSegment() && f.isLastFrame())
++command; // Single-frame command.
else
- offset += f.size();
+ offset += f.encodedSize();
}
else { // continuation frame for partial command
if (offset == 0)
@@ -76,7 +77,7 @@ void SessionPoint::advance(const AMQFrame& f) {
// that the relationship of fragment offsets to the replay
// list can be computed more easily.
//
- offset += f.size();
+ offset += f.encodedSize();
}
}
}
@@ -112,12 +113,13 @@ SessionState::ReplayRange SessionState::senderExpected(const SessionPoint& expec
void SessionState::senderRecord(const AMQFrame& f) {
if (isControl(f)) return; // Ignore control frames.
- QPID_LOG_IF(debug, f.getMethod(), getId() << ": sent cmd " << sender.sendPoint.command << ": " << *f.getMethod());
+ QPID_LOG(trace, getId() << ": sent cmd " << sender.sendPoint.command << ": " << *f.getBody());
+
stateful = true;
if (timeout) sender.replayList.push_back(f);
- sender.unflushedSize += f.size();
- sender.bytesSinceKnownCompleted += f.size();
- sender.replaySize += f.size();
+ 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)
@@ -146,15 +148,15 @@ void SessionState::senderRecordKnownCompleted() {
void SessionState::senderConfirmed(const SessionPoint& confirmed) {
if (confirmed > sender.sendPoint)
- throw InvalidArgumentException(QPID_MSG(getId() << "Confirmed commands not yet sent."));
+ 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->size();
+ sender.replaySize -= i->encodedSize();
if (sender.replayPoint > sender.flushPoint)
- sender.unflushedSize -= i->size();
+ sender.unflushedSize -= i->encodedSize();
++i;
}
if (sender.replayPoint > sender.flushPoint)
@@ -168,7 +170,7 @@ void SessionState::senderCompleted(const SequenceSet& commands) {
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 only the first contiguous range of commands.
+ // confirmation, so confirm up to the end of the first contiguous range of commands.
senderConfirmed(SessionPoint(commands.rangesBegin()->end()));
}
@@ -182,21 +184,23 @@ void SessionState::receiverSetCommandPoint(const SessionPoint& point) {
}
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.size();
+ receiver.bytesSinceKnownCompleted += f.encodedSize();
bool firstTime = receiver.expected > receiver.received;
if (firstTime) {
receiver.received = receiver.expected;
receiver.incomplete += receiverGetCurrent();
}
- QPID_LOG_IF(debug, f.getMethod(), getId() << ": recv cmd " << receiverGetCurrent() << ": " << *f.getMethod());
- QPID_LOG_IF(debug, !firstTime, "Ignoring duplicate frame: " << receiverGetCurrent() << ": " << f);
+ 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;
@@ -236,7 +240,7 @@ SessionState::Configuration::Configuration(size_t flush, size_t hard) :
replayFlushLimit(flush), replayHardLimit(hard) {}
SessionState::SessionState(const SessionId& i, const Configuration& c)
- : id(i), timeout(), config(c), stateful()
+ : id(i), timeout(), config(c), stateful(), receiverTrackingDisabled(false)
{
QPID_LOG(debug, "SessionState::SessionState " << id << ": " << this);
}
@@ -249,4 +253,32 @@ 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/cpp/src/qpid/SessionState.h b/cpp/src/qpid/SessionState.h
index 10937b7a1e..da28738546 100644
--- a/cpp/src/qpid/SessionState.h
+++ b/cpp/src/qpid/SessionState.h
@@ -31,6 +31,7 @@
#include <boost/range/iterator_range.hpp>
#include <vector>
#include <iosfwd>
+#include <qpid/CommonImportExport.h>
namespace qpid {
using framing::SequenceNumber;
@@ -38,19 +39,19 @@ using framing::SequenceSet;
/** A point in the session. Points to command id + offset */
struct SessionPoint : boost::totally_ordered1<SessionPoint> {
- SessionPoint(SequenceNumber command = 0, uint64_t offset = 0);
+ QPID_COMMON_EXTERN SessionPoint(SequenceNumber command = 0, uint64_t offset = 0);
SequenceNumber command;
uint64_t offset;
/** Advance past frame f */
- void advance(const framing::AMQFrame& f);
+ QPID_COMMON_EXTERN void advance(const framing::AMQFrame& f);
- bool operator<(const SessionPoint&) const;
- bool operator==(const SessionPoint&) const;
+ QPID_COMMON_EXTERN bool operator<(const SessionPoint&) const;
+ QPID_COMMON_EXTERN bool operator==(const SessionPoint&) const;
};
-std::ostream& operator<<(std::ostream&, const SessionPoint&);
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const SessionPoint&);
/**
* Support for session idempotence barrier and resume as defined in
@@ -78,14 +79,14 @@ class SessionState {
typedef boost::iterator_range<ReplayList::iterator> ReplayRange;
struct Configuration {
- Configuration(size_t flush=1024*1024, size_t hard=0);
+ 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.
};
- SessionState(const SessionId& =SessionId(), const Configuration& =Configuration());
+ QPID_COMMON_EXTERN SessionState(const SessionId& =SessionId(), const Configuration& =Configuration());
- virtual ~SessionState();
+ QPID_COMMON_EXTERN virtual ~SessionState();
bool hasState() const;
@@ -100,73 +101,100 @@ class SessionState {
// ==== Functions for sender state.
/** Record frame f for replay. Should not be called during replay. */
- virtual void senderRecord(const framing::AMQFrame& f);
+ QPID_COMMON_EXTERN virtual void senderRecord(const framing::AMQFrame& f);
/** @return true if we should send flush for confirmed and completed commands. */
- virtual bool senderNeedFlush() const;
+ QPID_COMMON_EXTERN virtual bool senderNeedFlush() const;
/** Called when flush for confirmed and completed commands is sent to peer. */
- virtual void senderRecordFlush();
+ QPID_COMMON_EXTERN virtual void senderRecordFlush();
/** True if we should reply to the next incoming completed command */
- virtual bool senderNeedKnownCompleted() const;
+ QPID_COMMON_EXTERN virtual bool senderNeedKnownCompleted() const;
/** Called when knownCompleted is sent to peer. */
- virtual void senderRecordKnownCompleted();
+ QPID_COMMON_EXTERN virtual void senderRecordKnownCompleted();
/** Called when the peer confirms up to comfirmed. */
- virtual void senderConfirmed(const SessionPoint& confirmed);
+ QPID_COMMON_EXTERN virtual void senderConfirmed(const SessionPoint& confirmed);
/** Called when the peer indicates commands completed */
- virtual void senderCompleted(const SequenceSet& commands);
+ QPID_COMMON_EXTERN virtual void senderCompleted(const SequenceSet& commands);
/** Point from which the next new (not replayed) data will be sent. */
- virtual SessionPoint senderGetCommandPoint();
+ QPID_COMMON_EXTERN virtual SessionPoint senderGetCommandPoint();
/** Set of outstanding incomplete commands */
- virtual SequenceSet senderGetIncomplete() const;
+ QPID_COMMON_EXTERN virtual SequenceSet senderGetIncomplete() const;
/** Point from which we can replay. */
- virtual SessionPoint senderGetReplayPoint() const;
+ QPID_COMMON_EXTERN virtual SessionPoint senderGetReplayPoint() const;
/** Peer expecting commands from this point.
- virtual *@return Range of frames to be replayed.
- */
- virtual ReplayRange senderExpected(const SessionPoint& expected);
+ *@return Range of frames to be replayed.
+ */
+ QPID_COMMON_EXTERN virtual ReplayRange senderExpected(const SessionPoint& expected);
// ==== Functions for receiver state
/** Set the command point. */
- virtual void receiverSetCommandPoint(const SessionPoint& point);
+ QPID_COMMON_EXTERN virtual void receiverSetCommandPoint(const SessionPoint& point);
/** Returns true if frame should be be processed, false if it is a duplicate. */
- virtual bool receiverRecord(const framing::AMQFrame& f);
+ QPID_COMMON_EXTERN virtual bool receiverRecord(const framing::AMQFrame& f);
/** Command completed locally */
- virtual void receiverCompleted(SequenceNumber command, bool cumulative=false);
+ QPID_COMMON_EXTERN virtual void receiverCompleted(SequenceNumber command, bool cumulative=false);
/** Peer has indicated commands are known completed */
- virtual void receiverKnownCompleted(const SequenceSet& commands);
+ 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.
*/
- virtual bool receiverNeedKnownCompleted() const;
+ QPID_COMMON_EXTERN virtual bool receiverNeedKnownCompleted() const;
/** Get the incoming command point */
- virtual const SessionPoint& receiverGetExpected() const;
+ QPID_COMMON_EXTERN virtual const SessionPoint& receiverGetExpected() const;
/** Get the received high-water-mark, may be > getExpected() during replay */
- virtual const SessionPoint& receiverGetReceived() const;
+ QPID_COMMON_EXTERN virtual const SessionPoint& receiverGetReceived() const;
/** Completed received commands that the peer may not know about. */
- virtual const SequenceSet& receiverGetUnknownComplete() const;
+ QPID_COMMON_EXTERN virtual const SequenceSet& receiverGetUnknownComplete() const;
/** Incomplete received commands. */
- virtual const SequenceSet& receiverGetIncomplete() const;
+ QPID_COMMON_EXTERN virtual const SequenceSet& receiverGetIncomplete() const;
/** ID of the command currently being handled. */
- virtual SequenceNumber receiverGetCurrent() const;
+ 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:
@@ -196,6 +224,7 @@ class SessionState {
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; }
diff --git a/cpp/src/qpid/StringUtils.cpp b/cpp/src/qpid/StringUtils.cpp
index 17eb141e12..c436441c56 100644
--- a/cpp/src/qpid/StringUtils.cpp
+++ b/cpp/src/qpid/StringUtils.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "StringUtils.h"
+#include "qpid/StringUtils.h"
namespace qpid {
diff --git a/cpp/src/qpid/StringUtils.h b/cpp/src/qpid/StringUtils.h
index 3120e43334..4130fae017 100644
--- a/cpp/src/qpid/StringUtils.h
+++ b/cpp/src/qpid/StringUtils.h
@@ -22,6 +22,8 @@
*
*/
+#include "qpid/CommonImportExport.h"
+
#include <string>
#include <vector>
@@ -31,12 +33,12 @@ namespace qpid {
* Split 'in' into words using delimiters in 'delims' and put
* resulting strings into 'out' vector.
*/
-void split(std::vector<std::string>& out, const std::string& in, const std::string& delims);
+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.
*/
-std::vector<std::string> split(const std::string& in, const std::string& delims);
+QPID_COMMON_EXTERN std::vector<std::string> split(const std::string& in, const std::string& delims);
} // namespace qpid
diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp
index 95d6a34136..f831167dd8 100644
--- a/cpp/src/qpid/Url.cpp
+++ b/cpp/src/qpid/Url.cpp
@@ -19,57 +19,32 @@
#include "qpid/Url.h"
#include "qpid/Exception.h"
#include "qpid/Msg.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/StrError.h"
-#include <limits.h> // NB: must be before boost/spirit headers.
-#include <boost/spirit.hpp>
-#include <boost/spirit/actor.hpp>
+#include <boost/lexical_cast.hpp>
-#include <sstream>
+#include <algorithm>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <errno.h>
+#include <string.h>
-using namespace boost::spirit;
using namespace std;
+using boost::lexical_cast;
namespace qpid {
-std::ostream& operator<<(std::ostream& os, const TcpAddress& a) {
- return os << "tcp:" << a.host << ":" << a.port;
-}
-
-std::istream& operator>>(std::istream&, const TcpAddress&);
+Url::Invalid::Invalid(const string& s) : Exception(s) {}
Url Url::getHostNameUrl(uint16_t port) {
- char name[HOST_NAME_MAX];
- if (::gethostname(name, sizeof(name)) != 0)
- throw InvalidUrl(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
- return Url(TcpAddress(name, port));
+ TcpAddress address(std::string(), port);
+ if (!sys::SystemInfo::getLocalHostname(address))
+ throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
+ return Url(address);
}
-static const string LOCALHOST("127.0.0.1");
-
Url Url::getIpAddressesUrl(uint16_t port) {
Url url;
- int s = socket (PF_INET, SOCK_STREAM, 0);
- for (int i=1;;i++) {
- struct ifreq ifr;
- ifr.ifr_ifindex = i;
- if (::ioctl (s, SIOCGIFNAME, &ifr) < 0)
- break;
- /* now ifr.ifr_name is set */
- if (::ioctl (s, SIOCGIFADDR, &ifr) < 0)
- continue;
- struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
- string addr(inet_ntoa(sin->sin_addr));
- if (addr != LOCALHOST)
- url.push_back(TcpAddress(addr, port));
- }
- close (s);
+ sys::SystemInfo::getLocalIpAddresses(port, url);
return url;
}
@@ -93,78 +68,138 @@ ostream& operator<<(ostream& os, const Url& url) {
return os;
}
-// Addition to boost::spirit parsers: accept any character from a
-// string. Vastly more compile-time-efficient than long rules of the
-// form: ch_p('x') | ch_p('y') |...
-//
-struct ch_in : public char_parser<ch_in> {
- ch_in(const string& chars_) : chars(chars_) {}
- bool test(char ch_) const {
- return chars.find(ch_) != string::npos;
- }
- string chars;
-};
-inline ch_in ch_in_p(const string& chars) {
- return ch_in(chars);
-}
+/** Simple recursive-descent parser for url grammar in AMQP 0-10 spec:
+
+ amqp_url = "amqp:" prot_addr_list
+ prot_addr_list = [prot_addr ","]* prot_addr
+ prot_addr = tcp_prot_addr | tls_prot_addr
+
+ tcp_prot_addr = tcp_id tcp_addr
+ tcp_id = "tcp:" | ""
+ tcp_addr = [host [":" port] ]
+ host = <as per http://www.ietf.org/rfc/rfc3986.txt>
+ port = number]]>
+*/
+class UrlParser {
+ public:
+ UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {}
+ bool parse() { return literal("amqp:") && list(&UrlParser::protAddr, &UrlParser::comma) && i == end; }
-/** Grammar for AMQP URLs. */
-struct UrlGrammar : public grammar<UrlGrammar>
-{
- Url& addr;
+ private:
+ typedef bool (UrlParser::*Rule)();
+
+ bool comma() { return literal(","); }
+
+ // NOTE: tcpAddr must be last since it is allowed to omit it's tcp: tag.
+ bool protAddr() { return exampleAddr() || tcpAddr(); }
+
+ bool tcpAddr() {
+ TcpAddress addr;
+ literal("tcp:"); // Don't check result, allowed to be absent.
+ return addIf(host(addr.host) && (literal(":") ? port(addr.port) : true), addr);
+ }
- UrlGrammar(Url& addr_) : addr(addr_) {}
-
- template <class ScannerT>
- struct definition {
- TcpAddress tcp;
-
- definition(const UrlGrammar& self)
- {
- first = eps_p[clear_a(self.addr)] >> amqp_url;
- amqp_url = str_p("amqp:") >> prot_addr_list >>
- !(str_p("/") >> !parameters);
- prot_addr_list = prot_addr % ',';
- prot_addr = tcp_prot_addr; // Extend for TLS etc.
-
- // TCP addresses
- tcp_prot_addr = tcp_id >> tcp_addr[push_back_a(self.addr, tcp)];
- tcp_id = !str_p("tcp:");
- tcp_addr = !(host[assign_a(tcp.host)] >> !(':' >> port));
-
- // See http://www.apps.ietf.org/rfc/rfc3986.html#sec-A
- // for real host grammar. Shortcut:
- port = uint_parser<uint16_t>()[assign_a(tcp.port)];
- host = *( unreserved | pct_encoded );
- unreserved = alnum_p | ch_in_p("-._~");
- pct_encoded = "%" >> xdigit_p >> xdigit_p;
- parameters = *anychar_p >> end_p; // Ignore, not used yet.
+ // Placeholder address type till we have multiple address types. Address is a single char.
+ bool exampleAddr () {
+ if (literal("example:") && i < end) {
+ ExampleAddress ex(*i++);
+ url.push_back(ex);
+ return true;
}
+ return false;
+ }
+
+ // FIXME aconway 2008-11-20: this does not 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) h = LOCALHOST; // Default
+ else h.assign(start, i);
+ return true;
+ }
+
+ bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); }
- const rule<ScannerT>& start() const { return first; }
+ bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); }
- rule<ScannerT> first, amqp_url, prot_addr_list, prot_addr,
- tcp_prot_addr, tcp_id, tcp_addr, host, port,
- unreserved, pct_encoded, parameters;
+ bool hexDigit() { return i < end && ::strchr("01234567890abcdefABCDEF", *i) && advance(); }
+
+ bool port(uint16_t& p) { return decimalInt(p); }
+
+ template <class AddrType> bool addIf(bool ok, const AddrType& addr) { if (ok) url.push_back(addr); return ok; }
+
+ 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) {
- cache.clear();
- if (!boost::spirit::parse(url, UrlGrammar(*this)).full)
- throw InvalidUrl(string("Invalid AMQP url: ")+url);
+ parseNoThrow(url);
+ if (empty())
+ throw Url::Invalid(QPID_MSG("Invalid URL: " << url));
}
void Url::parseNoThrow(const char* url) {
cache.clear();
- if (!boost::spirit::parse(url, UrlGrammar(*this)).full)
+ if (!UrlParser(*this, url).parse())
clear();
}
void Url::throwIfEmpty() const {
if (empty())
- throw InvalidUrl("URL contains no addresses");
+ throw Url::Invalid("URL contains no addresses");
}
std::istream& operator>>(std::istream& is, Url& url) {
diff --git a/cpp/src/qpid/Url.h b/cpp/src/qpid/Url.h
deleted file mode 100644
index 20f42db0ad..0000000000
--- a/cpp/src/qpid/Url.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef QPID_URL_H
-#define QPID_URL_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 <boost/variant.hpp>
-#include <string>
-#include <vector>
-#include <new>
-#include <ostream>
-
-namespace qpid {
-
-/** TCP address of a broker - host:port */
-struct TcpAddress {
- static const uint16_t DEFAULT_PORT=5672;
- explicit TcpAddress(const std::string& host_=std::string(),
- uint16_t port_=DEFAULT_PORT)
- : host(host_), port(port_) {}
- std::string host;
- uint16_t port;
-};
-
-inline bool operator==(const TcpAddress& x, const TcpAddress& y) {
- return y.host==x.host && y.port == x.port;
-}
-
-std::ostream& operator<<(std::ostream& os, const TcpAddress& a);
-
-/** Address is a variant of all address types, more coming in future. */
-typedef boost::variant<TcpAddress> Address;
-
-/** An AMQP URL contains a list of addresses */
-struct Url : public std::vector<Address> {
-
- /** Url with the hostname as returned by gethostname(2) */
- static Url getHostNameUrl(uint16_t port);
-
- /** Url with local IP address(es), may be more than one address
- * on a multi-homed host. */
- static Url getIpAddressesUrl(uint16_t port);
-
- struct InvalidUrl : public Exception {
- InvalidUrl(const std::string& s) : Exception(s) {}
- };
-
- /** Convert to string form. */
- std::string str() const;
-
- /** Empty URL. */
- Url() {}
-
- /** URL containing a single address */
- explicit Url(const Address& addr) { push_back(addr); }
-
- /** Parse url, throw InvalidUrl if invalid. */
- explicit Url(const std::string& url) { parse(url.c_str()); }
-
- /** Parse url, throw InvalidUrl if invalid. */
- explicit Url(const char* url) { parse(url); }
-
- template<class T> Url& operator=(T s) { parse(s); return *this; }
-
- /** Throw InvalidUrl if the URL does not contain any addresses. */
- void throwIfEmpty() const;
-
- /** Replace contents with parsed URL as defined in
- * https://wiki.108.redhat.com/jira/browse/AMQP-95
- *@exception InvalidUrl if the url is invalid.
- */
- void parse(const char* url);
- void parse(const std::string& url) { parse(url.c_str()); }
-
- /** Replace contesnts with parsed URL as defined in
- * https://wiki.108.redhat.com/jira/browse/AMQP-95
- * url.empty() will be true if url is invalid.
- */
- void parseNoThrow(const char* url);
-
- private:
- mutable std::string cache; // cache string form for efficiency.
-};
-
-inline bool operator==(const Url& a, const Url& b) { return a.str()==b.str(); }
-inline bool operator!=(const Url& a, const Url& b) { return a.str()!=b.str(); }
-
-std::ostream& operator<<(std::ostream& os, const Url& url);
-std::istream& operator>>(std::istream& is, Url& url);
-
-} // namespace qpid
-
-#endif /*!QPID_URL_H*/
diff --git a/cpp/src/qpid/Version.h b/cpp/src/qpid/Version.h
new file mode 100755
index 0000000000..67c0281ac5
--- /dev/null
+++ b/cpp/src/qpid/Version.h
@@ -0,0 +1,44 @@
+#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"
+#endif
+
+namespace qpid {
+#ifdef HAVE_CONFIG_H
+ 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
+#else
+ const std::string product = "qpidc";
+ const std::string version = "0.6";
+ const std::string saslName = "qpid-broker";
+#endif
+}
+
+#endif /*!QPID_VERSION_H*/
diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp
index 79e4af57ee..fe2644c136 100644
--- a/cpp/src/qpid/acl/Acl.cpp
+++ b/cpp/src/qpid/acl/Acl.cpp
@@ -16,126 +16,150 @@
*
*/
-#include "Acl.h"
-
+#include "qpid/acl/Acl.h"
+#include "qpid/acl/AclData.h"
#include "qpid/broker/Broker.h"
#include "qpid/Plugin.h"
#include "qpid/Options.h"
-#include "qpid/shared_ptr.h"
#include "qpid/log/Logger.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>
-namespace qpid {
-namespace acl {
-
using namespace std;
-
- Acl::Acl (AclValues& av, broker::Broker& b): aclValues(av), broker(&b), transferAcl(false)
+using namespace qpid::acl;
+using qpid::broker::Broker;
+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)
+{
+
+ 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)
{
- if (!readAclFile()) throw Exception("Could not read ACL file");
- QPID_LOG(info, "ACL Plugin loaded");
+ boost::shared_ptr<AclData> dataLocal = data; //rcu copy
- }
+ // add real ACL check here...
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,name,params);
- std::string Acl::printAction(acl::Action action)
- {
- switch (action)
- {
- case CONSUME: return "Consume";
- case PUBLISH: return "Publish";
- case CREATE: return "Create";
- case ACCESS: return "Access";
- case BIND: return "Bind";
- case UNBIND: return "Unbind";
- case DELETE: return "Delete";
- case PURGE: return "Purge";
- case UPDATE: return "Update";
- default: return "Unknown";
- }
- }
-
- std::string Acl::printObjType(acl::ObjectType objType)
- {
- switch (objType)
- {
- case QUEUE: return "Queue";
- case EXCHANGE: return "Exchnage";
- case BROKER: return "Broker";
- case LINK: return "Link";
- case ROUTE: return "Route";
- default: return "Unknown";
- }
- }
- bool Acl::authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string name, std::map<std::string, std::string>*
- /*params*/)
- {
- if (aclValues.noEnforce) return true;
- boost::shared_ptr<AclData> dataLocal = data; //rcu copy
-
- // only use dataLocal here...
-
- // add real ACL check here...
- AclResult aclreslt = ALLOWLOG; // hack to test, set based on real decision.
-
-
- return result(aclreslt, id, action, objType, name);
+ return result(aclreslt, id, action, objType, name);
}
- bool Acl::authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string ExchangeName, std::string /*RoutingKey*/)
+ bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey)
{
- if (aclValues.noEnforce) return true;
boost::shared_ptr<AclData> dataLocal = data; //rcu copy
-
+
// only use dataLocal here...
-
- // add real ACL check here...
- AclResult aclreslt = ALLOWLOG; // hack to test, set based on real decision.
-
-
- return result(aclreslt, id, action, objType, ExchangeName);
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,ExchangeName,RoutingKey);
+
+ return result(aclreslt, id, action, objType, ExchangeName);
}
-
- bool Acl::result(AclResult aclreslt, std::string id, acl::Action action, acl::ObjectType objType, std::string name)
+
+ 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:" << printAction(action) << " ObjectType:" << printObjType(objType) << " Name:" << name );
+ 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, framing::FieldTable()));
case ALLOW:
return true;
- case DENYNOLOG:
- return false;
case DENY:
- default:
- QPID_LOG(info, "ACL Deny id:" << id << " action:" << printAction(action) << " ObjectType:" << printObjType(objType) << " Name:" << name);
+ 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, framing::FieldTable()));
+ return false;
}
- return false;
+ return false;
}
-
- bool Acl::readAclFile()
+
+ bool Acl::readAclFile(std::string& errorText)
{
// only set transferAcl = true if a rule implies the use of ACL on transfer, else keep false for permormance reasons.
- return readAclFile(aclValues.aclFile);
+ return readAclFile(aclValues.aclFile, errorText);
}
- bool Acl::readAclFile(std::string aclFile) {
+ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
boost::shared_ptr<AclData> d(new AclData);
- if (AclReader::read(aclFile, d))
+ AclReader ar;
+ if (ar.read(aclFile, d)){
+ agent->raiseEvent(_qmf::EventFileLoadFailed("", ar.getError()));
+ errorText = ar.getError();
+ QPID_LOG(error,ar.getError());
return false;
-
+ }
+
data = d;
+ transferAcl = data->transferAcl; // any transfer ACL
+ if (mgmtObject!=0){
+ mgmtObject->set_transferAcl(transferAcl?1:0);
+ mgmtObject->set_policyFile(aclFile);
+ sys::AbsTime now = sys::AbsTime::now();
+ int64_t ns = sys::Duration(now);
+ mgmtObject->set_lastAclLoad(ns);
+ agent->raiseEvent(_qmf::EventFileLoaded(""));
+ }
return true;
}
Acl::~Acl(){}
+ ManagementObject* Acl::GetManagementObject(void) const
+ {
+ return (ManagementObject*) mgmtObject;
+ }
-
-}} // namespace qpid::acl
+ 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);
+ status = Manageable::STATUS_USER;
+ break;
+ }
+
+ return status;
+}
diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h
index a82add556c..e153187b3d 100644
--- a/cpp/src/qpid/acl/Acl.h
+++ b/cpp/src/qpid/acl/Acl.h
@@ -23,9 +23,12 @@
#include "qpid/acl/AclReader.h"
-#include "qpid/shared_ptr.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 <map>
#include <string>
@@ -38,14 +41,11 @@ class Broker;
namespace acl {
struct AclValues {
- bool noEnforce;
- std::string aclFile;
-
- AclValues() {noEnforce = false; aclFile = "policy.acl"; }
+ std::string aclFile;
};
-class Acl : public broker::AclModule, public RefCounted
+class Acl : public broker::AclModule, public RefCounted, public management::Manageable
{
private:
@@ -53,7 +53,8 @@ private:
broker::Broker* broker;
bool transferAcl;
boost::shared_ptr<AclData> data;
-
+ qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
+ qpid::management::ManagementAgent* agent;
public:
Acl (AclValues& av, broker::Broker& b);
@@ -63,20 +64,21 @@ public:
inline virtual bool doTransferAcl() {return transferAcl;};
// create specilied authorise methods for cases that need faster matching as needed.
- virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string name, std::map<std::string, std::string>* params);
- virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string ExchangeName, std::string RoutingKey);
+ 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:
- std::string printAction(acl::Action action);
- std::string printObjType(acl::ObjectType objType);
- bool result(AclResult aclreslt, std::string id, acl::Action action, acl::ObjectType objType, std::string name);
- bool readAclFile();
- bool readAclFile(std::string aclFile);
+ 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/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp
new file mode 100644
index 0000000000..5d7a028736
--- /dev/null
+++ b/cpp/src/qpid/acl/AclData.cpp
@@ -0,0 +1,236 @@
+/*
+ *
+ * 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)
+{
+ 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 = boost::lexical_cast<uint64_t>(pMItr->second);
+ uint64_t paramMax = boost::lexical_cast<uint64_t>(paramItr->second);
+ 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/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h
new file mode 100644
index 0000000000..a63afab12b
--- /dev/null
+++ b/cpp/src/qpid/acl/AclData.h
@@ -0,0 +1,83 @@
+#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 an deny or accept basis.
+ bool transferAcl;
+
+ 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/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp
index a025354f13..e4d721ea44 100644
--- a/cpp/src/qpid/acl/AclPlugin.cpp
+++ b/cpp/src/qpid/acl/AclPlugin.cpp
@@ -21,9 +21,9 @@
#include "qpid/broker/Broker.h"
#include "qpid/Plugin.h"
#include "qpid/Options.h"
-#include "qpid/shared_ptr.h"
#include "qpid/log/Statement.h"
+#include <boost/shared_ptr.hpp>
#include <boost/utility/in_place_factory.hpp>
namespace qpid {
@@ -36,13 +36,11 @@ using namespace std;
* New boost allows a shared_ptr but that's not compatible with old boost.
*/
struct AclOptions : public Options {
- AclValues& values;
+ AclValues& values;
AclOptions(AclValues& v) : Options("ACL Options"), values(v) {
addOptions()
- ("no-enforce-acl", optValue(values.noEnforce), "Do not enforce ACL")
- ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir")
- ;
+ ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir");
}
};
@@ -51,20 +49,25 @@ 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.noEnforce){
- QPID_LOG(info, "ACL Disabled, no ACL checking being done.");
- return;
- }
- if (acl) throw Exception("ACL plugin cannot be initialized twice in one process.");
- std::ostringstream oss;
- oss << b.getDataDir().getPath() << "/" << values.aclFile;
- values.aclFile = oss.str();
+ 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));
@@ -89,5 +92,5 @@ static AclPlugin instance; // Static initialization.
// For test purposes.
boost::intrusive_ptr<Acl> getGlobalAcl() { return instance.acl; }
-
+
}} // namespace qpid::acl
diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp
index 0a9517bc76..8f419a6f50 100644
--- a/cpp/src/qpid/acl/AclReader.cpp
+++ b/cpp/src/qpid/acl/AclReader.cpp
@@ -18,47 +18,306 @@
#include "qpid/acl/AclReader.h"
+#include <cctype>
#include <cstring>
-//#include <iostream> // debug
#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() - 1; cnt; i--, cnt--) {
+ 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) {
-//std::cout << "AclReader::read(" << fn << ")" << std::endl << std::flush;
+ fileName = fn;
+ lineNumber = 0;
char buff[1024];
std::ifstream ifs(fn.c_str(), std::ios_base::in);
if (!ifs.good()) {
- // error/exception - file open error
+ 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);
- processLine(buff, d);
+ 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 (...) {
- // error/exception - file read/processing error
+ errorStream << "Unable to read ACL file \"" << fn << "\": Unknown exception";
ifs.close();
- return -2;
+ 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;
-void AclReader::processLine(char* line, boost::shared_ptr<AclData> /*d*/) {
- std::vector<std::string> toks;
- int numToks = tokenizeLine(line, toks);
- for (int i=0; i<numToks; i++) {
-// DO MAGIC STUFF HERE
-//std::cout << "tok " << i << ": " << toks[i] << std::endl << std::flush;
- }
+ int numToks = tokenize(line, toks);
+ 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 << "Non-continuation line must start with \"group\" or \"acl\".";
+ ret = false;
+ }
+ }
+ contFlag = cont;
+ return ret;
}
-int AclReader::tokenizeLine(char* line, std::vector<std::string>& toks) {
- const char* tokChars = " \t\n";
+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) {
@@ -69,5 +328,223 @@ int AclReader::tokenizeLine(char* line, std::vector<std::string>& toks) {
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 (!checkName(toks[i])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters.";
+ return false;
+ }
+ if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ } else {
+ if (toksSize < (cont ? 2 : 3)) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Insufficient tokens for group definition.";
+ return false;
+ }
+ if (!checkName(toks[1])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "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 (!checkName(toks[i])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters.";
+ return false;
+ }
+ 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 << "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 << "Insufficient tokens for acl definition.";
+ return false;
+ }
+
+ AclResult res;
+ try {
+ res = AclHelper::getAclResult(toks[1]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "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 << "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 << "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 << "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 << "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 << "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 << "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::checkName(const std::string& name) {
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && 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 << "Username '" << name << "' must contain a realm";
+ return false;
+ }
+ return true;
+}
}} // namespace qpid::acl
diff --git a/cpp/src/qpid/acl/AclReader.h b/cpp/src/qpid/acl/AclReader.h
index 783b70d98a..dccb450192 100644
--- a/cpp/src/qpid/acl/AclReader.h
+++ b/cpp/src/qpid/acl/AclReader.h
@@ -21,25 +21,98 @@
*/
#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 {
-struct AclData {
- bool lc; // Line continue flag
- AclData() : lc(false) {}
-};
-
class AclReader {
-public:
- static int read(const std::string& fn, boost::shared_ptr<AclData> d);
-private:
- static void processLine(char* line, boost::shared_ptr<AclData> d);
- static int tokenizeLine(char* line, std::vector<std::string>& toks);
+ 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 checkName(const std::string& name);
+ static nvPair splitNameValuePair(const std::string& nvpString);
};
-
+
}} // namespace qpid::acl
#endif // QPID_ACL_ACLREADER_H
diff --git a/cpp/src/qpid/acl/management-schema.xml b/cpp/src/qpid/acl/management-schema.xml
new file mode 100644
index 0000000000..f4637253d0
--- /dev/null
+++ b/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="sstr" 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="sstr"/>
+ <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/cpp/src/qpid/agent/ManagementAgent.h b/cpp/src/qpid/agent/ManagementAgent.h
deleted file mode 100644
index e7379e6c94..0000000000
--- a/cpp/src/qpid/agent/ManagementAgent.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef _qpid_agent_ManagementAgent_
-#define _qpid_agent_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/management/ManagementObject.h"
-#include "qpid/management/Manageable.h"
-#include "qpid/sys/Mutex.h"
-
-namespace qpid {
-namespace management {
-
-class ManagementAgent
-{
- public:
-
- class Singleton {
- public:
- Singleton(bool disableManagement = false);
- ~Singleton();
- static ManagementAgent* getInstance();
- private:
- static sys::Mutex lock;
- static bool disabled;
- static int refCount;
- static ManagementAgent* agent;
- };
-
- ManagementAgent () {}
- virtual ~ManagementAgent () {}
-
- virtual int getMaxThreads() = 0;
-
- // Connect to a management broker
- //
- // brokerHost - Hostname or IP address (dotted-quad) of broker.
- //
- // brokerPort - TCP port of broker.
- //
- // intervalSeconds - The interval (in seconds) that this agent shall use
- // between broadcast updates to the broker.
- //
- // useExternalThread - If true, the thread of control used for callbacks
- // must be supplied by the user of the object (via the
- // pollCallbacks method).
- //
- // If false, callbacks shall be invoked on the management
- // agent's thread. In this case, the callback implementations
- // MUST be thread safe.
- //
- virtual void init (std::string brokerHost = "localhost",
- uint16_t brokerPort = 5672,
- uint16_t intervalSeconds = 10,
- bool useExternalThread = false) = 0;
-
- // Register a schema with the management agent. This is normally called by the
- // package initializer generated by the management code generator.
- //
- virtual void
- RegisterClass (std::string packageName,
- std::string className,
- uint8_t* md5Sum,
- management::ManagementObject::writeSchemaCall_t schemaCall) = 0;
-
- // Add a management object to the agent. Once added, this object shall be visible
- // in the greater management context.
- //
- // Please note that ManagementObject instances are not explicitly deleted from
- // the management agent. When the core object represented by a management object
- // is deleted, the "resourceDestroy" method on the management object must be called.
- // It will then be reclaimed in due course by the management agent.
- //
- // Once a ManagementObject instance is added to the agent, the agent then owns the
- // instance. The caller MUST NOT free the resources of the instance at any time.
- // When it is no longer needed, invoke its "resourceDestroy" method and discard the
- // pointer. This allows the management agent to report the deletion of the object
- // in an orderly way.
- //
- virtual uint64_t addObject (ManagementObject* objectPtr,
- uint32_t persistId = 0,
- uint32_t persistBank = 4) = 0;
-
- // If "useExternalThread" was set to true in init, this method must
- // be called to provide a thread for any pending method calls that have arrived.
- // The method calls for ManagementObject instances shall be invoked synchronously
- // during the execution of this method.
- //
- // callLimit may optionally be used to limit the number of callbacks invoked.
- // if 0, no limit is imposed.
- //
- // The return value is the number of callbacks that remain queued after this
- // call is complete. It can be used to determine whether or not further calls
- // to pollCallbacks are necessary to clear the backlog. If callLimit is zero,
- // the return value will also be zero.
- //
- virtual uint32_t pollCallbacks (uint32_t callLimit = 0) = 0;
-
- // If "useExternalThread" was set to true in the constructor, this method provides
- // a standard file descriptor that can be used in a select statement to signal that
- // there are method callbacks ready (i.e. that "pollCallbacks" will result in at
- // least one method call). When this fd is ready-for-read, pollCallbacks may be
- // invoked. Calling pollCallbacks shall reset the ready-to-read state of the fd.
- //
- virtual int getSignalFd (void) = 0;
-
-};
-
-}}
-
-#endif /*!_qpid_agent_ManagementAgent_*/
diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
index ebdc71e3b1..f84e158154 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -20,16 +20,25 @@
#include "qpid/management/Manageable.h"
#include "qpid/management/ManagementObject.h"
-#include "ManagementAgentImpl.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/PipeHandle.h"
+#include "qpid/agent/ManagementAgentImpl.h"
#include <list>
-#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <iostream>
+#include <fstream>
+
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::cout;
using std::endl;
@@ -66,128 +75,274 @@ ManagementAgent* ManagementAgent::Singleton::getInstance()
return agent;
}
+const string ManagementAgentImpl::storeMagicNumber("MA02");
+
ManagementAgentImpl::ManagementAgentImpl() :
- clientWasAdded(true), objIdPrefix(0), bgThread(*this), thread(bgThread), startupWait(false)
+ interval(10), extThread(false), pipeHandle(0),
+ initialized(false), connected(false), lastFailure("never connected"),
+ clientWasAdded(true), requestedBrokerBank(0), requestedAgentBank(0),
+ assignedBrokerBank(0), assignedAgentBank(0), bootSequence(0),
+ connThreadBody(*this), connThread(connThreadBody),
+ pubThreadBody(*this), pubThread(pubThreadBody)
{
- // TODO: Establish system ID
}
-void ManagementAgentImpl::init(std::string brokerHost,
- uint16_t brokerPort,
- uint16_t intervalSeconds,
- bool useExternalThread)
+ManagementAgentImpl::~ManagementAgentImpl()
{
+ // shutdown & cleanup all threads
+ connThreadBody.close();
+ pubThreadBody.close();
+
+ connThread.join();
+ pubThread.join();
+
+ // Release the memory associated with stored management objects.
{
Mutex::ScopedLock lock(agentLock);
- startupWait = true;
+
+ moveNewObjectsLH();
+ for (ManagementObjectMap::iterator iter = managementObjects.begin ();
+ iter != managementObjects.end ();
+ iter++) {
+ ManagementObject* object = iter->second;
+ delete object;
+ }
+ managementObjects.clear();
+ }
+ if (pipeHandle) {
+ delete pipeHandle;
+ pipeHandle = 0;
}
+}
+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)
+{
+ client::ConnectionSettings settings;
+ settings.protocol = proto;
+ settings.host = brokerHost;
+ settings.port = brokerPort;
+ settings.username = uid;
+ settings.password = pwd;
+ settings.mechanism = mech;
+ init(settings, intervalSeconds, useExternalThread, _storeFile);
+}
+
+void ManagementAgentImpl::init(const qpid::client::ConnectionSettings& settings,
+ uint16_t intervalSeconds,
+ bool useExternalThread,
+ const std::string& _storeFile)
+{
interval = intervalSeconds;
extThread = useExternalThread;
+ storeFile = _storeFile;
nextObjectId = 1;
- sessionId.generate();
- queueName << "qmfagent-" << sessionId;
- string dest = "qmfagent";
+ QPID_LOG(info, "QMF Agent Initialized: broker=" << settings.host << ":" << settings.port <<
+ " interval=" << intervalSeconds << " storeFile=" << _storeFile);
+ connectionSettings = settings;
- connection.open(brokerHost.c_str(), brokerPort);
- session = connection.newSession (queueName.str());
- dispatcher = new client::Dispatcher(session);
+ // TODO: Abstract the socket calls for portability
+ // qpid::sys::PipeHandle to create a pipe
+ if (extThread) {
+ pipeHandle = new PipeHandle(true);
+ }
+ retrieveData();
+ bootSequence++;
+ if ((bootSequence & 0xF000) != 0)
+ bootSequence = 1;
+ storeData(true);
- session.queueDeclare (arg::queue=queueName.str());
- session.exchangeBind (arg::exchange="amq.direct", arg::queue=queueName.str(),
- arg::bindingKey=queueName.str());
- session.messageSubscribe (arg::queue=queueName.str(),
- arg::destination=dest);
- session.messageFlow (arg::destination=dest, arg::unit=0, arg::value=0xFFFFFFFF);
- session.messageFlow (arg::destination=dest, arg::unit=1, arg::value=0xFFFFFFFF);
+ initialized = true;
+}
- Message attachRequest;
- char rawbuffer[512];
- Buffer buffer (rawbuffer, 512);
+void ManagementAgentImpl::registerClass(const string& packageName,
+ const string& className,
+ uint8_t* md5Sum,
+ qpid::management::ManagementObject::writeSchemaCall_t schemaCall)
+{
+ Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = findOrAddPackage(packageName);
+ addClassLocal(ManagementItem::CLASS_KIND_TABLE, pIter, className, md5Sum, schemaCall);
+}
- attachRequest.getDeliveryProperties().setRoutingKey("broker");
- attachRequest.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str()));
+void ManagementAgentImpl::registerEvent(const string& packageName,
+ const string& eventName,
+ uint8_t* md5Sum,
+ qpid::management::ManagementObject::writeSchemaCall_t schemaCall)
+{
+ Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = findOrAddPackage(packageName);
+ addClassLocal(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall);
+}
- EncodeHeader (buffer, 'A');
- buffer.putShortString ("RemoteAgent [C++]");
- systemId.encode (buffer);
- buffer.putLong (11);
+ObjectId ManagementAgentImpl::addObject(ManagementObject* object,
+ uint64_t persistId)
+{
+ Mutex::ScopedLock lock(addLock);
+ uint16_t sequence = persistId ? 0 : bootSequence;
+ uint64_t objectNum = persistId ? persistId : nextObjectId++;
- size_t length = 512 - buffer.available ();
- string stringBuffer (rawbuffer, length);
- attachRequest.setData (stringBuffer);
+ ObjectId objectId(&attachment, 0, sequence, objectNum);
- session.messageTransfer(arg::content=attachRequest, arg::destination="qpid.management");
+ // TODO: fix object-id handling
+ object->setObjectId(objectId);
+ newManagementObjects[objectId] = object;
+ return objectId;
+}
- dispatcher->listen(dest, this);
- dispatcher->start();
+void ManagementAgentImpl::raiseEvent(const ManagementEvent& event, severity_t severity)
+{
+ Mutex::ScopedLock lock(agentLock);
+ Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+ stringstream key;
- {
- Mutex::ScopedLock lock(agentLock);
- if (startupWait)
- startupCond.wait(agentLock);
- }
+ key << "console.event." << assignedBrokerBank << "." << assignedAgentBank << "." <<
+ event.getPackageName() << "." << event.getEventName();
+
+ encodeHeader(outBuffer, 'e');
+ outBuffer.putShortString(event.getPackageName());
+ outBuffer.putShortString(event.getEventName());
+ outBuffer.putBin128(event.getMd5Sum());
+ outBuffer.putLongLong(uint64_t(Duration(now())));
+ outBuffer.putOctet(sev);
+ event.encode(outBuffer);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", key.str());
}
-ManagementAgentImpl::~ManagementAgentImpl()
+uint32_t ManagementAgentImpl::pollCallbacks(uint32_t callLimit)
{
- dispatcher->stop();
- session.close();
- delete dispatcher;
+ Mutex::ScopedLock lock(agentLock);
+
+ for (uint32_t idx = 0; callLimit == 0 || idx < callLimit; idx++) {
+ if (methodQueue.empty())
+ break;
+
+ QueuedMethod* item = methodQueue.front();
+ methodQueue.pop_front();
+ {
+ Mutex::ScopedUnlock unlock(agentLock);
+ Buffer inBuffer(const_cast<char*>(item->body.c_str()), item->body.size());
+ invokeMethodRequest(inBuffer, item->sequence, item->replyTo);
+ delete item;
+ }
+ }
+
+ char rbuf[100];
+ while (pipeHandle->read(rbuf, 100) > 0) ; // Consume all signaling bytes
+ return methodQueue.size();
}
-void ManagementAgentImpl::RegisterClass (std::string packageName,
- std::string className,
- uint8_t* md5Sum,
- management::ManagementObject::writeSchemaCall_t schemaCall)
-{
- Mutex::ScopedLock lock(agentLock);
- PackageMap::iterator pIter = FindOrAddPackage (packageName);
- AddClassLocal (pIter, className, md5Sum, schemaCall);
+int ManagementAgentImpl::getSignalFd(void)
+{
+ return pipeHandle->getReadHandle();
}
-uint64_t ManagementAgentImpl::addObject (ManagementObject* object,
- uint32_t /*persistId*/,
- uint32_t /*persistBank*/)
+void ManagementAgentImpl::startProtocol()
{
- Mutex::ScopedLock lock(addLock);
- uint64_t objectId;
+ char rawbuffer[512];
+ Buffer buffer(rawbuffer, 512);
+
+ connected = true;
+ encodeHeader(buffer, 'A');
+ buffer.putShortString("RemoteAgent [C++]");
+ systemId.encode (buffer);
+ buffer.putLong(requestedBrokerBank);
+ buffer.putLong(requestedAgentBank);
+ uint32_t length = buffer.getPosition();
+ buffer.reset();
+ connThreadBody.sendBuffer(buffer, length, "qpid.management", "broker");
+ QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank <<
+ " reqAgent=" << requestedAgentBank);
+}
- // TODO: fix object-id handling
- objectId = objIdPrefix | ((nextObjectId++) & 0x00FFFFFF);
- object->setObjectId (objectId);
- newManagementObjects[objectId] = object;
- return objectId;
+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;
+ outFile.close();
+ }
+ }
}
-uint32_t ManagementAgentImpl::pollCallbacks(uint32_t /*callLimit*/)
+void ManagementAgentImpl::retrieveData()
{
- return 0;
+ if (!storeFile.empty()) {
+ ifstream inFile(storeFile.c_str());
+ string mn;
+
+ if (inFile.good()) {
+ inFile >> mn;
+ if (mn == storeMagicNumber) {
+ inFile >> requestedBrokerBank;
+ inFile >> requestedAgentBank;
+ inFile >> bootSequence;
+ }
+ inFile.close();
+ }
+ }
}
-int ManagementAgentImpl::getSignalFd(void)
+void ManagementAgentImpl::sendCommandComplete(string replyToKey, uint32_t sequence,
+ uint32_t code, string text)
{
- return -1;
+ 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();
+ connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyToKey);
+ QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text);
}
void ManagementAgentImpl::handleAttachResponse(Buffer& inBuffer)
{
Mutex::ScopedLock lock(agentLock);
- uint32_t assigned;
- stringstream key;
- assigned = inBuffer.getLong();
- objIdPrefix = ((uint64_t) assigned) << 24;
+ assignedBrokerBank = inBuffer.getLong();
+ assignedAgentBank = inBuffer.getLong();
- startupWait = false;
- startupCond.notify();
+ 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();
+ requestedBrokerBank = assignedBrokerBank;
+ requestedAgentBank = assignedAgentBank;
+ }
+
+ attachment.setBanks(assignedBrokerBank, assignedAgentBank);
// Bind to qpid.management to receive commands
- key << "agent." << assigned;
- session.exchangeBind (arg::exchange="qpid.management", arg::queue=queueName.str(),
- arg::bindingKey=key.str());
+ connThreadBody.bindToBank(assignedBrokerBank, assignedAgentBank);
// Send package indications for all local packages
for (PackageMap::iterator pIter = packages.begin();
@@ -196,21 +351,21 @@ void ManagementAgentImpl::handleAttachResponse(Buffer& inBuffer)
Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
uint32_t outLen;
- EncodeHeader(outBuffer, 'p');
- EncodePackageIndication(outBuffer, pIter);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ encodeHeader(outBuffer, 'p');
+ encodePackageIndication(outBuffer, pIter);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
outBuffer.reset();
- SendBuffer(outBuffer, outLen, "qpid.management", "broker");
+ connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker");
// Send class indications for all local classes
ClassMap cMap = pIter->second;
for (ClassMap::iterator cIter = cMap.begin(); cIter != cMap.end(); cIter++) {
outBuffer.reset();
- EncodeHeader(outBuffer, 'q');
- EncodeClassIndication(outBuffer, pIter, cIter);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ encodeHeader(outBuffer, 'q');
+ encodeClassIndication(outBuffer, pIter, cIter);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
outBuffer.reset();
- SendBuffer(outBuffer, outLen, "qpid.management", "broker");
+ connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker");
}
}
}
@@ -225,20 +380,24 @@ void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequenc
inBuffer.getShortString(key.name);
inBuffer.getBin128(key.hash);
+ QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name);
+
PackageMap::iterator pIter = packages.find(packageName);
if (pIter != packages.end()) {
- ClassMap cMap = pIter->second;
+ ClassMap& cMap = pIter->second;
ClassMap::iterator cIter = cMap.find(key);
if (cIter != cMap.end()) {
- SchemaClass schema = cIter->second;
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader(outBuffer, 's', sequence);
- schema.writeSchemaCall(outBuffer);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset();
- SendBuffer(outBuffer, outLen, "qpid.management", "broker");
+ SchemaClass& schema = cIter->second;
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 's', sequence);
+ schema.writeSchemaCall(outBuffer);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "broker");
+
+ QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name);
}
}
}
@@ -247,30 +406,134 @@ void ManagementAgentImpl::handleConsoleAddedIndication()
{
Mutex::ScopedLock lock(agentLock);
clientWasAdded = true;
+
+ QPID_LOG(trace, "RCVD ConsoleAddedInd");
}
-void ManagementAgentImpl::handleMethodRequest(Buffer& inBuffer, uint32_t sequence, string replyTo)
+void ManagementAgentImpl::invokeMethodRequest(Buffer& inBuffer, uint32_t sequence, string replyTo)
{
string methodName;
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
uint32_t outLen;
- uint64_t objId = inBuffer.getLongLong();
+ ObjectId objId(inBuffer);
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
inBuffer.getShortString(methodName);
- EncodeHeader(outBuffer, 'm', sequence);
+ encodeHeader(outBuffer, 'm', sequence);
ManagementObjectMap::iterator iter = managementObjects.find(objId);
if (iter == managementObjects.end() || iter->second->isDeleted()) {
outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT);
- outBuffer.putShortString (Manageable::StatusText (Manageable::STATUS_UNKNOWN_OBJECT));
+ outBuffer.putMediumString(Manageable::StatusText(Manageable::STATUS_UNKNOWN_OBJECT));
} else {
- iter->second->doMethod(methodName, inBuffer, outBuffer);
+ 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();
+ iter->second->doMethod(methodName, inBuffer, outBuffer);
+ } catch(exception& e) {
+ outBuffer.restore();
+ outBuffer.putLong(Manageable::STATUS_EXCEPTION);
+ outBuffer.putMediumString(e.what());
+ }
}
outLen = MA_BUFFER_SIZE - outBuffer.available();
outBuffer.reset();
- SendBuffer(outBuffer, outLen, "amq.direct", replyTo);
+ connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo);
+}
+
+void ManagementAgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, string replyTo)
+{
+ FieldTable ft;
+ FieldTable::ValuePtr value;
+
+ moveNewObjectsLH();
+
+ ft.decode(inBuffer);
+
+ QPID_LOG(trace, "RCVD GetQuery: map=" << ft);
+
+ 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 = managementObjects.find(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();
+
+ encodeHeader(outBuffer, 'g', sequence);
+ object->writeProperties(outBuffer);
+ object->writeStatistics(outBuffer, true);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo);
+
+ QPID_LOG(trace, "SENT ObjectInd");
+ }
+ sendCommandComplete(replyTo, sequence);
+ return;
+ }
+
+ string className(value->get<string>());
+
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ if (object->getClassName() == className) {
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ encodeHeader(outBuffer, 'g', sequence);
+ object->writeProperties(outBuffer);
+ object->writeStatistics(outBuffer, true);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ connThreadBody.sendBuffer(outBuffer, outLen, "amq.direct", replyTo);
+
+ QPID_LOG(trace, "SENT ObjectInd");
+ }
+ }
+
+ sendCommandComplete(replyTo, sequence);
+}
+
+void ManagementAgentImpl::handleMethodRequest(Buffer& inBuffer, uint32_t sequence, string replyTo)
+{
+ if (extThread) {
+ Mutex::ScopedLock lock(agentLock);
+ string body;
+
+ inBuffer.getRawData(body, inBuffer.available());
+ methodQueue.push_back(new QueuedMethod(sequence, replyTo, body));
+ pipeHandle->write("X", 1);
+ } else {
+ invokeMethodRequest(inBuffer, sequence, replyTo);
+ }
+
+ QPID_LOG(trace, "RCVD MethodRequest");
}
void ManagementAgentImpl::received(Message& msg)
@@ -287,215 +550,371 @@ void ManagementAgentImpl::received(Message& msg)
replyToKey = rt.getRoutingKey();
}
- if (CheckHeader (inBuffer, &opcode, &sequence))
+ if (checkHeader(inBuffer, &opcode, &sequence))
{
if (opcode == 'a') handleAttachResponse(inBuffer);
else if (opcode == 'S') handleSchemaRequest(inBuffer, sequence);
else if (opcode == 'x') handleConsoleAddedIndication();
+ else if (opcode == 'G') handleGetQuery(inBuffer, sequence, replyToKey);
else if (opcode == 'M') handleMethodRequest(inBuffer, sequence, replyToKey);
}
}
-void ManagementAgentImpl::EncodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq)
+void ManagementAgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq)
{
- buf.putOctet ('A');
- buf.putOctet ('M');
- buf.putOctet ('1');
- buf.putOctet (opcode);
- buf.putLong (seq);
+ buf.putOctet('A');
+ buf.putOctet('M');
+ buf.putOctet('2');
+ buf.putOctet(opcode);
+ buf.putLong (seq);
}
-bool ManagementAgentImpl::CheckHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq)
+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 ();
+ uint8_t h1 = buf.getOctet();
+ uint8_t h2 = buf.getOctet();
+ uint8_t h3 = buf.getOctet();
- *opcode = buf.getOctet ();
- *seq = buf.getLong ();
+ *opcode = buf.getOctet();
+ *seq = buf.getLong();
- return h1 == 'A' && h2 == 'M' && h3 == '1';
+ return h1 == 'A' && h2 == 'M' && h3 == '2';
}
-void ManagementAgentImpl::SendBuffer (Buffer& buf,
- uint32_t length,
- string exchange,
- string routingKey)
+ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(const string& name)
{
- Message msg;
- string data;
-
- if (objIdPrefix == 0)
- return;
-
- buf.getRawData(data, length);
- msg.getDeliveryProperties().setRoutingKey(routingKey);
- msg.getMessageProperties().setReplyTo(ReplyTo("amq.direct", queueName.str()));
- msg.setData (data);
- session.messageTransfer (arg::content=msg, arg::destination=exchange);
-}
-
-ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::FindOrAddPackage (std::string name)
-{
- PackageMap::iterator pIter = packages.find (name);
- if (pIter != packages.end ())
+ PackageMap::iterator pIter = packages.find(name);
+ if (pIter != packages.end())
return pIter;
// No such package found, create a new map entry.
- std::pair<PackageMap::iterator, bool> result =
- packages.insert (std::pair<string, ClassMap> (name, ClassMap ()));
+ pair<PackageMap::iterator, bool> result =
+ packages.insert(pair<string, ClassMap>(name, ClassMap()));
- // Publish a package-indication message
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
+ if (connected) {
+ // 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 ();
- SendBuffer (outBuffer, outLen, "qpid.management", "mgmt.schema.package");
+ encodeHeader(outBuffer, 'p');
+ encodePackageIndication(outBuffer, result.first);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "schema.package");
+ }
return result.first;
}
void ManagementAgentImpl::moveNewObjectsLH()
{
- Mutex::ScopedLock lock (addLock);
- for (ManagementObjectMap::iterator iter = newManagementObjects.begin ();
- iter != newManagementObjects.end ();
+ Mutex::ScopedLock lock(addLock);
+ for (ManagementObjectMap::iterator iter = newManagementObjects.begin();
+ iter != newManagementObjects.end();
iter++)
managementObjects[iter->first] = iter->second;
newManagementObjects.clear();
}
-void ManagementAgentImpl::AddClassLocal (PackageMap::iterator pIter,
- string className,
- uint8_t* md5Sum,
- management::ManagementObject::writeSchemaCall_t schemaCall)
+void ManagementAgentImpl::addClassLocal(uint8_t classKind,
+ PackageMap::iterator pIter,
+ const string& className,
+ uint8_t* md5Sum,
+ qpid::management::ManagementObject::writeSchemaCall_t schemaCall)
{
SchemaClassKey key;
ClassMap& cMap = pIter->second;
key.name = className;
- memcpy (&key.hash, md5Sum, 16);
+ memcpy(&key.hash, md5Sum, 16);
- ClassMap::iterator cIter = cMap.find (key);
- if (cIter != cMap.end ())
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end())
return;
// No such class found, create a new class with local information.
- SchemaClass classInfo;
-
- classInfo.writeSchemaCall = schemaCall;
- cMap[key] = classInfo;
-
- // TODO: Publish a class-indication message
+ cMap.insert(pair<SchemaClassKey, SchemaClass>(key, SchemaClass(schemaCall, classKind)));
}
-void ManagementAgentImpl::EncodePackageIndication (Buffer& buf,
- PackageMap::iterator pIter)
+void ManagementAgentImpl::encodePackageIndication(Buffer& buf,
+ PackageMap::iterator pIter)
{
- buf.putShortString ((*pIter).first);
+ buf.putShortString((*pIter).first);
+
+ QPID_LOG(trace, "SENT PackageInd: package=" << (*pIter).first);
}
-void ManagementAgentImpl::EncodeClassIndication (Buffer& buf,
- PackageMap::iterator pIter,
- ClassMap::iterator cIter)
+void ManagementAgentImpl::encodeClassIndication(Buffer& buf,
+ PackageMap::iterator pIter,
+ ClassMap::iterator cIter)
{
SchemaClassKey key = (*cIter).first;
- buf.putShortString ((*pIter).first);
- buf.putShortString (key.name);
- buf.putBin128 (key.hash);
+ 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);
}
-void ManagementAgentImpl::PeriodicProcessing()
+void ManagementAgentImpl::periodicProcessing()
{
#define BUFSIZE 65536
Mutex::ScopedLock lock(agentLock);
char msgChars[BUFSIZE];
uint32_t contentSize;
- string routingKey;
- std::list<uint64_t> deleteList;
+ list<pair<ObjectId, ManagementObject*> > deleteList;
- {
- Buffer msgBuffer(msgChars, BUFSIZE);
- EncodeHeader(msgBuffer, 'h');
- msgBuffer.putLongLong(uint64_t(Duration(now())));
+ if (!connected)
+ return;
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + systemId.str() + ".heartbeat";
- SendBuffer (msgBuffer, contentSize, "qpid.management", routingKey);
+ 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);
+ }
}
- moveNewObjectsLH();
+ clientWasAdded = false;
+
+ //
+ // Process the entire object map.
+ //
+ for (ManagementObjectMap::iterator baseIter = managementObjects.begin();
+ baseIter != managementObjects.end();
+ baseIter++) {
+ ManagementObject* baseObject = baseIter->second;
+
+ //
+ // Skip until we find a base object requiring a sent message.
+ //
+ if (baseObject->getFlags() == 1 ||
+ (!baseObject->getConfigChanged() &&
+ !baseObject->getInstChanged() &&
+ !baseObject->getForcePublish() &&
+ !baseObject->isDeleted()))
+ continue;
- if (clientWasAdded)
- {
- clientWasAdded = false;
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++)
- {
+ Buffer msgBuffer(msgChars, BUFSIZE);
+ for (ManagementObjectMap::iterator iter = baseIter;
+ iter != managementObjects.end();
+ iter++) {
ManagementObject* object = iter->second;
- object->setAllChanged ();
+ if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
+ object->setFlags(1);
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (object->getConfigChanged() || object->getForcePublish() || object->isDeleted()) {
+ encodeHeader(msgBuffer, 'c');
+ object->writeProperties(msgBuffer);
+ }
+
+ if (object->hasInst() && (object->getInstChanged() || object->getForcePublish())) {
+ encodeHeader(msgBuffer, 'i');
+ object->writeStatistics(msgBuffer);
+ }
+
+ if (object->isDeleted())
+ deleteList.push_back(pair<ObjectId, ManagementObject*>(iter->first, object));
+ object->setForcePublish(false);
+
+ if (msgBuffer.available() < (BUFSIZE / 2))
+ break;
+ }
+ }
+
+ contentSize = BUFSIZE - msgBuffer.available();
+ if (contentSize > 0) {
+ msgBuffer.reset();
+ stringstream key;
+ key << "console.obj." << assignedBrokerBank << "." << assignedAgentBank << "." <<
+ baseObject->getPackageName() << "." << baseObject->getClassName();
+ connThreadBody.sendBuffer(msgBuffer, contentSize, "qpid.management", key.str());
}
}
- if (managementObjects.empty ())
- return;
-
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++)
+ // Delete flagged objects
+ for (list<pair<ObjectId, ManagementObject*> >::reverse_iterator iter = deleteList.rbegin();
+ iter != deleteList.rend();
+ iter++) {
+ delete iter->second;
+ managementObjects.erase(iter->first);
+ }
+
+ deleteList.clear();
+
{
- ManagementObject* object = iter->second;
+ Buffer msgBuffer(msgChars, BUFSIZE);
+ encodeHeader(msgBuffer, 'h');
+ msgBuffer.putLongLong(uint64_t(Duration(now())));
+ stringstream key;
+ key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank;
- if (object->getConfigChanged () || object->isDeleted ())
- {
- Buffer msgBuffer (msgChars, BUFSIZE);
- EncodeHeader (msgBuffer, 'c');
- object->writeProperties(msgBuffer);
-
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + systemId.str() + ".prop." + object->getClassName ();
- SendBuffer (msgBuffer, contentSize, "qpid.management", routingKey);
+ contentSize = BUFSIZE - msgBuffer.available();
+ msgBuffer.reset();
+ connThreadBody.sendBuffer(msgBuffer, contentSize, "qpid.management", key.str());
+ }
+}
+
+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());
+
+ subscriptions->subscribe(agent, queueName.str(), dest);
+ QPID_LOG(info, "Connection established with broker");
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (shutdown)
+ return;
+ operational = true;
+ agent.startProtocol();
+ try {
+ 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());
}
-
- if (object->getInstChanged ())
+
{
- Buffer msgBuffer (msgChars, BUFSIZE);
- EncodeHeader (msgBuffer, 'i');
- object->writeStatistics(msgBuffer);
-
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + systemId.str () + ".stat." + object->getClassName ();
- SendBuffer (msgBuffer, contentSize, "qpid.management", routingKey);
+ // 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
+ Mutex::ScopedLock _lock(connLock);
+ if (shutdown)
+ return;
+ sleeping = true;
+ int totalSleep = 0;
+ do {
+ Mutex::ScopedUnlock _unlock(connLock);
+ ::sleep(delayMin);
+ totalSleep += delayMin;
+ } while (totalSleep < delay && !shutdown);
+ sleeping = false;
+ if (shutdown)
+ return;
}
+ }
+}
+
+ManagementAgentImpl::ConnectionThread::~ConnectionThread()
+{
+}
- if (object->isDeleted ())
- deleteList.push_back (iter->first);
+void ManagementAgentImpl::ConnectionThread::sendBuffer(Buffer& buf,
+ uint32_t length,
+ const string& exchange,
+ const string& routingKey)
+{
+ ConnectionThread::shared_ptr s;
+ {
+ Mutex::ScopedLock _lock(connLock);
+ if (!operational)
+ return;
+ s = subscriptions;
}
- // Delete flagged objects
- for (std::list<uint64_t>::reverse_iterator iter = deleteList.rbegin ();
- iter != deleteList.rend ();
- iter++)
- managementObjects.erase (*iter);
+ Message msg;
+ string data;
- deleteList.clear ();
+ 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(exception& e) {
+ QPID_LOG(error, "Exception caught in sendBuffer: " << e.what());
+ // Bounce the connection
+ if (s)
+ s->stop();
+ }
}
-void ManagementAgentImpl::BackgroundThread::run()
+void ManagementAgentImpl::ConnectionThread::bindToBank(uint32_t brokerBank, uint32_t agentBank)
{
- while (true) {
- ::sleep(5);
- agent.PeriodicProcessing();
+ 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;
+ {
+ Mutex::ScopedLock _lock(connLock);
+ shutdown = true;
+ s = subscriptions;
+ }
+ if (s)
+ s->stop();
+}
+
+bool ManagementAgentImpl::ConnectionThread::isSleeping() const
+{
+ 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/cpp/src/qpid/agent/ManagementAgentImpl.h b/cpp/src/qpid/agent/ManagementAgentImpl.h
index f7f19e145d..a876496e98 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.h
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.h
@@ -20,9 +20,10 @@
// under the License.
//
-#include "ManagementAgent.h"
+#include "qpid/agent/ManagementAgent.h"
#include "qpid/client/Connection.h"
-#include "qpid/client/Dispatcher.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"
@@ -30,10 +31,11 @@
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/Mutex.h"
-#include "qpid/sys/Condition.h"
+#include "qpid/sys/PipeHandle.h"
#include "qpid/framing/Uuid.h"
#include <iostream>
#include <sstream>
+#include <deque>
namespace qpid {
namespace management {
@@ -45,33 +47,49 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen
ManagementAgentImpl();
virtual ~ManagementAgentImpl();
+ //
+ // Methods from ManagementAgent
+ //
int getMaxThreads() { return 1; }
- void init(std::string brokerHost = "localhost",
- uint16_t brokerPort = 5672,
- uint16_t intervalSeconds = 10,
- bool useExternalThread = false);
- void RegisterClass(std::string packageName,
- std::string className,
- uint8_t* md5Sum,
+ 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 client::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);
- uint64_t addObject (management::ManagementObject* objectPtr,
- uint32_t persistId = 0,
- uint32_t persistBank = 4);
- uint32_t pollCallbacks (uint32_t callLimit = 0);
- int getSignalFd (void);
+ 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);
+ void raiseEvent(const management::ManagementEvent& event, severity_t severity = SEV_DEFAULT);
+ uint32_t pollCallbacks(uint32_t callLimit = 0);
+ int getSignalFd();
- void PeriodicProcessing();
+ uint16_t getInterval() { return interval; }
+ void periodicProcessing();
private:
- struct SchemaClassKey
- {
+ struct SchemaClassKey {
std::string name;
uint8_t hash[16];
};
- struct SchemaClassKeyComp
- {
+ struct SchemaClassKeyComp {
bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const
{
if (lhs.name != rhs.name)
@@ -84,74 +102,137 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen
}
};
- struct SchemaClass
- {
+ struct SchemaClass {
management::ManagementObject::writeSchemaCall_t writeSchemaCall;
+ uint8_t kind;
- SchemaClass () : writeSchemaCall(0) {}
+ SchemaClass(const management::ManagementObject::writeSchemaCall_t call,
+ const uint8_t _kind) : writeSchemaCall(call), kind(_kind) {}
};
+ struct QueuedMethod {
+ QueuedMethod(uint32_t _seq, std::string _reply, std::string _body) :
+ sequence(_seq), replyTo(_reply), body(_body) {}
+
+ uint32_t sequence;
+ std::string replyTo;
+ std::string body;
+ };
+
+ typedef std::deque<QueuedMethod*> MethodQueue;
typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap;
typedef std::map<std::string, ClassMap> PackageMap;
PackageMap packages;
+ AgentAttachment attachment;
management::ManagementObjectMap managementObjects;
management::ManagementObjectMap newManagementObjects;
+ MethodQueue methodQueue;
void received (client::Message& msg);
uint16_t interval;
bool extThread;
+ sys::PipeHandle* pipeHandle;
uint64_t nextObjectId;
+ std::string storeFile;
sys::Mutex agentLock;
sys::Mutex addLock;
- framing::Uuid sessionId;
framing::Uuid systemId;
+ client::ConnectionSettings connectionSettings;
+ bool initialized;
+ bool connected;
+ std::string lastFailure;
+
+ bool clientWasAdded;
+ uint32_t requestedBrokerBank;
+ uint32_t requestedAgentBank;
+ uint32_t assignedBrokerBank;
+ uint32_t assignedAgentBank;
+ uint16_t bootSequence;
+
+ 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;
- int signalFdIn, signalFdOut;
- client::Connection connection;
- client::Session session;
- client::Dispatcher* dispatcher;
- bool clientWasAdded;
- uint64_t objIdPrefix;
- std::stringstream queueName;
# define MA_BUFFER_SIZE 65536
char outputBuffer[MA_BUFFER_SIZE];
+ char eventBuffer[MA_BUFFER_SIZE];
- class BackgroundThread : public sys::Runnable
+ 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:
- BackgroundThread(ManagementAgentImpl& _agent) : agent(_agent) {}
+ 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 bindToBank(uint32_t brokerBank, uint32_t agentBank);
+ void close();
+ bool isSleeping() const;
};
- BackgroundThread bgThread;
- sys::Thread thread;
- sys::Condition startupCond;
- bool startupWait;
+ 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;
- PackageMap::iterator FindOrAddPackage (std::string name);
+ void startProtocol();
+ void storeData(bool requested=false);
+ void retrieveData();
+ PackageMap::iterator findOrAddPackage(const std::string& name);
void moveNewObjectsLH();
- void AddClassLocal (PackageMap::iterator pIter,
- std::string className,
+ void addClassLocal (uint8_t classKind,
+ PackageMap::iterator pIter,
+ const std::string& className,
uint8_t* md5Sum,
management::ManagementObject::writeSchemaCall_t schemaCall);
- void EncodePackageIndication (qpid::framing::Buffer& buf,
+ void encodePackageIndication (framing::Buffer& buf,
PackageMap::iterator pIter);
- void EncodeClassIndication (qpid::framing::Buffer& buf,
+ void encodeClassIndication (framing::Buffer& buf,
PackageMap::iterator pIter,
ClassMap::iterator cIter);
- void EncodeHeader (qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
- bool CheckHeader (qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
- void SendBuffer (qpid::framing::Buffer& buf,
- uint32_t length,
- std::string exchange,
- std::string routingKey);
+ void encodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
+ bool checkHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
+ void sendCommandComplete (std::string replyToKey, uint32_t sequence,
+ uint32_t code = 0, std::string text = std::string("OK"));
void handleAttachResponse (qpid::framing::Buffer& inBuffer);
void handlePackageRequest (qpid::framing::Buffer& inBuffer);
void handleClassQuery (qpid::framing::Buffer& inBuffer);
void handleSchemaRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence);
+ void invokeMethodRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo);
+ void handleGetQuery (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo);
void handleMethodRequest (qpid::framing::Buffer& inBuffer, uint32_t sequence, std::string replyTo);
void handleConsoleAddedIndication();
};
diff --git a/cpp/src/qpid/amqp_0_10/Array.cpp b/cpp/src/qpid/amqp_0_10/Array.cpp
index 380e0f1f36..2ee47546f2 100644
--- a/cpp/src/qpid/amqp_0_10/Array.cpp
+++ b/cpp/src/qpid/amqp_0_10/Array.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "Array.h"
+#include "qpid/amqp_0_10/Array.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/Codec.h b/cpp/src/qpid/amqp_0_10/Codec.h
index 5cad5cf4ed..fd006a348e 100644
--- a/cpp/src/qpid/amqp_0_10/Codec.h
+++ b/cpp/src/qpid/amqp_0_10/Codec.h
@@ -22,7 +22,7 @@
*
*/
-#include "built_in_types.h"
+#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>
diff --git a/cpp/src/qpid/amqp_0_10/Command.h b/cpp/src/qpid/amqp_0_10/Command.h
index 0fe023e520..b1d3607a84 100644
--- a/cpp/src/qpid/amqp_0_10/Command.h
+++ b/cpp/src/qpid/amqp_0_10/Command.h
@@ -22,7 +22,7 @@
*
*/
-#include "Control.h"
+#include "qpid/amqp_0_10/Control.h"
#include "qpid/amqp_0_10/structs.h"
namespace qpid {
diff --git a/cpp/src/qpid/amqp_0_10/Connection.cpp b/cpp/src/qpid/amqp_0_10/Connection.cpp
index a3692911b2..bf2e7d5713 100644
--- a/cpp/src/qpid/amqp_0_10/Connection.cpp
+++ b/cpp/src/qpid/amqp_0_10/Connection.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,19 +18,26 @@
* under the License.
*
*/
-#include "Connection.h"
+#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, broker::Broker& broker, const std::string& id, bool _isClient)
- : frameQueueClosed(false), output(o),
- connection(new broker::Connection(this, broker, id, _isClient)),
- identifier(id), initialized(false), isClient(_isClient) {}
+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);
@@ -38,7 +45,9 @@ size_t Connection::decode(const char* buffer, size_t size) {
//read in protocol header
framing::ProtocolInitiation pi;
if (pi.decode(in)) {
- //TODO: check the version is correct
+ if(!(pi==version))
+ throw Exception(QPID_MSG("Unsupported version: " << pi
+ << " supported version " << version));
QPID_LOG(trace, "RECV " << identifier << " INIT(" << pi << ")");
}
initialized = true;
@@ -52,18 +61,26 @@ size_t Connection::decode(const char* buffer, size_t size) {
}
bool Connection::canEncode() {
- if (!frameQueueClosed) connection->doOutput();
Mutex::ScopedLock l(frameQueueLock);
- return (!isClient && !initialized) || !frameQueue.empty();
+ if (!popClosed) {
+ Mutex::ScopedUnlock u(frameQueueLock);
+ connection->doOutput();
+ }
+ return !popClosed && ((!isClient && !initialized) || !frameQueue.empty());
}
bool Connection::isClosed() const {
Mutex::ScopedLock l(frameQueueLock);
- return frameQueueClosed;
+ return pushClosed && popClosed;
}
size_t Connection::encode(const char* buffer, size_t size) {
- Mutex::ScopedLock l(frameQueueLock);
+ { // 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());
@@ -71,23 +88,39 @@ size_t Connection::encode(const char* buffer, size_t size) {
initialized = true;
QPID_LOG(trace, "SENT " << identifier << " INIT(" << pi << ")");
}
- while (!frameQueue.empty() && (frameQueue.front().size() <= out.available())) {
- frameQueue.front().encode(out);
- QPID_LOG(trace, "SENT [" << identifier << "]: " << frameQueue.front());
- frameQueue.pop();
+ 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;
}
- assert(frameQueue.empty() || frameQueue.front().size() <= size);
- if (!frameQueue.empty() && frameQueue.front().size() > size)
- throw InternalErrorException(QPID_MSG("Could not write frame, too large for buffer."));
return out.getPosition();
}
-void Connection::activateOutput() { output.activateOutput(); }
+void Connection::abort() { output.abort(); }
+void Connection::activateOutput() { output.activateOutput(); }
+void Connection::giveReadCredit(int32_t credit) { output.giveReadCredit(credit); }
void Connection::close() {
- // Close the output queue.
+ // No more frames can be pushed onto the queue.
+ // Frames aleady on the queue can be popped.
Mutex::ScopedLock l(frameQueueLock);
- frameQueueClosed = true;
+ pushClosed = true;
}
void Connection::closed() {
@@ -97,14 +130,24 @@ void Connection::closed() {
void Connection::send(framing::AMQFrame& f) {
{
Mutex::ScopedLock l(frameQueueLock);
- if (!frameQueueClosed)
- frameQueue.push(f);
+ if (!pushClosed)
+ frameQueue.push_back(f);
+ buffered += f.encodedSize();
}
activateOutput();
}
framing::ProtocolVersion Connection::getVersion() const {
- return framing::ProtocolVersion(0,10);
+ 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/cpp/src/qpid/amqp_0_10/Connection.h b/cpp/src/qpid/amqp_0_10/Connection.h
index b707031789..995d824796 100644
--- a/cpp/src/qpid/amqp_0_10/Connection.h
+++ b/cpp/src/qpid/amqp_0_10/Connection.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,41 +21,61 @@
* 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/Connection.h"
+#include "qpid/broker/BrokerImportExport.h"
#include <boost/intrusive_ptr.hpp>
-#include <queue>
#include <memory>
+#include <deque>
namespace qpid {
-namespace broker { class Broker; }
+
+namespace sys {
+class ConnectionInputHandlerFactory;
+}
+
namespace amqp_0_10 {
class Connection : public sys::ConnectionCodec,
public sys::ConnectionOutputHandler
{
- std::queue<framing::AMQFrame> frameQueue;
- bool frameQueueClosed;
+ typedef std::deque<framing::AMQFrame> FrameQueue;
+
+ FrameQueue frameQueue;
+ FrameQueue workQueue;
+ bool pushClosed, popClosed;
mutable sys::Mutex frameQueueLock;
sys::OutputControl& output;
- boost::intrusive_ptr<broker::Connection> connection;
+ std::auto_ptr<sys::ConnectionInputHandler> connection;
std::string identifier;
bool initialized;
bool isClient;
-
+ size_t buffered;
+ framing::ProtocolVersion version;
+
public:
- Connection(sys::OutputControl&, broker::Broker&, const std::string& id, bool isClient = false);
+ 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
diff --git a/cpp/src/qpid/amqp_0_10/Control.h b/cpp/src/qpid/amqp_0_10/Control.h
index 226f6f92a6..ce188ae6d8 100644
--- a/cpp/src/qpid/amqp_0_10/Control.h
+++ b/cpp/src/qpid/amqp_0_10/Control.h
@@ -22,7 +22,7 @@
*
*/
-#include "Struct.h"
+#include "qpid/amqp_0_10/Struct.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/Exception.h b/cpp/src/qpid/amqp_0_10/Exception.h
index 4841d91215..6d526c1706 100644
--- a/cpp/src/qpid/amqp_0_10/Exception.h
+++ b/cpp/src/qpid/amqp_0_10/Exception.h
@@ -32,12 +32,12 @@ 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::ConnectionException {
+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::ConnectionException(c,m), code(c) {}
+ : qpid::Exception(m), code(c) {}
Code code;
};
@@ -45,10 +45,10 @@ struct ConnectionException : public qpid::ConnectionException {
* 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::SessionException {
+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::SessionException(code, msg) {}
+ SessionException(int /*code*/, const std::string& msg) : qpid::Exception(msg) {}
};
/** Raised when the state of a session has been destroyed */
@@ -94,93 +94,3 @@ struct SessionDetachedException : public SessionException {
}} // namespace qpid::amqp_0_10
#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/
-#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 Exception {
- typedef connection::CloseCode Code;
- ConnectionException(Code c, const std::string m)
- : 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 Exception {
- SessionException(const std::string& msg) : Exception(msg) {}
-};
-
-/** Raised when the state of a session has been destroyed */
-struct SessionDestroyedException : public SessionException {
- SessionDestroyedException(const std::string& msg) : SessionException(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(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(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(m), code(c) {}
- Code code;
-};
-
-}} // namespace qpid::amqp_0_10
-
-#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/
diff --git a/cpp/src/qpid/amqp_0_10/FrameHeader.cpp b/cpp/src/qpid/amqp_0_10/FrameHeader.cpp
index f1a59b9e27..371e3c1bcb 100644
--- a/cpp/src/qpid/amqp_0_10/FrameHeader.cpp
+++ b/cpp/src/qpid/amqp_0_10/FrameHeader.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "FrameHeader.h"
+#include "qpid/amqp_0_10/FrameHeader.h"
#include <ios>
#include <iomanip>
#include <ostream>
diff --git a/cpp/src/qpid/amqp_0_10/Header.cpp b/cpp/src/qpid/amqp_0_10/Header.cpp
index 669c960e7f..d83814e969 100644
--- a/cpp/src/qpid/amqp_0_10/Header.cpp
+++ b/cpp/src/qpid/amqp_0_10/Header.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "Header.h"
+#include "qpid/amqp_0_10/Header.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/Holder.h b/cpp/src/qpid/amqp_0_10/Holder.h
index 8712db6c86..605d2e0ed5 100644
--- a/cpp/src/qpid/amqp_0_10/Holder.h
+++ b/cpp/src/qpid/amqp_0_10/Holder.h
@@ -22,7 +22,7 @@
*
*/
#include "qpid/framing/Blob.h"
-#include "apply.h"
+#include "qpid/amqp_0_10/apply.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/Map.cpp b/cpp/src/qpid/amqp_0_10/Map.cpp
index b517b8baba..af3b302d25 100644
--- a/cpp/src/qpid/amqp_0_10/Map.cpp
+++ b/cpp/src/qpid/amqp_0_10/Map.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "Map.h"
+#include "qpid/amqp_0_10/Map.h"
#include "qpid/amqp_0_10/Struct32.h"
#include "qpid/amqp_0_10/Array.h"
#include <ostream>
diff --git a/cpp/src/qpid/amqp_0_10/SessionHandler.cpp b/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
index 35587940e5..5f97d292bc 100644
--- a/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
+++ b/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
@@ -19,10 +19,11 @@
*/
-#include "SessionHandler.h"
+#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"
@@ -33,30 +34,35 @@ 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), ignoring(false), sendReady(), receiveReady() {}
+ : channel(ch, out), peer(channel),
+ awaitingDetached(false),
+ sendReady(), receiveReady() {}
SessionHandler::~SessionHandler() {}
namespace {
bool isSessionControl(AMQMethodBody* m) {
- return m &&
- m->amqpClassId() == SESSION_CLASS_ID;
+ return m && m->amqpClassId() == SESSION_CLASS_ID;
}
-bool isSessionDetachedControl(AMQMethodBody* m) {
- return isSessionControl(m) &&
- m->amqpMethodId() == SESSION_DETACHED_METHOD_ID;
-}
-} // namespace
-void SessionHandler::checkAttached() {
- if (!getState())
- throw NotAttachedException(
- QPID_MSG("Channel " << channel.get() << " is not attached"));
- assert(getInHandler());
- assert(channel.next);
+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);
}
@@ -65,12 +71,19 @@ void SessionHandler::handleIn(AMQFrame& f) {
// Note on channel states: a channel is attached if session != 0
AMQMethodBody* m = f.getBody()->getMethod();
try {
- if (ignoring && !isSessionDetachedControl(m))
- return;
- else if (isSessionControl(m))
+ // 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 {
- checkAttached();
+ // 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))
@@ -80,11 +93,21 @@ void SessionHandler::handleIn(AMQFrame& f) {
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());
- if (getState())
- peer.detached(getState()->getId().getName(), e.code);
- channelException(e.code, e.getMessage());
+ 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());
@@ -92,16 +115,16 @@ void SessionHandler::handleIn(AMQFrame& f) {
}
catch(const std::exception& e) {
QPID_LOG(error, "Unexpected exception: " << e.what());
- connectionException(connection::FRAMING_ERROR, e.what());
+ connectionException(connection::CLOSE_CODE_FRAMING_ERROR, e.what());
}
}
namespace {
bool isControl(const AMQFrame& f) {
- return f.getMethod() && f.getMethod()->type() == framing::CONTROL;
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL;
}
bool isCommand(const AMQFrame& f) {
- return f.getMethod() && f.getMethod()->type() == framing::COMMAND;
+ return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND;
}
} // namespace
@@ -117,19 +140,15 @@ void SessionHandler::handleOut(AMQFrame& f) {
channel.handle(f);
}
-void SessionHandler::checkName(const std::string& name) {
- checkAttached();
- if (name != getState()->getId().getName())
- throw InvalidArgumentException(
- QPID_MSG("Incorrect session name: " << name
- << ", expecting: " << getState()->getId().getName()));
-}
-
-void SessionHandler::attach(const std::string& name, bool force) {
+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 SessionBusyException(
+ 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());
@@ -140,21 +159,30 @@ void SessionHandler::attach(const std::string& name, bool force) {
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) {
- checkName(name);
+ CHECK_NAME(name, "session.attached");
}
void SessionHandler::detach(const std::string& name) {
- checkName(name);
- peer.detached(name, session::NORMAL);
+ CHECK_NAME(name, "session.detach");
+ peer.detached(name, session::DETACH_CODE_NORMAL);
handleDetach();
}
void SessionHandler::detached(const std::string& name, uint8_t code) {
- checkName(name);
- ignoring = false;
- if (code != session::NORMAL)
- channelException(code, "session.detached from peer.");
+ CHECK_NAME(name, "session.detached");
+ awaitingDetached = false;
+ if (code != session::DETACH_CODE_NORMAL)
+ channelException(convert(code), "session.detached from peer.");
else {
handleDetach();
}
@@ -247,7 +275,7 @@ void SessionHandler::gap(const SequenceSet& /*commands*/) {
void SessionHandler::sendDetach()
{
checkAttached();
- ignoring = true;
+ awaitingDetached = true;
peer.detach(getState()->getId().getName());
}
@@ -258,7 +286,6 @@ void SessionHandler::sendCompletion() {
}
void SessionHandler::sendAttach(bool force) {
- checkAttached();
QPID_LOG(debug, "SessionHandler::sendAttach attach id=" << getState()->getId());
peer.attach(getState()->getId().getName(), force);
if (getState()->hasState())
@@ -275,6 +302,12 @@ void SessionHandler::sendCommandPoint(const SessionPoint& point) {
}
}
+void SessionHandler::markReadyToSend() {
+ if (!sendReady) {
+ sendReady = true;
+ }
+}
+
void SessionHandler::sendTimeout(uint32_t t) {
checkAttached();
peer.requestTimeout(t);
diff --git a/cpp/src/qpid/amqp_0_10/SessionHandler.h b/cpp/src/qpid/amqp_0_10/SessionHandler.h
index ccbe597bfc..fa6e6f4af6 100644
--- a/cpp/src/qpid/amqp_0_10/SessionHandler.h
+++ b/cpp/src/qpid/amqp_0_10/SessionHandler.h
@@ -26,6 +26,7 @@
#include "qpid/framing/AMQP_AllProxy.h"
#include "qpid/framing/AMQP_AllOperations.h"
#include "qpid/SessionState.h"
+#include "qpid/CommonImportExport.h"
namespace qpid {
@@ -43,10 +44,8 @@ class SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
public framing::FrameHandler::InOutHandler
{
public:
- typedef framing::AMQP_AllProxy::Session Peer;
-
- SessionHandler(framing::FrameHandler* out=0, uint16_t channel=0);
- ~SessionHandler();
+ 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(); }
@@ -57,58 +56,60 @@ class SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
virtual framing::FrameHandler* getInHandler() = 0;
// Non-protocol methods, called locally to initiate some action.
- void sendDetach();
- void sendCompletion();
- void sendAttach(bool force);
- void sendTimeout(uint32_t t);
- void sendFlush();
+ 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
/** True if the handler is ready to send and receive */
bool ready() const;
// Protocol methods
- 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 code);
-
- void requestTimeout(uint32_t t);
- void timeout(uint32_t t);
-
- void commandPoint(const framing::SequenceNumber& id, uint64_t offset);
- 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);
+ 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:
- virtual void invoke(const framing::AMQMethodBody& m);
+ QPID_COMMON_EXTERN virtual void invoke(const framing::AMQMethodBody& m);
virtual void setState(const std::string& sessionName, bool force) = 0;
- virtual void channelException(uint16_t code, const std::string& msg) = 0;
- virtual void connectionException(uint16_t code, const std::string& msg) = 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() {}
- virtual void handleDetach();
- virtual void handleIn(framing::AMQFrame&);
- virtual void handleOut(framing::AMQFrame&);
-
- void checkAttached();
- void checkName(const std::string& name);
+ 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;
- Peer peer;
- bool ignoring;
- bool sendReady, receiveReady;
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
diff --git a/cpp/src/qpid/amqp_0_10/Struct.h b/cpp/src/qpid/amqp_0_10/Struct.h
index c0cea09c60..29ece84f6e 100644
--- a/cpp/src/qpid/amqp_0_10/Struct.h
+++ b/cpp/src/qpid/amqp_0_10/Struct.h
@@ -22,7 +22,7 @@
*
*/
-#include "built_in_types.h"
+#include "qpid/amqp_0_10/built_in_types.h"
#include <iosfwd>
namespace qpid {
diff --git a/cpp/src/qpid/amqp_0_10/Struct32.cpp b/cpp/src/qpid/amqp_0_10/Struct32.cpp
index 541f02bcc4..2d38c09c21 100644
--- a/cpp/src/qpid/amqp_0_10/Struct32.cpp
+++ b/cpp/src/qpid/amqp_0_10/Struct32.cpp
@@ -18,7 +18,7 @@
*
*/
-#include "Struct32.h"
+#include "qpid/amqp_0_10/Struct32.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/Unit.cpp b/cpp/src/qpid/amqp_0_10/Unit.cpp
index 75ea1c1b30..381de76dcc 100644
--- a/cpp/src/qpid/amqp_0_10/Unit.cpp
+++ b/cpp/src/qpid/amqp_0_10/Unit.cpp
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "Unit.h"
-#include "Codec.h"
+#include "qpid/amqp_0_10/Unit.h"
+#include "qpid/amqp_0_10/Codec.h"
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/UnknownType.cpp b/cpp/src/qpid/amqp_0_10/UnknownType.cpp
index 844891d732..cd45dd76db 100644
--- a/cpp/src/qpid/amqp_0_10/UnknownType.cpp
+++ b/cpp/src/qpid/amqp_0_10/UnknownType.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "UnknownType.h"
+#include "qpid/amqp_0_10/UnknownType.h"
#include <boost/range/iterator_range.hpp>
#include <ostream>
diff --git a/cpp/src/qpid/amqp_0_10/UnknownType.h b/cpp/src/qpid/amqp_0_10/UnknownType.h
index 1e4aa04bf4..77498871b3 100644
--- a/cpp/src/qpid/amqp_0_10/UnknownType.h
+++ b/cpp/src/qpid/amqp_0_10/UnknownType.h
@@ -21,9 +21,9 @@
* under the License.
*
*/
+#include "qpid/sys/IntegerTypes.h"
#include <vector>
#include <iosfwd>
-#include <stdint.h>
namespace qpid {
namespace amqp_0_10 {
diff --git a/cpp/src/qpid/amqp_0_10/built_in_types.h b/cpp/src/qpid/amqp_0_10/built_in_types.h
index 7665d91736..e95d1cf3e9 100644
--- a/cpp/src/qpid/amqp_0_10/built_in_types.h
+++ b/cpp/src/qpid/amqp_0_10/built_in_types.h
@@ -23,15 +23,15 @@
#include "qpid/Serializer.h"
#include "qpid/framing/Uuid.h"
+#include "qpid/sys/IntegerTypes.h"
#include "qpid/sys/Time.h"
-#include "Decimal.h"
-#include "SerializableString.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>
-#include <stdint.h>
/**@file Mapping from built-in AMQP types to C++ types */
@@ -145,7 +145,6 @@ typedef framing::SequenceSet SequenceSet;
// Forward declare class types.
class Map;
class Struct32;
-class List;
class UnknownType;
template <class T> struct ArrayDomain;
diff --git a/cpp/src/qpid/assert.cpp b/cpp/src/qpid/assert.cpp
index 5d039da528..bf36d3be86 100644
--- a/cpp/src/qpid/assert.cpp
+++ b/cpp/src/qpid/assert.cpp
@@ -35,7 +35,7 @@ void assert_fail(char const * expr, char const * function, char const * file, lo
#ifdef NDEBUG
throw framing::InternalErrorException(msg.str());
#else
- std::cerr << msg << std::endl;
+ std::cerr << msg.str() << std::endl;
abort();
#endif
}
diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h
index f766978d18..2f4f7eaacc 100644
--- a/cpp/src/qpid/broker/AclModule.h
+++ b/cpp/src/qpid/broker/AclModule.h
@@ -21,20 +21,30 @@
*/
-
-#include "qpid/shared_ptr.h"
#include "qpid/RefCounted.h"
+#include <boost/shared_ptr.hpp>
#include <map>
+#include <set>
#include <string>
-
+#include <sstream>
namespace qpid {
-namespace acl{
-enum ObjectType {QUEUE,EXCHANGE,BROKER,LINK,ROUTE};
-enum Action {CONSUME,PUBLISH,CREATE,ACCESS,BIND,UNBIND,DELETE,PURGE,UPDATE};
-enum AclResult {ALLOW,ALLOWLOG,DENY,DENYNOLOG};
-}
+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 {
@@ -47,17 +57,225 @@ public:
// effienty turn off ACL on message transfer.
virtual bool doTransferAcl()=0;
- virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string name,
- std::map<std::string, std::string>* params)=0;
- virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string ExchangeName,
- std::string RoutingKey)=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::broker
+}} // namespace qpid::acl
#endif // QPID_ACLMODULE_ACL_H
diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp
index 53bed020e2..79e311d032 100644
--- a/cpp/src/qpid/broker/Bridge.cpp
+++ b/cpp/src/qpid/broker/Bridge.cpp
@@ -18,36 +18,62 @@
* under the License.
*
*/
-#include "Bridge.h"
-#include "ConnectionState.h"
-#include "LinkRegistry.h"
+#include "qpid/broker/Bridge.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/agent/ManagementAgent.h"
-#include "qpid/framing/FieldTable.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;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+namespace
+{
+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");
+}
namespace qpid {
namespace broker {
+void Bridge::PushHandler::handle(framing::AMQFrame& frame)
+{
+ conn->received(frame);
+}
+
Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l,
- const management::ArgsLinkBridge& _args) :
+ const _qmf::ArgsLinkBridge& _args) :
link(_link), id(_id), args(_args), mgmtObject(0),
- listener(l), name(Uuid(true).str()), persistenceId(0)
+ listener(l), name(Uuid(true).str()), queueName("bridge_queue_"), persistenceId(0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ std::stringstream title;
+ title << id << "_" << link->getBroker()->getFederationTag();
+ queueName += title.str();
+ ManagementAgent* agent = link->getBroker()->getManagementAgent();
if (agent != 0) {
- mgmtObject = new management::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);
+ 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);
if (!args.i_durable)
agent->addObject(mgmtObject);
}
+ QPID_LOG(debug, "Bridge created from " << args.i_src << " to " << args.i_dest);
}
Bridge::~Bridge()
@@ -55,62 +81,111 @@ Bridge::~Bridge()
mgmtObject->resourceDestroy();
}
-void Bridge::create(ConnectionState& c)
+void Bridge::create(Connection& c)
{
- channelHandler.reset(new framing::ChannelHandler(id, &(c.getOutput())));
- session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler));
- peer.reset(new framing::AMQP_ServerProxy(*channelHandler));
+ 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->attach(name, false);
- session->commandPoint(0,0);
+ 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) {
- //TODO: handle 'push' here... simplest way is to create frames and pass them to Connection::received()
+ 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 {
- if (args.i_srcIsQueue) {
- peer->getMessage().subscribe(args.i_src, args.i_dest, 1, 0, false, "", 0, FieldTable());
- peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
- peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
+ FieldTable queueSettings;
+
+ if (args.i_tag.size()) {
+ queueSettings.setString("qpid.trace.id", args.i_tag);
} else {
- string queue = "bridge_queue_";
- queue += Uuid(true).str();
- FieldTable queueSettings;
- if (args.i_tag.size()) {
- queueSettings.setString("qpid.trace.id", args.i_tag);
- }
- if (args.i_excludes.size()) {
- queueSettings.setString("qpid.trace.exclude", args.i_excludes);
- }
-
- bool durable = false;//should this be an arg, or would be use srcIsQueue for durable queues?
- bool autoDelete = !durable;//auto delete transient queues?
- peer->getQueue().declare(queue, "", false, durable, true, autoDelete, queueSettings);
- peer->getExchange().bind(queue, args.i_src, args.i_key, FieldTable());
- peer->getMessage().subscribe(queue, args.i_dest, 1, 0, false, "", 0, FieldTable());
- peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
- peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
+ 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()
+void Bridge::cancel(Connection& c)
{
+ if (args.i_srcIsLocal) {
+ //recreate peer to be sure that the session handler reference
+ //is valid (it could have been deleted due to a detach)
+ SessionHandler& sessionHandler = c.getChannel(id);
+ peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out));
+ }
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 id) const
+void Bridge::setPersistenceId(uint64_t pId) const
{
if (mgmtObject != 0 && persistenceId == 0) {
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
- agent->addObject (mgmtObject, id);
+ ManagementAgent* agent = link->getBroker()->getManagementAgent();
+ agent->addObject (mgmtObject, pId);
}
- persistenceId = id;
+ persistenceId = pId;
}
const string& Bridge::getName() const
@@ -138,9 +213,11 @@ Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer)
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).first;
+ is_queue, is_local, id, excludes, dynamic, sync).first;
}
void Bridge::encode(Buffer& buffer) const
@@ -156,6 +233,8 @@ void Bridge::encode(Buffer& buffer) const
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
@@ -170,7 +249,9 @@ uint32_t Bridge::encodedSize() const
+ 1 // srcIsQueue
+ 1 // srcIsLocal
+ args.i_tag.size() + 1
- + args.i_excludes.size() + 1;
+ + args.i_excludes.size() + 1
+ + 1 // dynamic
+ + 2; // sync
}
management::ManagementObject* Bridge::GetManagementObject (void) const
@@ -178,9 +259,11 @@ management::ManagementObject* Bridge::GetManagementObject (void) const
return (management::ManagementObject*) mgmtObject;
}
-management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, management::Args& /*args*/)
+management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId,
+ management::Args& /*args*/,
+ string&)
{
- if (methodId == management::Bridge::METHOD_CLOSE) {
+ if (methodId == _qmf::Bridge::METHOD_CLOSE) {
//notify that we are closed
destroy();
return management::Manageable::STATUS_OK;
@@ -189,4 +272,53 @@ management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, man
}
}
+void Bridge::propagateBinding(const string& key, const string& tagList,
+ const string& op, const string& origin)
+{
+ const string& localTag = link->getBroker()->getFederationTag();
+ const string& peerTag = connState->getFederationPeerTag();
+
+ if (tagList.find(peerTag) == tagList.npos) {
+ FieldTable bindArgs;
+ 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));
+}
+
+void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchange, const string& key, FieldTable args)
+{
+ peer->getExchange().bind(queue, exchange, key, args);
+}
+
+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/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h
index 06fba25268..7dae5c37a1 100644
--- a/cpp/src/qpid/broker/Bridge.h
+++ b/cpp/src/qpid/broker/Bridge.h
@@ -21,13 +21,16 @@
#ifndef _Bridge_
#define _Bridge_
-#include "PersistableConfig.h"
+#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/management/ArgsLinkBridge.h"
-#include "qpid/management/Bridge.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>
@@ -35,26 +38,31 @@
namespace qpid {
namespace broker {
+class Connection;
class ConnectionState;
class Link;
class LinkRegistry;
-class Bridge : public PersistableConfig, public management::Manageable
+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 management::ArgsLinkBridge& args);
+ Bridge(Link* link, framing::ChannelId id, CancellationListener l,
+ const qmf::org::apache::qpid::broker::ArgsLinkBridge& args);
~Bridge();
- void create(ConnectionState& c);
- void cancel();
+ 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);
+ management::Manageable::status_t ManagementMethod(uint32_t methodId,
+ management::Args& args,
+ std::string& text);
// PersistableConfig:
void setPersistenceId(uint64_t id) const;
@@ -64,18 +72,35 @@ public:
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);
+ void sendReorigin();
+ void ioThreadPropagateBinding(const string& queue, const string& exchange, const 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;
- management::ArgsLinkBridge args;
- management::Bridge* mgmtObject;
+ 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;
};
diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp
index 4d7c07649b..849bf6d1f5 100644
--- a/cpp/src/qpid/broker/Broker.cpp
+++ b/cpp/src/qpid/broker/Broker.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,23 +19,27 @@
*
*/
-#include "config.h"
-#include "Broker.h"
-#include "DirectExchange.h"
-#include "FanOutExchange.h"
-#include "HeadersExchange.h"
-#include "MessageStoreModule.h"
-#include "NullMessageStore.h"
-#include "RecoveryManagerImpl.h"
-#include "TopicExchange.h"
-#include "Link.h"
-
-#include "qpid/management/PackageQpid.h"
+#include "qpid/broker/Broker.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 "qmf/org/apache/qpid/broker/Package.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h"
#include "qpid/management/ManagementExchange.h"
-#include "qpid/management/ArgsBrokerEcho.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/Uuid.h"
#include "qpid/sys/ProtocolFactory.h"
#include "qpid/sys/Poller.h"
#include "qpid/sys/Dispatcher.h"
@@ -45,20 +49,14 @@
#include "qpid/sys/ConnectionInputHandlerFactory.h"
#include "qpid/sys/TimeoutHandler.h"
#include "qpid/sys/SystemInfo.h"
+#include "qpid/Address.h"
#include "qpid/Url.h"
+#include "qpid/Version.h"
#include <boost/bind.hpp>
#include <iostream>
#include <memory>
-#include <stdlib.h>
-
-#if HAVE_SASL
-#include <sasl/sasl.h>
-static const bool AUTH_DEFAULT=true;
-#else
-static const bool AUTH_DEFAULT=false;
-#endif
using qpid::sys::ProtocolFactory;
using qpid::sys::Poller;
@@ -66,11 +64,11 @@ using qpid::sys::Dispatcher;
using qpid::sys::Thread;
using qpid::framing::FrameHandler;
using qpid::framing::ChannelId;
-using qpid::management::ManagementBroker;
+using qpid::management::ManagementAgent;
using qpid::management::ManagementObject;
using qpid::management::Manageable;
using qpid::management::Args;
-using qpid::management::ArgsBrokerEcho;
+namespace _qmf = qmf::org::apache::qpid::broker;
namespace qpid {
namespace broker {
@@ -85,22 +83,26 @@ Broker::Options::Options(const std::string& name) :
stagingThreshold(5000000),
enableMgmt(1),
mgmtPubInterval(10),
- auth(AUTH_DEFAULT),
+ queueCleanInterval(60*10),//10 minutes
+ auth(SaslAuthenticator::available()),
realm("QPID"),
replayFlushLimit(0),
replayHardLimit(0),
queueLimit(100*1048576/*100M default limit*/),
- tcpNoDelay(false)
+ tcpNoDelay(false),
+ requireEncrypted(false),
+ maxSessionRate(0),
+ asyncQueueEvents(true)
{
int c = sys::SystemInfo::concurrency();
workerThreads=c+1;
- char *home = ::getenv("HOME");
+ std::string home = getHome();
- if (home == 0)
- dataDir += "/tmp";
+ if (home.length() == 0)
+ dataDir += DEFAULT_DATA_DIR_LOCATION;
else
dataDir += home;
- dataDir += "/.qpidd";
+ dataDir += DEFAULT_DATA_DIR_NAME;
addOptions()
("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker")
@@ -112,10 +114,16 @@ Broker::Options::Options(const std::string& name) :
("staging-threshold", optValue(stagingThreshold, "N"), "Stages messages over N bytes to disk")
("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management")
("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");
+ ("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)")
+ ("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");
}
const std::string empty;
@@ -124,79 +132,99 @@ 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),
- managementAgentSingleton(!config.enableMgmt),
- store(0),
- acl(0),
- dataDir(conf.noDataDir ? std::string () : conf.dataDir),
+ managementAgent(conf.enableMgmt ? new ManagementAgent() : 0),
+ store(new NullMessageStore),
+ acl(0),
+ dataDir(conf.noDataDir ? std::string() : conf.dataDir),
+ queues(this),
+ exchanges(this),
links(this),
- factory(*this),
+ factory(new SecureConnectionFactory(*this)),
+ dtxManager(timer),
sessionManager(
qpid::SessionState::Configuration(
conf.replayFlushLimit*1024, // convert kb to bytes.
conf.replayHardLimit*1024),
- *this)
+ *this),
+ queueCleaner(queues, timer),
+ queueEvents(poller,!conf.asyncQueueEvents),
+ recovery(true),
+ clusterUpdatee(false),
+ expiryPolicy(new ExpiryPolicy),
+ connectionCounter(conf.maxConnections),
+ getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this))
{
- if(conf.enableMgmt){
+ if (conf.enableMgmt) {
QPID_LOG(info, "Management enabled");
- managementAgent = managementAgentSingleton.getInstance();
- ((ManagementBroker*) managementAgent)->configure
- (dataDir.isEnabled () ? dataDir.getPath () : string (),
- conf.mgmtPubInterval, this, conf.workerThreads + 3);
- qpid::management::PackageQpid packageInitializer (managementAgent);
-
- System* system = new System (dataDir.isEnabled () ? dataDir.getPath () : string ());
- systemObject = System::shared_ptr (system);
-
- mgmtObject = new management::Broker (managementAgent, this, system, conf.port);
- mgmtObject->set_workerThreads (conf.workerThreads);
- mgmtObject->set_maxConns (conf.maxConnections);
- mgmtObject->set_connBacklog (conf.connectionBacklog);
- mgmtObject->set_stagingThreshold (conf.stagingThreshold);
- mgmtObject->set_mgmtPubInterval (conf.mgmtPubInterval);
- mgmtObject->set_version (PACKAGE_VERSION);
- mgmtObject->set_dataDirEnabled (dataDir.isEnabled ());
- mgmtObject->set_dataDir (dataDir.getPath ());
-
- managementAgent->addObject (mgmtObject, 2, 1);
+ managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(),
+ conf.mgmtPubInterval, this, conf.workerThreads + 3);
+ _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, conf.port);
+ mgmtObject->set_workerThreads(conf.workerThreads);
+ mgmtObject->set_maxConns(conf.maxConnections);
+ mgmtObject->set_connBacklog(conf.connectionBacklog);
+ mgmtObject->set_stagingThreshold(conf.stagingThreshold);
+ 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, 0x1000000000000002LL);
// 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);
- vhostObject = Vhost::shared_ptr (vhost);
-
- queues.setParent (vhost);
- exchanges.setParent (vhost);
- links.setParent (vhost);
+ 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);
+ queues.setQueueEvents(&queueEvents);
// Early-Initialize plugins
- const Plugin::Plugins& plugins=Plugin::getPlugins();
- for (Plugin::Plugins::const_iterator i = plugins.begin();
- i != plugins.end();
- i++)
- (*i)->earlyInitialize(*this);
+ Plugin::earlyInitAll(*this);
// If no plugin store module registered itself, set up the null store.
- if (store == 0)
- setStore (new NullMessageStore (false));
-
- queues.setStore (store);
- dtxManager.setStore (store);
- links.setStore (store);
+ if (NullMessageStore::isNullStore(store.get()))
+ setStore();
exchanges.declare(empty, DirectExchange::typeName); // Default exchange.
-
- if (store != 0) {
- RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager,
- conf.stagingThreshold);
- store->recover(recoverer);
+
+ 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,
+ conf.stagingThreshold);
+ 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)
@@ -209,9 +237,8 @@ Broker::Broker(const Broker::Options& conf) :
exchanges.declare(qpid_management, ManagementExchange::typeName);
Exchange::shared_ptr mExchange = exchanges.get (qpid_management);
Exchange::shared_ptr dExchange = exchanges.get (amq_direct);
- ((ManagementBroker*) managementAgent)->setExchange (mExchange, dExchange);
- dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent
- ((ManagementBroker*) managementAgent);
+ managementAgent->setExchange(mExchange, dExchange);
+ boost::dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent(managementAgent.get());
}
else
QPID_LOG(info, "Management not enabled");
@@ -220,29 +247,33 @@ Broker::Broker(const Broker::Options& conf) :
* SASL setup, can fail and terminate startup
*/
if (conf.auth) {
-#if HAVE_SASL
- int code = sasl_server_init(NULL, BROKER_SASL_NAME);
- 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));
- }
+ SaslAuthenticator::init(qpid::saslName);
QPID_LOG(info, "SASL enabled");
-#else
- throw Exception("Requested authentication but SASL unavailable");
-#endif
+ } else {
+ QPID_LOG(notice, "SASL disabled: No Authentication Performed");
}
// Initialize plugins
- for (Plugin::Plugins::const_iterator i = plugins.begin();
- i != plugins.end();
- i++)
- (*i)->initialize(*this);
+ Plugin::initializeAll(*this);
+
+ 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));
+ }
}
void Broker::declareStandardExchange(const std::string& name, const std::string& type)
{
- bool storeEnabled = store != NULL;
+ 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 ());
@@ -250,28 +281,32 @@ void Broker::declareStandardExchange(const std::string& name, const std::string&
}
-boost::intrusive_ptr<Broker> Broker::create(int16_t port)
+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)
+boost::intrusive_ptr<Broker> Broker::create(const Options& opts)
{
return boost::intrusive_ptr<Broker>(new Broker(opts));
}
-void Broker::setStore (MessageStore* _store)
+void Broker::setStore (boost::shared_ptr<MessageStore>& _store)
{
- assert (store == 0 && _store != 0);
- if (store == 0 && _store != 0)
- store = new MessageStoreModule (_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() {
- accept();
-
+ QPID_LOG(notice, "Broker running");
Dispatcher d(poller);
int numIOThreads = config.workerThreads;
std::vector<Thread> t(numIOThreads-1);
@@ -282,7 +317,7 @@ void Broker::run() {
// 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();
@@ -298,13 +333,10 @@ void Broker::shutdown() {
Broker::~Broker() {
shutdown();
+ queueEvents.shutdown();
finalize(); // Finalize any plugins.
- delete store;
- if (config.auth) {
-#if HAVE_SASL
- sasl_done();
-#endif
- }
+ if (config.auth)
+ SaslAuthenticator::fini();
QPID_LOG(notice, "Shut down");
}
@@ -319,7 +351,8 @@ Manageable* Broker::GetVhostObject(void) const
}
Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
- Args& args)
+ Args& args,
+ string&)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
@@ -327,27 +360,36 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
switch (methodId)
{
- case management::Broker::METHOD_ECHO :
+ case _qmf::Broker::METHOD_ECHO :
status = Manageable::STATUS_OK;
break;
- case management::Broker::METHOD_CONNECT : {
- management::ArgsBrokerConnect& hp=
- dynamic_cast<management::ArgsBrokerConnect&>(args);
-
- if (hp.i_useSsl)
- return Manageable::STATUS_FEATURE_NOT_IMPLEMENTED;
-
+ case _qmf::Broker::METHOD_CONNECT : {
+ _qmf::ArgsBrokerConnect& hp=
+ dynamic_cast<_qmf::ArgsBrokerConnect&>(args);
+
+ 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, hp.i_useSsl, hp.i_durable,
+ 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 management::Broker::METHOD_JOINCLUSTER :
- case management::Broker::METHOD_LEAVECLUSTER :
+ case _qmf::Broker::METHOD_QUEUEMOVEMESSAGES : {
+ _qmf::ArgsBrokerQueueMoveMessages& moveArgs=
+ dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args);
+ if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty))
+ status = Manageable::STATUS_OK;
+ else
+ return Manageable::STATUS_PARAMETER_INVALID;
+ break;
+ }
+ default:
status = Manageable::STATUS_NOT_IMPLEMENTED;
break;
}
@@ -355,34 +397,40 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
return status;
}
-boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory() const {
- assert(protocolFactories.size() > 0);
- return protocolFactories[0];
+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;
}
-void Broker::registerProtocolFactory(ProtocolFactory::shared_ptr protocolFactory) {
- protocolFactories.push_back(protocolFactory);
+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 << "'"));
+ }
}
-// TODO: This can only work if there is only one protocolFactory
-uint16_t Broker::getPort() const {
- return getProtocolFactory()->getPort();
+void Broker::registerProtocolFactory(const std::string& name, ProtocolFactory::shared_ptr protocolFactory) {
+ protocolFactories[name] = protocolFactory;
}
-// TODO: This should iterate over all protocolFactories
void Broker::accept() {
- for (unsigned int i = 0; i < protocolFactories.size(); ++i)
- protocolFactories[i]->accept(poller, &factory);
+ for (ProtocolFactoryMap::const_iterator i = protocolFactories.begin(); i != protocolFactories.end(); i++) {
+ i->second->accept(poller, factory.get());
+ }
}
-
-// TODO: How to chose the protocolFactory to use for the connection
void Broker::connect(
- const std::string& host, uint16_t port, bool /*useSsl*/,
+ const std::string& host, uint16_t port, const std::string& transport,
boost::function2<void, int, std::string> failed,
sys::ConnectionCodec::Factory* f)
{
- getProtocolFactory()->connect(poller, host, port, f ? f : &factory, failed);
+ 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(
@@ -391,11 +439,35 @@ void Broker::connect(
sys::ConnectionCodec::Factory* f)
{
url.throwIfEmpty();
- TcpAddress addr=boost::get<TcpAddress>(url[0]);
- connect(addr.host, addr.port, false, failed, f);
+ const TcpAddress* addr=url[0].get<TcpAddress>();
+ connect(addr->host, addr->port, TCP_TRANSPORT, 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;
+}
+
+const std::string Broker::TCP_TRANSPORT("tcp");
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h
index f7399c375f..b85aa7d96c 100644
--- a/cpp/src/qpid/broker/Broker.h
+++ b/cpp/src/qpid/broker/Broker.h
@@ -22,21 +22,25 @@
*
*/
-#include "ConnectionFactory.h"
-#include "ConnectionToken.h"
-#include "DirectExchange.h"
-#include "DtxManager.h"
-#include "ExchangeRegistry.h"
-#include "MessageStore.h"
-#include "QueueRegistry.h"
-#include "LinkRegistry.h"
-#include "SessionManager.h"
-#include "Vhost.h"
-#include "System.h"
+#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/ManagementBroker.h"
-#include "qpid/management/Broker.h"
-#include "qpid/management/ArgsBrokerConnect.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"
@@ -44,10 +48,13 @@
#include "qpid/framing/OutputHandler.h"
#include "qpid/framing/ProtocolInitiation.h"
#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Timer.h"
#include "qpid/RefCounted.h"
-#include "AclModule.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/sys/Mutex.h"
#include <boost/intrusive_ptr.hpp>
+#include <string>
#include <vector>
namespace qpid {
@@ -57,22 +64,34 @@ namespace sys {
class Poller;
}
-class Url;
+struct Url;
namespace broker {
+class ExpiryPolicy;
+
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 management::Manageable,
+ public RefCounted
{
- public:
+public:
struct Options : public qpid::Options {
- Options(const std::string& name="Broker 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;
@@ -83,45 +102,82 @@ class Broker : public sys::Runnable, public Plugin::Target,
uint64_t stagingThreshold;
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;
+ uint32_t maxSessionRate;
+ bool asyncQueueEvents;
+
+ 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 ();
+
boost::shared_ptr<sys::Poller> poller;
+ sys::Timer timer;
Options config;
- management::ManagementAgent::Singleton managementAgentSingleton;
- std::vector< boost::shared_ptr<sys::ProtocolFactory> > protocolFactories;
- MessageStore* store;
- AclModule* acl;
+ std::auto_ptr<management::ManagementAgent> managementAgent;
+ ProtocolFactoryMap protocolFactories;
+ std::auto_ptr<MessageStore> store;
+ AclModule* acl;
DataDir dataDir;
QueueRegistry queues;
ExchangeRegistry exchanges;
LinkRegistry links;
- ConnectionFactory factory;
+ boost::shared_ptr<sys::ConnectionCodec::Factory> factory;
DtxManager dtxManager;
SessionManager sessionManager;
- management::ManagementAgent* managementAgent;
- management::Broker* mgmtObject;
+ qmf::org::apache::qpid::broker::Broker* mgmtObject;
Vhost::shared_ptr vhostObject;
System::shared_ptr systemObject;
-
- void declareStandardExchange(const std::string& name, const std::string& type);
-
-
+ QueueCleaner queueCleaner;
+ QueueEvents queueEvents;
+ std::vector<Url> knownBrokers;
+ std::vector<Url> getKnownBrokersImpl();
+ std::string federationTag;
+ bool recovery;
+ bool clusterUpdatee;
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+ ConnectionCounter connectionCounter;
+
public:
-
-
virtual ~Broker();
- Broker(const Options& configuration);
- static boost::intrusive_ptr<Broker> create(const Options& configuration);
- static boost::intrusive_ptr<Broker> create(int16_t port = DEFAULT_PORT);
+ 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
@@ -129,7 +185,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
* port, which will be different if the configured port is
* 0.
*/
- virtual uint16_t getPort() const;
+ virtual uint16_t getPort(const std::string& name) const;
/**
* Run the broker. Implements Runnable::run() so the broker
@@ -140,7 +196,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
/** Shut down the broker */
virtual void shutdown();
- void setStore (MessageStore*);
+ QPID_BROKER_EXTERN void setStore (boost::shared_ptr<MessageStore>& store);
MessageStore& getStore() { return *store; }
void setAcl (AclModule* _acl) {acl = _acl;}
AclModule* getAcl() { return acl; }
@@ -151,21 +207,29 @@ class Broker : public sys::Runnable, public Plugin::Target,
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);
-
+ management::Manageable::status_t ManagementMethod (uint32_t methodId,
+ management::Args& args,
+ std::string& text);
+
/** Add to the broker's protocolFactorys */
- void registerProtocolFactory(boost::shared_ptr<sys::ProtocolFactory>);
+ void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>);
/** Accept connections */
- void accept();
+ QPID_BROKER_EXTERN void accept();
/** Create a connection to another broker. */
- void connect(const std::string& host, uint16_t port, bool useSsl,
+ void connect(const std::string& host, uint16_t port,
+ const std::string& transport,
boost::function2<void, int, std::string> failed,
sys::ConnectionCodec::Factory* =0);
/** Create a connection to another broker. */
@@ -173,16 +237,38 @@ class Broker : public sys::Runnable, public Plugin::Target,
boost::function2<void, int, std::string> failed,
sys::ConnectionCodec::Factory* =0);
- // TODO: There isn't a single ProtocolFactory so the use of the following needs to be fixed
- // For the present just return the first ProtocolFactory registered.
- boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory() const;
+ /** 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::Poller> getPoller();
+
+ boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; }
+ void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; }
+
+ sys::Timer& getTimer() { return 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; }
+
+ void setClusterUpdatee(bool set) { clusterUpdatee = set; }
+ bool isClusterUpdatee() const { return clusterUpdatee; }
+
+ management::ManagementAgent* getManagementAgent() { return managementAgent.get(); }
+
+ ConnectionCounter& getConnectionCounter() {return connectionCounter;}
};
}}
-
-
#endif /*!_Broker_*/
diff --git a/cpp/src/qpid/broker/BrokerImportExport.h b/cpp/src/qpid/broker/BrokerImportExport.h
new file mode 100644
index 0000000000..4edf8c9844
--- /dev/null
+++ b/cpp/src/qpid/broker/BrokerImportExport.h
@@ -0,0 +1,33 @@
+#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_BROKER_STATIC)
+#if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS)
+#define QPID_BROKER_EXTERN __declspec(dllexport)
+#else
+#define QPID_BROKER_EXTERN __declspec(dllimport)
+#endif
+#else
+#define QPID_BROKER_EXTERN
+#endif
+
+#endif
diff --git a/cpp/src/qpid/broker/BrokerSingleton.h b/cpp/src/qpid/broker/BrokerSingleton.h
deleted file mode 100644
index 22b707506b..0000000000
--- a/cpp/src/qpid/broker/BrokerSingleton.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef _broker_BrokerSingleton_h
-#define _broker_BrokerSingleton_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 "Broker.h"
-
-namespace qpid {
-namespace broker {
-
-/**
- * BrokerSingleton is a smart pointer to a process-wide singleton broker
- * started on an os-chosen port. The broker starts the first time
- * an instance of BrokerSingleton is created and runs untill the process exits.
- *
- * Useful for unit tests that want to share a broker between multiple
- * tests to reduce overhead of starting/stopping a broker for every test.
- *
- * Tests that need a new broker can create it directly.
- *
- * THREAD UNSAFE.
- */
-class BrokerSingleton : public boost::intrusive_ptr<Broker>
-{
- public:
- BrokerSingleton();
- ~BrokerSingleton();
- private:
- static boost::intrusive_ptr<Broker> broker;
-};
-
-}} // namespace qpid::broker
-
-
-
-#endif /*!_broker_BrokerSingleton_h*/
diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp
index ab18d1f035..17de83e033 100644
--- a/cpp/src/qpid/broker/Connection.cpp
+++ b/cpp/src/qpid/broker/Connection.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,14 +18,17 @@
* under the License.
*
*/
-#include "Connection.h"
-#include "SessionState.h"
-#include "Bridge.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Bridge.h"
+#include "qpid/broker/Broker.h"
#include "qpid/log/Statement.h"
#include "qpid/ptr_map.h"
#include "qpid/framing/AMQP_ClientProxy.h"
-#include "qpid/agent/ManagementAgent.h"
+#include "qpid/framing/enum.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>
@@ -34,30 +37,53 @@
#include <iostream>
#include <assert.h>
-using namespace boost;
using namespace qpid::sys;
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;
namespace qpid {
namespace broker {
-Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, bool isLink_) :
+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)),
+ 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 timed out: closing");
+ connection.abort();
+ }
+};
+
+Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, unsigned int ssf, bool isLink_, uint64_t objectId) :
ConnectionState(out_, broker_),
- receivedFn(boost::bind(&Connection::receivedImpl, this, _1)),
- closedFn(boost::bind(&Connection::closedImpl, this)),
- doOutputFn(boost::bind(&Connection::doOutputImpl, this)),
+ ssf(ssf),
adapter(*this, isLink_),
isLink(isLink_),
mgmtClosing(false),
mgmtId(mgmtId_),
mgmtObject(0),
- links(broker_.getLinks())
+ links(broker_.getLinks()),
+ agent(0),
+ timer(broker_.getTimer()),
+ errorListener(0),
+ shadow(false)
{
Manageable* parent = broker.GetVhostObject();
@@ -66,33 +92,48 @@ Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std
if (parent != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ agent = broker_.getManagementAgent();
- if (agent != 0)
- mgmtObject = new management::Connection(agent, this, parent, mgmtId, !isLink);
- agent->addObject(mgmtObject);
- }
- Plugin::initializeAll(*this); // Let plug-ins update extension points.
+ // TODO set last bool true if system connection
+ if (agent != 0) {
+ mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !isLink, false);
+ agent->addObject(mgmtObject, objectId, true);
+ }
+ ConnectionState::setUrl(mgmtId);
+ }
+ if (!isShadow()) broker.getConnectionCounter().inc_connectionCount();
}
void Connection::requestIOProcessing(boost::function0<void> callback)
{
- ioCallback = callback;
- out->activateOutput();
+ ScopedLock<Mutex> l(ioCallbackLock);
+ ioCallbacks.push(callback);
+ out.activateOutput();
}
Connection::~Connection()
{
- if (mgmtObject != 0)
+ if (mgmtObject != 0) {
mgmtObject->resourceDestroy();
+ if (!isLink)
+ 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) { receivedFn(frame); }
+void Connection::received(framing::AMQFrame& frame) {
+ // Received frame on connection so delay timeout
+ restartTimeout();
-void Connection::receivedImpl(framing::AMQFrame& frame){
if (frame.getChannel() == 0 && frame.getMethod()) {
adapter.handle(frame);
} else {
@@ -110,7 +151,7 @@ void Connection::recordFromServer(framing::AMQFrame& frame)
if (mgmtObject != 0)
{
mgmtObject->inc_framesToClient();
- mgmtObject->inc_bytesToClient(frame.size());
+ mgmtObject->inc_bytesToClient(frame.encodedSize());
}
}
@@ -119,7 +160,7 @@ void Connection::recordFromClient(framing::AMQFrame& frame)
if (mgmtObject != 0)
{
mgmtObject->inc_framesFromClient();
- mgmtObject->inc_bytesFromClient(frame.size());
+ mgmtObject->inc_bytesFromClient(frame.encodedSize());
}
}
@@ -156,29 +197,55 @@ void Connection::notifyConnectionForced(const string& text)
void Connection::setUserId(const string& userId)
{
ConnectionState::setUserId(userId);
- if (mgmtObject != 0)
+ if (mgmtObject != 0) {
mgmtObject->set_authIdentity(userId);
+ agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId));
+ }
}
-void Connection::close(
- ReplyCode code, const string& text, ClassId classId, MethodId methodId)
+void Connection::setFederationLink(bool b)
{
- adapter.close(code, text, classId, methodId);
+ 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() { closedFn(); }
-
-void Connection::closedImpl(){ // Physically closed, suspend open sessions.
+void Connection::closed(){ // Physically closed, suspend open sessions.
+ if (heartbeatTimer)
+ heartbeatTimer->cancel();
+ if (timeoutTimer)
+ timeoutTimer->cancel();
try {
- while (!channels.empty())
+ while (!channels.empty())
ptr_map_ptr(channels.begin())->handleDetach();
- // FIXME aconway 2008-07-15: exclusive is per-session not per-connection in 0-10.
while (!exclusiveQueues.empty()) {
Queue::shared_ptr q(exclusiveQueues.front());
q->releaseExclusiveOwnership();
@@ -195,27 +262,36 @@ void Connection::closedImpl(){ // Physically closed, suspend open sessions.
bool Connection::hasOutput() { return outputTasks.hasOutput(); }
-bool Connection::doOutput() { return doOutputFn(); }
-
-bool Connection::doOutputImpl() {
- try{
- if (ioCallback)
- ioCallback(); // Lend the IO thread for management processing
- ioCallback = 0;
-
- if (mgmtClosing)
- close(403, "Closed by Management Request", 0, 0);
- else
+bool Connection::doOutput() {
+ try {
+ {
+ ScopedLock<Mutex> l(ioCallbackLock);
+ while (!ioCallbacks.empty()) {
+ boost::function0<void> cb = ioCallbacks.front();
+ ioCallbacks.pop();
+ ScopedUnlock<Mutex> ul(ioCallbackLock);
+ cb(); // Lend the IO thread for management processing
+ }
+ }
+ if (mgmtClosing) {
+ closed();
+ close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request");
+ } else {
//then do other output as needed:
return outputTasks.doOutput();
+ }
}catch(ConnectionException& e){
- close(e.code, e.getMessage(), 0, 0);
+ close(e.code, e.getMessage());
}catch(std::exception& e){
- close(541/*internal error*/, e.what(), 0, 0);
+ 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);
@@ -234,7 +310,7 @@ ManagementObject* Connection::GetManagementObject(void) const
return (ManagementObject*) mgmtObject;
}
-Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&)
+Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
@@ -242,10 +318,10 @@ Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&)
switch (methodId)
{
- case management::Connection::METHOD_CLOSE :
+ case _qmf::Connection::METHOD_CLOSE :
mgmtClosing = true;
if (mgmtObject != 0) mgmtObject->set_closing(1);
- out->activateOutput();
+ out.activateOutput();
status = Manageable::STATUS_OK;
break;
}
@@ -253,5 +329,55 @@ Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&)
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)),
+ 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();
+}
+
+}}
diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h
index 1367f3b9ca..66ede59df5 100644
--- a/cpp/src/qpid/broker/Connection.h
+++ b/cpp/src/qpid/broker/Connection.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -25,52 +25,68 @@
#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_ServerOperations.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/ConnectionOutputHandler.h"
#include "qpid/sys/ConnectionInputHandler.h"
-#include "qpid/sys/TimeoutHandler.h"
-#include "qpid/framing/ProtocolVersion.h"
-#include "Broker.h"
+#include "qpid/sys/ConnectionOutputHandler.h"
#include "qpid/sys/Socket.h"
-#include "qpid/Exception.h"
-#include "ConnectionHandler.h"
-#include "ConnectionState.h"
-#include "SessionHandler.h"
-#include "qpid/management/Manageable.h"
-#include "qpid/management/Connection.h"
-#include "qpid/Plugin.h"
-#include "qpid/RefCounted.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,
+class Connection : public sys::ConnectionInputHandler,
public ConnectionState,
- public Plugin::Target,
public RefCounted
{
public:
- Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, bool isLink = false);
+ /**
+ * 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, unsigned int ssf,
+ bool isLink = false, uint64_t objectId = 0);
~Connection ();
/** Get the SessionHandler for channel. Create if it does not already exist */
SessionHandler& getChannel(framing::ChannelId channel);
/** Close the connection */
- void close(framing::ReplyCode code = 403,
- const string& text = string(),
- framing::ClassId classId = 0,
- framing::MethodId methodId = 0);
+ void close(framing::connection::CloseCode code, const string& text);
// ConnectionInputHandler methods
void received(framing::AMQFrame& frame);
@@ -85,7 +101,7 @@ class Connection : public sys::ConnectionInputHandler,
// Manageable entry points
management::ManagementObject* GetManagementObject (void) const;
management::Manageable::status_t
- ManagementMethod (uint32_t methodId, management::Args& args);
+ ManagementMethod (uint32_t methodId, management::Args& args, std::string&);
void requestIOProcessing (boost::function0<void>);
void recordFromServer (framing::AMQFrame& frame);
@@ -94,29 +110,60 @@ class Connection : public sys::ConnectionInputHandler,
std::string getAuthCredentials();
void notifyConnectionForced(const std::string& text);
void setUserId(const string& uid);
+ 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));
+ }
- // Extension points: allow plugins to insert additional functionality.
- boost::function<void(framing::AMQFrame&)> receivedFn;
- boost::function<void ()> closedFn;
- boost::function<bool ()> doOutputFn;
+ void sendClose();
+ void setSecureConnection(SecureConnection* secured);
+
+ /** True if this is a shadow connection in a cluster. */
+ bool isShadow() { return shadow; }
+ /** Called by cluster to mark shadow connections */
+ void setShadow() { shadow = true; }
+
+ // Used by cluster to update connection status
+ sys::AggregateOutput& getOutputTasks() { return outputTasks; }
+
+ unsigned int getSSF() { return ssf; }
private:
typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap;
typedef std::vector<Queue::shared_ptr>::iterator queue_iterator;
- void receivedImpl(framing::AMQFrame& frame);
- void closedImpl();
- bool doOutputImpl();
-
ChannelMap channels;
- framing::AMQP_ClientProxy::Connection* client;
+ unsigned int ssf;
ConnectionHandler adapter;
- bool isLink;
+ const bool isLink;
bool mgmtClosing;
const std::string mgmtId;
- boost::function0<void> ioCallback;
- management::Connection* mgmtObject;
+ 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;
+ bool shadow;
+
+ public:
+ qmf::org::apache::qpid::broker::Connection* getMgmtObject() { return mgmtObject; }
};
}}
diff --git a/cpp/src/qpid/broker/ConnectionFactory.cpp b/cpp/src/qpid/broker/ConnectionFactory.cpp
index 5de5a0230a..ffb0b34b95 100644
--- a/cpp/src/qpid/broker/ConnectionFactory.cpp
+++ b/cpp/src/qpid/broker/ConnectionFactory.cpp
@@ -18,30 +18,47 @@
* under the License.
*
*/
-#include "ConnectionFactory.h"
+#include "qpid/broker/ConnectionFactory.h"
#include "qpid/framing/ProtocolVersion.h"
#include "qpid/amqp_0_10/Connection.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
namespace qpid {
namespace broker {
using framing::ProtocolVersion;
+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) {
- if (v == ProtocolVersion(0, 10))
- return new amqp_0_10::Connection(out, broker, id);
+ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
+ unsigned int ) {
+ 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, false)));
+ return c.release();
+ }
return 0;
}
sys::ConnectionCodec*
-ConnectionFactory::create(sys::OutputControl& out, const std::string& id) {
+ConnectionFactory::create(sys::OutputControl& out, const std::string& id,
+ unsigned int) {
// used to create connections from one broker to another
- return new amqp_0_10::Connection(out, broker, id, true);
+ ConnectionPtr c(new amqp_0_10::Connection(out, id, true));
+ c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, true)));
+ return c.release();
}
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/ConnectionFactory.h b/cpp/src/qpid/broker/ConnectionFactory.h
index 5797495054..d812292ad1 100644
--- a/cpp/src/qpid/broker/ConnectionFactory.h
+++ b/cpp/src/qpid/broker/ConnectionFactory.h
@@ -27,17 +27,20 @@ namespace qpid {
namespace broker {
class Broker;
-class ConnectionFactory : public sys::ConnectionCodec::Factory {
+class ConnectionFactory : public sys::ConnectionCodec::Factory
+{
public:
ConnectionFactory(Broker& b);
virtual ~ConnectionFactory();
sys::ConnectionCodec*
- create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id);
+ create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id,
+ unsigned int conn_ssf);
sys::ConnectionCodec*
- create(sys::OutputControl&, const std::string& id);
+ create(sys::OutputControl&, const std::string& id,
+ unsigned int conn_ssf);
private:
Broker& broker;
diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp
index 77a4d1a3de..50a5aff2c9 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -8,9 +8,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -20,70 +20,91 @@
*
*/
-#include "config.h"
-
-#include "ConnectionHandler.h"
-#include "Connection.h"
-#include "qpid/framing/ClientInvoker.h"
-#include "qpid/framing/ServerInvoker.h"
-#include "qpid/framing/constants.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
+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;
}
-void ConnectionHandler::close(ReplyCode code, const string& text, ClassId, MethodId)
+void ConnectionHandler::close(connection::CloseCode code, const string& text)
{
- handler->client.close(code, 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{
- bool handled = false;
- if (handler->serverMode) {
- handled = invoke(static_cast<AMQP_ServerOperations::ConnectionHandler&>(*handler.get()), *method);
- } else {
- handled = invoke(static_cast<AMQP_ClientOperations::ConnectionHandler&>(*handler.get()), *method);
- }
- if (!handled) {
+ if (!invoke(static_cast<AMQP_AllOperations::ConnectionHandler&>(*handler.get()), *method)) {
handler->connection.getChannel(frame.getChannel()).in(frame);
}
-
}catch(ConnectionException& e){
- handler->client.close(e.code, e.what());
+ if (errorListener) errorListener->connectionError(e.what());
+ handler->proxy.close(e.code, e.what());
}catch(std::exception& e){
- handler->client.close(541/*internal error*/, e.what());
+ 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) : handler(new Handler(connection, isClient)) {}
ConnectionHandler::Handler::Handler(Connection& c, bool isClient) :
- client(c.getOutput()), server(c.getOutput()),
- connection(c), serverMode(!isClient)
+ proxy(c.getOutput()),
+ connection(c), serverMode(!isClient), acl(0), secured(0)
{
if (serverMode) {
+
+ acl = connection.getBroker().getAcl();
+
FieldTable properties;
Array mechanisms(0x95);
-
+
+ properties.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
+
authenticator = SaslAuthenticator::createAuthenticator(c);
authenticator->getMechanisms(mechanisms);
-
+
Array locales(0x95);
boost::shared_ptr<FieldValue> l(new Str16Value(en_US));
locales.add(l);
- client.start(properties, mechanisms, locales);
+ proxy.start(properties, mechanisms, locales);
}
}
@@ -91,65 +112,138 @@ ConnectionHandler::Handler::Handler(Connection& c, bool isClient) :
ConnectionHandler::Handler::~Handler() {}
-void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/,
- const string& mechanism,
+void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProperties,
+ const string& mechanism,
const string& response,
const string& /*locale*/)
{
- authenticator->start(mechanism, response);
+ 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)
{
- authenticator->step(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.setHeartbeat(heartbeat);
+ connection.setHeartbeatInterval(heartbeat);
}
-
+
void ConnectionHandler::Handler::open(const string& /*virtualHost*/,
const framing::Array& /*capabilities*/, bool /*insist*/)
{
- framing::Array knownhosts;
- client.openOk(knownhosts);
+ 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())));
+ proxy.openOk(array);
+
+ //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);
+ }
}
-
+
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::CONNECTION_FORCED)
+ if (replyCode == framing::connection::CLOSE_CODE_CONNECTION_FORCED)
connection.notifyConnectionForced(replyText);
- client.closeOk();
+ 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*/,
+void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
const framing::Array& /*mechanisms*/,
const framing::Array& /*locales*/)
{
string mechanism = connection.getAuthMechanism();
string response = connection.getAuthCredentials();
-
- server.startOk(FieldTable(), mechanism, response, en_US);
+
+ connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG));
+
+ FieldTable ft;
+ ft.setInt(QPID_FED_LINK,1);
+ ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
+ proxy.startOk(ft, mechanism, response, en_US);
}
void ConnectionHandler::Handler::secure(const string& /*challenge*/)
{
- server.secureOk("");
+ proxy.secureOk("");
}
void ConnectionHandler::Handler::tune(uint16_t channelMax,
@@ -159,15 +253,19 @@ void ConnectionHandler::Handler::tune(uint16_t channelMax,
{
connection.setFrameMax(frameMax);
connection.setHeartbeat(heartbeatMax);
- server.tuneOk(channelMax, frameMax, heartbeatMax);
- server.open("/", Array(), true);
+ proxy.tuneOk(channelMax, frameMax, heartbeatMax);
+ proxy.open("/", Array(), true);
}
-void ConnectionHandler::Handler::openOk(const framing::Array& /*knownHosts*/)
+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);
+ }
}
void ConnectionHandler::Handler::redirect(const string& /*host*/, const framing::Array& /*knownHosts*/)
{
-
+
}
diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h
index a04936a943..d74f65da36 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.h
+++ b/cpp/src/qpid/broker/ConnectionHandler.h
@@ -22,68 +22,71 @@
#define _ConnectionAdapter_
#include <memory>
-#include "SaslAuthenticator.h"
+#include "qpid/broker/SaslAuthenticator.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/AMQFrame.h"
-#include "qpid/framing/AMQP_ClientOperations.h"
-#include "qpid/framing/AMQP_ClientProxy.h"
-#include "qpid/framing/AMQP_ServerOperations.h"
-#include "qpid/framing/AMQP_ServerProxy.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"
namespace qpid {
namespace broker {
class Connection;
+class SecureConnection;
class ConnectionHandler : public framing::FrameHandler
{
- struct Handler : public framing::AMQP_ServerOperations::ConnectionHandler,
- public framing::AMQP_ClientOperations::ConnectionHandler
+ struct Handler : public framing::AMQP_AllOperations::ConnectionHandler
{
- framing::AMQP_ClientProxy::Connection client;
- framing::AMQP_ServerProxy::Connection server;
+ framing::AMQP_AllProxy::Connection proxy;
Connection& connection;
bool serverMode;
std::auto_ptr<SaslAuthenticator> authenticator;
-
+ AclModule* acl;
+ SecureConnection* secured;
+
Handler(Connection& connection, bool isClient);
~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() {}
+ 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();
+ 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);
+
+ void redirect(const std::string& host, const framing::Array& knownHosts);
};
std::auto_ptr<Handler> handler;
public:
ConnectionHandler(Connection& connection, bool isClient);
- void close(framing::ReplyCode code, const std::string& text, framing::ClassId classId, framing::MethodId methodId);
+ void close(framing::connection::CloseCode code, const std::string& text);
+ void heartbeat();
void handle(framing::AMQFrame& frame);
+ void setSecureConnection(SecureConnection* secured);
};
diff --git a/cpp/src/qpid/broker/ConnectionState.h b/cpp/src/qpid/broker/ConnectionState.h
index c9cf6ece8d..77ac5a59b0 100644
--- a/cpp/src/qpid/broker/ConnectionState.h
+++ b/cpp/src/qpid/broker/ConnectionState.h
@@ -24,61 +24,97 @@
#include <vector>
#include "qpid/sys/AggregateOutput.h"
-#include "qpid/sys/ConnectionOutputHandler.h"
+#include "qpid/sys/ConnectionOutputHandlerPtr.h"
#include "qpid/framing/ProtocolVersion.h"
#include "qpid/management/Manageable.h"
-#include "Broker.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) :
- broker(b),
- outputTasks(*o),
- out(o),
- framemax(65535),
+ ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) :
+ out(o),
+ broker(b),
+ outputTasks(out),
+ framemax(65535),
heartbeat(0),
- stagingThreshold(broker.getStagingThreshold())
- {}
-
-
+ heartbeatmax(120),
+ stagingThreshold(broker.getStagingThreshold()),
+ 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; }
uint64_t getStagingThreshold() const { return stagingThreshold; }
- void setFrameMax(uint32_t fm) { framemax = fm; }
+ 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; }
void setStagingThreshold(uint64_t st) { stagingThreshold = st; }
virtual void setUserId(const string& uid) { userId = uid; }
const string& getUserId() const { return userId; }
+
+ void setUrl(const string& _url) { url = _url; }
+ const string& getUrl() const { return url; }
+
+ void setFederationLink(bool b) { federationLink = b; }
+ bool isFederationLink() const { return federationLink; }
+ void setFederationPeerTag(const string& tag) { federationPeerTag = string(tag); }
+ const 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;
std::vector<Queue::shared_ptr> exclusiveQueues;
-
+
//contained output tasks
sys::AggregateOutput outputTasks;
- sys::ConnectionOutputHandler& getOutput() const { return *out; }
+ 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; }
- void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out = o; }
+ virtual void requestIOProcessing (boost::function0<void>) = 0;
protected:
framing::ProtocolVersion version;
- sys::ConnectionOutputHandler* out;
uint32_t framemax;
uint16_t heartbeat;
+ uint16_t heartbeatmax;
uint64_t stagingThreshold;
string userId;
+ string url;
+ bool federationLink;
+ string federationPeerTag;
+ std::vector<Url> knownHosts;
+ bool clientSupportsThrottling;
+ framing::FrameHandler* clusterOrderOut;
};
}}
diff --git a/cpp/src/qpid/broker/ConnectionToken.h b/cpp/src/qpid/broker/ConnectionToken.h
index 0e3b301897..9b40383c80 100644
--- a/cpp/src/qpid/broker/ConnectionToken.h
+++ b/cpp/src/qpid/broker/ConnectionToken.h
@@ -21,7 +21,7 @@
#ifndef _ConnectionToken_
#define _ConnectionToken_
-#include "OwnershipToken.h"
+#include "qpid/broker/OwnershipToken.h"
namespace qpid {
namespace broker {
/**
diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h
index 4274ce823e..b96443fa7c 100644
--- a/cpp/src/qpid/broker/Consumer.h
+++ b/cpp/src/qpid/broker/Consumer.h
@@ -21,47 +21,33 @@
#ifndef _Consumer_
#define _Consumer_
-namespace qpid {
- namespace broker {
- class Queue;
-}}
-
-#include "Message.h"
-#include "OwnershipToken.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/OwnershipToken.h"
namespace qpid {
- namespace broker {
-
- struct QueuedMessage
- {
- boost::intrusive_ptr<Message> payload;
- framing::SequenceNumber position;
- Queue* queue;
-
- QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) :
- payload(msg), position(sn), queue(q) {}
- QueuedMessage(Queue* q) : queue(q) {}
- };
-
+namespace broker {
+
+class Queue;
+
+class Consumer {
+ const bool acquires;
+ public:
+ typedef boost::shared_ptr<Consumer> shared_ptr;
+
+ framing::SequenceNumber position;
+
+ Consumer(bool preAcquires = true) : acquires(preAcquires) {}
+ 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(){}
+};
- class Consumer {
- const bool acquires;
- public:
- typedef shared_ptr<Consumer> ptr;
-
- framing::SequenceNumber position;
-
- Consumer(bool preAcquires = true) : acquires(preAcquires) {}
- 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(){}
- };
- }
-}
+}}
#endif
diff --git a/cpp/src/qpid/broker/Daemon.cpp b/cpp/src/qpid/broker/Daemon.cpp
index c311730f76..b30e5f18cb 100644
--- a/cpp/src/qpid/broker/Daemon.cpp
+++ b/cpp/src/qpid/broker/Daemon.cpp
@@ -15,10 +15,16 @@
* limitations under the License.
*
*/
-#include "Daemon.h"
+
+/*
+ * 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/LockFile.h"
+#include "qpid/sys/posix/PidFile.h"
#include <errno.h>
#include <fcntl.h>
@@ -31,7 +37,7 @@ namespace qpid {
namespace broker {
using namespace std;
-using qpid::sys::LockFile;
+using qpid::sys::PidFile;
Daemon::Daemon(std::string _pidDir) : pidDir(_pidDir) {
struct stat s;
@@ -85,12 +91,13 @@ void Daemon::fork()
child();
}
catch (const exception& e) {
- QPID_LOG(critical, "Daemon startup failed: " << e.what());
+ QPID_LOG(critical, "Unexpected error: " << e.what());
uint16_t port = 0;
- write(pipeFds[1], &port, sizeof(uint16_t));
+ int unused_ret; //Supress warning about ignoring return value.
+ unused_ret = write(pipeFds[1], &port, sizeof(uint16_t));
std::string pipeFailureMessage = e.what();
- write ( pipeFds[1],
+ unused_ret = write ( pipeFds[1],
pipeFailureMessage.c_str(),
strlen(pipeFailureMessage.c_str())
);
@@ -108,52 +115,61 @@ Daemon::~Daemon() {
}
uint16_t Daemon::wait(int timeout) { // parent waits for child.
- 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(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 write lock file "+lockFile);
+ 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;
}
-
- /*
- * 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;
}
@@ -166,25 +182,17 @@ uint16_t Daemon::wait(int timeout) { // parent waits for child.
*/
void Daemon::ready(uint16_t port) { // child
lockFile = pidFile(pidDir, port);
- LockFile lf(lockFile, true);
+ PidFile lf(lockFile, true);
/*
- * Rewritten using low-level IO, for compatibility
- * with earlier Boost versions, i.e. 103200.
- */
- /*
* Write the PID to the lockfile.
*/
- pid_t pid = getpid();
- int desired_write = sizeof(pid_t);
- if ( desired_write > ::write(lf.fd, & pid, desired_write) ) {
- throw Exception("Cannot write lock file "+lockFile);
- }
+ lf.writePid();
/*
* Write the port number to the parent.
*/
- desired_write = sizeof(uint16_t);
+ int desired_write = sizeof(uint16_t);
if ( desired_write > ::write(pipeFds[1], & port, desired_write) ) {
throw Exception("Error writing to parent." );
}
@@ -198,17 +206,8 @@ void Daemon::ready(uint16_t port) { // child
*/
pid_t Daemon::getPid(string _pidDir, uint16_t port) {
string name = pidFile(_pidDir, port);
- LockFile lf(name, false);
- pid_t pid;
-
- /*
- * Rewritten using low-level IO, for compatibility
- * with earlier Boost versions, i.e. 103200.
- */
- int desired_read = sizeof(pid_t);
- if ( desired_read > ::read(lf.fd, & pid, desired_read) ) {
- throw Exception("Cannot read lock file " + name);
- }
+ 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);
diff --git a/cpp/src/qpid/broker/Daemon.h b/cpp/src/qpid/broker/Daemon.h
index 98468debb7..a9cd98bce2 100644
--- a/cpp/src/qpid/broker/Daemon.h
+++ b/cpp/src/qpid/broker/Daemon.h
@@ -19,10 +19,12 @@
*
*/
-#include <string>
+#include "qpid/sys/IntegerTypes.h"
#include <boost/scoped_ptr.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
+#include <string>
+
namespace qpid {
namespace broker {
diff --git a/cpp/src/qpid/broker/Deliverable.h b/cpp/src/qpid/broker/Deliverable.h
index c40780c4ae..433469a212 100644
--- a/cpp/src/qpid/broker/Deliverable.h
+++ b/cpp/src/qpid/broker/Deliverable.h
@@ -21,8 +21,8 @@
#ifndef _Deliverable_
#define _Deliverable_
-#include "Queue.h"
-#include "Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Message.h"
namespace qpid {
namespace broker {
@@ -33,7 +33,7 @@ namespace qpid {
virtual Message& getMessage() = 0;
- virtual void deliverTo(Queue::shared_ptr& queue) = 0;
+ virtual void deliverTo(const boost::shared_ptr<Queue>& queue) = 0;
virtual uint64_t contentSize() { return 0; }
virtual ~Deliverable(){}
};
diff --git a/cpp/src/qpid/broker/DeliverableMessage.cpp b/cpp/src/qpid/broker/DeliverableMessage.cpp
index fd15acf464..658e6bf48f 100644
--- a/cpp/src/qpid/broker/DeliverableMessage.cpp
+++ b/cpp/src/qpid/broker/DeliverableMessage.cpp
@@ -18,15 +18,15 @@
* under the License.
*
*/
-#include "DeliverableMessage.h"
+#include "qpid/broker/DeliverableMessage.h"
using namespace qpid::broker;
-DeliverableMessage::DeliverableMessage(boost::intrusive_ptr<Message>& _msg) : msg(_msg)
+DeliverableMessage::DeliverableMessage(const boost::intrusive_ptr<Message>& _msg) : msg(_msg)
{
}
-void DeliverableMessage::deliverTo(Queue::shared_ptr& queue)
+void DeliverableMessage::deliverTo(const boost::shared_ptr<Queue>& queue)
{
queue->deliver(msg);
delivered = true;
diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h
index 18e1ec5e29..08abce35ef 100644
--- a/cpp/src/qpid/broker/DeliverableMessage.h
+++ b/cpp/src/qpid/broker/DeliverableMessage.h
@@ -21,9 +21,10 @@
#ifndef _DeliverableMessage_
#define _DeliverableMessage_
-#include "Deliverable.h"
-#include "Queue.h"
-#include "Message.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Message.h"
#include <boost/intrusive_ptr.hpp>
@@ -32,10 +33,10 @@ namespace qpid {
class DeliverableMessage : public Deliverable{
boost::intrusive_ptr<Message> msg;
public:
- DeliverableMessage(boost::intrusive_ptr<Message>& msg);
- virtual void deliverTo(Queue::shared_ptr& queue);
- Message& getMessage();
- uint64_t contentSize();
+ 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(){}
};
}
diff --git a/cpp/src/qpid/broker/DeliveryAdapter.h b/cpp/src/qpid/broker/DeliveryAdapter.h
index 4c2b2f615f..b0bec60890 100644
--- a/cpp/src/qpid/broker/DeliveryAdapter.h
+++ b/cpp/src/qpid/broker/DeliveryAdapter.h
@@ -21,30 +21,31 @@
#ifndef _DeliveryAdapter_
#define _DeliveryAdapter_
-#include "DeliveryId.h"
-#include "DeliveryToken.h"
-#include "Message.h"
+#include "qpid/broker/DeliveryId.h"
+#include "qpid/broker/Message.h"
#include "qpid/framing/amqp_types.h"
namespace qpid {
namespace broker {
- /**
- * 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 DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) = 0;
- virtual ~DeliveryAdapter(){}
- };
+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(){}
+};
}}
diff --git a/cpp/src/qpid/broker/DeliveryRecord.cpp b/cpp/src/qpid/broker/DeliveryRecord.cpp
index 530dca99a4..22ec5e86a0 100644
--- a/cpp/src/qpid/broker/DeliveryRecord.cpp
+++ b/cpp/src/qpid/broker/DeliveryRecord.cpp
@@ -18,91 +18,72 @@
* under the License.
*
*/
-#include "DeliveryRecord.h"
-#include "DeliverableMessage.h"
-#include "SemanticState.h"
-#include "Exchange.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/broker/Exchange.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,
- Queue::shared_ptr _queue,
- const std::string _tag,
- DeliveryToken::shared_ptr _token,
- const DeliveryId _id,
- bool _acquired, bool accepted) : msg(_msg),
- queue(_queue),
- tag(_tag),
- token(_token),
- id(_id),
- acquired(_acquired),
- pull(false),
- cancelled(false),
- credit(msg.payload ? msg.payload->getRequiredCredit() : 0),
- size(msg.payload ? msg.payload->contentSize() : 0),
- completed(false),
- ended(accepted)
-{
- if (accepted) setEnded();
-}
-
-DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg,
- Queue::shared_ptr _queue,
- const DeliveryId _id) : msg(_msg),
- queue(_queue),
- id(_id),
- acquired(true),
- pull(true),
- cancelled(false),
- credit(msg.payload ? msg.payload->getRequiredCredit() : 0),
- size(msg.payload ? msg.payload->contentSize() : 0),
- completed(false),
- ended(false)
+ 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)
{}
-void DeliveryRecord::setEnded()
+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);
-}
-
-bool DeliveryRecord::matches(DeliveryId tag) const{
- return id == tag;
-}
-
-bool DeliveryRecord::matchOrAfter(DeliveryId tag) const{
- return matches(tag) || after(tag);
-}
-
-bool DeliveryRecord::after(DeliveryId tag) const{
- return id > tag;
-}
-
-bool DeliveryRecord::coveredBy(const framing::SequenceSet* const range) const{
- return range->contains(id);
+ return isRedundant();
}
void DeliveryRecord::redeliver(SemanticState* const session) {
if (!ended) {
- if(pull || cancelled){
- //if message was originally sent as response to get, we must requeue it
-
- //or if subscription was cancelled, requeue it (waiting for
+ 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
- id = session->redeliver(msg, token);
+ 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);
+ }
+
+ 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) {
@@ -123,25 +104,29 @@ void DeliveryRecord::release(bool setRedelivered)
}
}
-void DeliveryRecord::complete()
-{
+void DeliveryRecord::complete() {
completed = true;
}
-void DeliveryRecord::accept(TransactionContext* ctxt) {
+bool DeliveryRecord::accept(TransactionContext* ctxt) {
if (acquired && !ended) {
- queue->dequeue(ctxt, msg.payload);
+ 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.payload);
+ queue->dequeue(ctxt, msg);
}
}
+void DeliveryRecord::committed() const{
+ queue->dequeueCommitted(msg);
+}
+
void DeliveryRecord::reject()
{
Exchange::shared_ptr alternate = queue->getAlternateExchange();
@@ -161,29 +146,14 @@ uint32_t DeliveryRecord::getCredit() const
return credit;
}
-
-void DeliveryRecord::addTo(Prefetch& prefetch) const{
- if(!pull){
- //ignore 'pulled' messages (i.e. those that were sent in
- //response to get) when calculating prefetch
- prefetch.size += size;
- prefetch.count++;
- }
-}
-
-void DeliveryRecord::subtractFrom(Prefetch& prefetch) const{
- if(!pull){
- //ignore 'pulled' messages (i.e. those that were sent in
- //response to get) when calculating prefetch
- prefetch.size -= size;
- prefetch.count--;
- }
-}
-
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());
}
@@ -195,6 +165,16 @@ void DeliveryRecord::cancel(const std::string& 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 {
@@ -206,9 +186,5 @@ std::ostream& operator<<(std::ostream& out, const DeliveryRecord& r)
return out;
}
-bool operator<(const DeliveryRecord& a, const DeliveryRecord& b)
-{
- return a.id < b.id;
-}
}}
diff --git a/cpp/src/qpid/broker/DeliveryRecord.h b/cpp/src/qpid/broker/DeliveryRecord.h
index 78dc99e3c6..5f802766b6 100644
--- a/cpp/src/qpid/broker/DeliveryRecord.h
+++ b/cpp/src/qpid/broker/DeliveryRecord.h
@@ -1,3 +1,6 @@
+#ifndef QPID_BROKER_DELIVERYRECORD_H
+#define QPID_BROKER_DELIVERYRECORD_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,53 +21,60 @@
* under the License.
*
*/
-#ifndef _DeliveryRecord_
-#define _DeliveryRecord_
#include <algorithm>
-#include <list>
+#include <deque>
#include <vector>
#include <ostream>
#include "qpid/framing/SequenceSet.h"
-#include "Queue.h"
-#include "Consumer.h"
-#include "DeliveryId.h"
-#include "DeliveryToken.h"
-#include "Message.h"
-#include "Prefetch.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/DeliveryId.h"
+#include "qpid/broker/Message.h"
namespace qpid {
namespace broker {
class SemanticState;
+struct AckRange;
/**
* Record of a delivery for which an ack is outstanding.
*/
-class DeliveryRecord{
+class DeliveryRecord
+{
QueuedMessage msg;
mutable Queue::shared_ptr queue;
- const std::string tag;
- DeliveryToken::shared_ptr token;
+ std::string tag;
DeliveryId id;
- bool acquired;
- const bool pull;
- bool cancelled;
- const uint32_t credit;
- const uint64_t size;
-
- bool completed;
- bool ended;
+ 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:
- DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const std::string tag, DeliveryToken::shared_ptr token,
- const DeliveryId id, bool acquired, bool confirmed = false);
- DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const DeliveryId id);
-
- bool matches(DeliveryId tag) const;
- bool matchOrAfter(DeliveryId tag) const;
- bool after(DeliveryId tag) const;
- bool coveredBy(const framing::SequenceSet* const range) const;
-
+ QPID_BROKER_EXTERN DeliveryRecord(const QueuedMessage& msg,
+ const Queue::shared_ptr& 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);
@@ -73,32 +83,37 @@ class DeliveryRecord{
void redeliver(SemanticState* const);
void acquire(DeliveryIds& results);
void complete();
- void accept(TransactionContext* ctxt);
- void setEnded();
+ 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 && 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;
- void addTo(Prefetch&) const;
- void subtractFrom(Prefetch&) const;
- const std::string& getTag() const { return tag; }
- bool isPull() const { return pull; }
- friend bool operator<(const DeliveryRecord&, const DeliveryRecord&);
- friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&);
-};
+ const std::string& getTag() const { return tag; }
-typedef std::list<DeliveryRecord> DeliveryRecords;
-typedef std::list<DeliveryRecord>::iterator ack_iterator;
+ void deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize);
+ void setId(DeliveryId _id) { id = _id; }
-struct AckRange
-{
- ack_iterator start;
- ack_iterator end;
- AckRange(ack_iterator _start, ack_iterator _end) : start(_start), end(_end) {}
+ 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; }
+ Queue::shared_ptr 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;
@@ -111,8 +126,17 @@ struct AcquireFunctor
}
};
+typedef DeliveryRecord::DeliveryRecords DeliveryRecords;
+
+struct AckRange
+{
+ DeliveryRecords::iterator start;
+ DeliveryRecords::iterator end;
+ AckRange(DeliveryRecords::iterator _start, DeliveryRecords::iterator _end) : start(_start), end(_end) {}
+};
+
}
}
-#endif
+#endif /*!QPID_BROKER_DELIVERYRECORD_H*/
diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp
index 4aa68bee9c..094f59cdec 100644
--- a/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/cpp/src/qpid/broker/DirectExchange.cpp
@@ -19,111 +19,142 @@
*
*/
#include "qpid/log/Statement.h"
-#include "DirectExchange.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;
-DirectExchange::DirectExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent)
+namespace
+{
+const std::string qpidFedOp("qpid.fed.op");
+const std::string qpidFedTags("qpid.fed.tags");
+const std::string qpidFedOrigin("qpid.fed.origin");
+const std::string qpidExclusiveBinding("qpid.exclusive-binding");
+
+const std::string fedOpBind("B");
+const std::string fedOpUnbind("U");
+const std::string fedOpReorigin("R");
+const std::string fedOpHello("H");
+}
+
+DirectExchange::DirectExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
{
if (mgmtExchange != 0)
- mgmtExchange->set_type (typeName);
+ mgmtExchange->set_type(typeName);
}
-DirectExchange::DirectExchange(const std::string& _name, bool _durable,
- const FieldTable& _args, Manageable* _parent) :
- Exchange(_name, _durable, _args, _parent)
+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);
+ mgmtExchange->set_type(typeName);
}
-bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable*){
- RWlock::ScopedWlock l(lock);
- std::vector<Binding::shared_ptr>& queues(bindings[routingKey]);
- std::vector<Binding::shared_ptr>::iterator i;
-
- for (i = queues.begin(); i != queues.end(); i++)
- if ((*i)->queue == queue)
- break;
-
- if (i == queues.end()) {
- Binding::shared_ptr binding (new Binding (routingKey, queue, this));
- bindings[routingKey].push_back(binding);
- if (mgmtExchange != 0) {
- mgmtExchange->inc_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->inc_bindingCount();
- }
- return true;
- } else{
- return false;
+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);
}
-}
-bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){
- RWlock::ScopedWlock l(lock);
- std::vector<Binding::shared_ptr>& queues(bindings[routingKey]);
- std::vector<Binding::shared_ptr>::iterator i;
+ bool propagate = false;
- for (i = queues.begin(); i != queues.end(); i++)
- if ((*i)->queue == queue)
- break;
+ 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();
- if (i < queues.end()) {
- queues.erase(i);
- if (queues.empty()) {
- bindings.erase(routingKey);
+ if (bk.queues.add_unless(b, MatchQueue(queue))) {
+ propagate = bk.fedBinding.addOrigin(fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ return false;
}
- if (mgmtExchange != 0) {
- mgmtExchange->dec_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->dec_bindingCount();
+ } else if (fedOp == fedOpUnbind) {
+ Mutex::ScopedLock l(lock);
+ BoundKey& bk = bindings[routingKey];
+ propagate = bk.fedBinding.delOrigin(fedOrigin);
+ if (bk.fedBinding.count() == 0)
+ unbind(queue, routingKey, 0);
+ } 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());
}
- return true;
- } else {
- return false;
}
+
+ routeIVE();
+ if (propagate)
+ propagateFedOp(routingKey, fedTags, fedOp, fedOrigin);
+ return true;
}
-void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){
- RWlock::ScopedRlock l(lock);
- std::vector<Binding::shared_ptr>& queues(bindings[routingKey]);
- std::vector<Binding::shared_ptr>::iterator i;
- int count(0);
-
- for(i = queues.begin(); i != queues.end(); i++, count++) {
- msg.deliverTo((*i)->queue);
- if ((*i)->mgmtBinding != 0)
- (*i)->mgmtBinding->inc_msgMatched ();
- }
-
- if(!count){
- QPID_LOG(warning, "DirectExchange " << getName() << " could not route message with key " << routingKey);
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgDrops ();
- mgmtExchange->inc_byteDrops (msg.contentSize ());
- }
- }
- else {
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgRoutes (count);
- mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
+bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/)
+{
+ bool propagate = false;
+
+ {
+ Mutex::ScopedLock l(lock);
+ BoundKey& bk = bindings[routingKey];
+ if (bk.queues.remove_if(MatchQueue(queue))) {
+ propagate = bk.fedBinding.delOrigin();
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ }
+ } else {
+ return false;
}
}
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgReceives ();
- mgmtExchange->inc_byteReceives (msg.contentSize ());
+ 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)
{
- std::vector<Binding::shared_ptr>::iterator j;
-
+ Mutex::ScopedLock l(lock);
if (routingKey) {
Bindings::iterator i = bindings.find(*routingKey);
@@ -131,17 +162,17 @@ bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routin
return false;
if (!queue)
return true;
- for (j = i->second.begin(); j != i->second.end(); j++)
- if ((*j)->queue == 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++)
- for (j = i->second.begin(); j != i->second.end(); j++)
- if ((*j)->queue == queue)
- return true;
+ 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;
}
diff --git a/cpp/src/qpid/broker/DirectExchange.h b/cpp/src/qpid/broker/DirectExchange.h
index 118f2ed4d3..9a73f3bc41 100644
--- a/cpp/src/qpid/broker/DirectExchange.h
+++ b/cpp/src/qpid/broker/DirectExchange.h
@@ -23,40 +23,53 @@
#include <map>
#include <vector>
-#include "Exchange.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
-#include "qpid/sys/Monitor.h"
-#include "Queue.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/Queue.h"
namespace qpid {
namespace broker {
- class DirectExchange : public virtual Exchange{
- typedef std::vector<Binding::shared_ptr> Queues;
- typedef std::map<string, Queues> Bindings;
- Bindings bindings;
- qpid::sys::RWlock lock;
-
- public:
- static const std::string typeName;
-
- DirectExchange(const std::string& name, management::Manageable* parent = 0);
- DirectExchange(const string& _name, bool _durable,
- const qpid::framing::FieldTable& _args, management::Manageable* parent = 0);
+class DirectExchange : public virtual Exchange {
+ typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Queues;
+ struct BoundKey {
+ Queues queues;
+ FedBinding fedBinding;
+ };
+ typedef std::map<string, BoundKey> Bindings;
+ Bindings bindings;
+ qpid::sys::Mutex lock;
- virtual std::string getType() const { return typeName; }
+public:
+ static const std::string 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);
+ QPID_BROKER_EXTERN DirectExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN DirectExchange(const string& _name,
+ bool _durable,
+ const qpid::framing::FieldTable& _args,
+ management::Manageable* parent = 0, Broker* broker = 0);
- virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ 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 string* const routingKey,
+ const qpid::framing::FieldTable* const args);
- virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args);
+ QPID_BROKER_EXTERN virtual ~DirectExchange();
- virtual ~DirectExchange();
- };
-}
-}
+ virtual bool supportsDynamicBinding() { return true; }
+};
+}}
#endif
diff --git a/cpp/src/qpid/broker/DtxAck.cpp b/cpp/src/qpid/broker/DtxAck.cpp
index 47637369ca..bca3f90bbe 100644
--- a/cpp/src/qpid/broker/DtxAck.cpp
+++ b/cpp/src/qpid/broker/DtxAck.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "DtxAck.h"
+#include "qpid/broker/DtxAck.h"
#include "qpid/log/Statement.h"
using std::bind1st;
@@ -26,7 +26,7 @@ using std::bind2nd;
using std::mem_fun_ref;
using namespace qpid::broker;
-DtxAck::DtxAck(const framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked)
+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)));
@@ -36,7 +36,7 @@ bool DtxAck::prepare(TransactionContext* ctxt) throw()
{
try{
//record dequeue in the store
- for (ack_iterator i = pending.begin(); i != pending.end(); i++) {
+ for (DeliveryRecords::iterator i = pending.begin(); i != pending.end(); i++) {
i->dequeue(ctxt);
}
return true;
@@ -48,11 +48,26 @@ bool DtxAck::prepare(TransactionContext* ctxt) throw()
void DtxAck::commit() throw()
{
- pending.clear();
+ 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()
{
- for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue));
- pending.clear();
+ 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/cpp/src/qpid/broker/DtxAck.h b/cpp/src/qpid/broker/DtxAck.h
index 05c4499839..166147e58d 100644
--- a/cpp/src/qpid/broker/DtxAck.h
+++ b/cpp/src/qpid/broker/DtxAck.h
@@ -25,20 +25,21 @@
#include <functional>
#include <list>
#include "qpid/framing/SequenceSet.h"
-#include "DeliveryRecord.h"
-#include "TxOp.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/TxOp.h"
namespace qpid {
namespace broker {
class DtxAck : public TxOp{
- std::list<DeliveryRecord> pending;
+ DeliveryRecords pending;
public:
- DtxAck(const framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked);
+ 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); }
};
}
}
diff --git a/cpp/src/qpid/broker/DtxBuffer.cpp b/cpp/src/qpid/broker/DtxBuffer.cpp
index 29a07ea6d9..f1b8169cf7 100644
--- a/cpp/src/qpid/broker/DtxBuffer.cpp
+++ b/cpp/src/qpid/broker/DtxBuffer.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "DtxBuffer.h"
+#include "qpid/broker/DtxBuffer.h"
using namespace qpid::broker;
using qpid::sys::Mutex;
diff --git a/cpp/src/qpid/broker/DtxBuffer.h b/cpp/src/qpid/broker/DtxBuffer.h
index b302632037..1511cb032f 100644
--- a/cpp/src/qpid/broker/DtxBuffer.h
+++ b/cpp/src/qpid/broker/DtxBuffer.h
@@ -21,7 +21,8 @@
#ifndef _DtxBuffer_
#define _DtxBuffer_
-#include "TxBuffer.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/TxBuffer.h"
#include "qpid/sys/Mutex.h"
namespace qpid {
@@ -37,9 +38,9 @@ namespace qpid {
public:
typedef boost::shared_ptr<DtxBuffer> shared_ptr;
- DtxBuffer(const std::string& xid = "");
- ~DtxBuffer();
- void markEnded();
+ 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();
diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp
index 942dbdcbc6..a2ab20ec44 100644
--- a/cpp/src/qpid/broker/DtxManager.cpp
+++ b/cpp/src/qpid/broker/DtxManager.cpp
@@ -18,10 +18,11 @@
* under the License.
*
*/
-#include "DtxManager.h"
-#include "DtxTimeout.h"
+#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>
@@ -33,7 +34,7 @@ using qpid::ptr_map_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
-DtxManager::DtxManager() : store(0) {}
+DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(t) {}
DtxManager::~DtxManager() {}
@@ -126,12 +127,11 @@ void DtxManager::setTimeout(const std::string& xid, uint32_t secs)
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->cancelled = true;
+ timeout->cancel();
}
timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid));
record->setTimeout(timeout);
- timer.add(boost::static_pointer_cast<TimerTask>(timeout));
-
+ timer.add(timeout);
}
uint32_t DtxManager::getTimeout(const std::string& xid)
@@ -160,13 +160,12 @@ void DtxManager::DtxCleanup::fire()
{
try {
mgr.remove(xid);
- } catch (ConnectionException& e) {
+ } catch (ConnectionException& /*e*/) {
//assume it was explicitly cleaned up after a call to prepare, commit or rollback
}
}
void DtxManager::setStore (TransactionalStore* _store)
{
- assert (store == 0 && _store != 0);
store = _store;
}
diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h
index fa5c62c233..680b62eeb2 100644
--- a/cpp/src/qpid/broker/DtxManager.h
+++ b/cpp/src/qpid/broker/DtxManager.h
@@ -22,11 +22,11 @@
#define _DtxManager_
#include <boost/ptr_container/ptr_map.hpp>
-#include "DtxBuffer.h"
-#include "DtxWorkRecord.h"
-#include "Timer.h"
-#include "TransactionalStore.h"
+#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 {
@@ -35,7 +35,7 @@ namespace broker {
class DtxManager{
typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap;
- struct DtxCleanup : public TimerTask
+ struct DtxCleanup : public sys::TimerTask
{
DtxManager& mgr;
const std::string& xid;
@@ -47,14 +47,14 @@ class DtxManager{
WorkMap work;
TransactionalStore* store;
qpid::sys::Mutex lock;
- Timer timer;
+ qpid::sys::Timer& timer;
void remove(const std::string& xid);
DtxWorkRecord* getWork(const std::string& xid);
DtxWorkRecord* createWork(std::string xid);
public:
- DtxManager();
+ 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);
diff --git a/cpp/src/qpid/broker/DtxTimeout.cpp b/cpp/src/qpid/broker/DtxTimeout.cpp
index 8e0a7741c4..f5238d0909 100644
--- a/cpp/src/qpid/broker/DtxTimeout.cpp
+++ b/cpp/src/qpid/broker/DtxTimeout.cpp
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "DtxTimeout.h"
-#include "DtxManager.h"
+#include "qpid/broker/DtxTimeout.h"
+#include "qpid/broker/DtxManager.h"
#include "qpid/sys/Time.h"
using namespace qpid::broker;
diff --git a/cpp/src/qpid/broker/DtxTimeout.h b/cpp/src/qpid/broker/DtxTimeout.h
index 6e949eab0d..680a210e4f 100644
--- a/cpp/src/qpid/broker/DtxTimeout.h
+++ b/cpp/src/qpid/broker/DtxTimeout.h
@@ -22,7 +22,7 @@
#define _DtxTimeout_
#include "qpid/Exception.h"
-#include "Timer.h"
+#include "qpid/sys/Timer.h"
namespace qpid {
namespace broker {
@@ -31,12 +31,12 @@ class DtxManager;
struct DtxTimeoutException : public Exception {};
-struct DtxTimeout : public TimerTask
+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();
};
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp
index cc79813dab..9f33e698db 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.cpp
+++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "DtxWorkRecord.h"
+#include "qpid/broker/DtxWorkRecord.h"
#include "qpid/framing/reply_exceptions.h"
#include <boost/format.hpp>
#include <boost/mem_fn.hpp>
@@ -34,7 +34,7 @@ DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const
DtxWorkRecord::~DtxWorkRecord()
{
if (timeout.get()) {
- timeout->cancelled = true;
+ timeout->cancel();
}
}
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.h b/cpp/src/qpid/broker/DtxWorkRecord.h
index 6677784c32..aec2d2aed4 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.h
+++ b/cpp/src/qpid/broker/DtxWorkRecord.h
@@ -21,9 +21,10 @@
#ifndef _DtxWorkRecord_
#define _DtxWorkRecord_
-#include "DtxBuffer.h"
-#include "DtxTimeout.h"
-#include "TransactionalStore.h"
+#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"
@@ -61,12 +62,13 @@ class DtxWorkRecord
void abort();
bool prepare(TransactionContext* txn);
public:
- DtxWorkRecord(const std::string& xid, TransactionalStore* const store);
- ~DtxWorkRecord();
- bool prepare();
- bool commit(bool onePhase);
- void rollback();
- void add(DtxBuffer::shared_ptr ops);
+ 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; }
diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp
index fbfcaede82..8efb9ac545 100644
--- a/cpp/src/qpid/broker/Exchange.cpp
+++ b/cpp/src/qpid/broker/Exchange.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,52 +19,157 @@
*
*/
-#include "Exchange.h"
-#include "ExchangeRegistry.h"
-#include "qpid/agent/ManagementAgent.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/MessageProperties.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/broker/DeliverableMessage.h"
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;
-Exchange::Exchange (const string& _name, Manageable* parent) :
- name(_name), durable(false), persistenceId(0), mgmtExchange(0)
+namespace
{
- if (parent != 0)
+const std::string qpidMsgSequence("qpid.msg_sequence");
+const std::string qpidSequenceCounter("qpid.sequence_counter");
+const std::string qpidIVE("qpid.ive");
+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");
+
+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();
+ }
+}
+
+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();
+
+ for(std::vector<Binding::shared_ptr>::const_iterator i = b->begin(); i != b->end(); i++, count++) {
+ msg.deliverTo((*i)->queue);
+ if ((*i)->mgmtBinding != 0)
+ (*i)->mgmtBinding->inc_msgMatched();
+ }
+ }
+
+ 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)
+{
+ if (parent != 0 && broker != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new management::Exchange (agent, this, parent, _name, durable);
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
agent->addObject (mgmtExchange);
}
}
}
Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args,
- Manageable* parent)
- : name(_name), durable(_durable), args(_args), alternateUsers(0), persistenceId(0), mgmtExchange(0)
+ Manageable* parent, Broker* b)
+ : name(_name), durable(_durable), alternateUsers(0), persistenceId(0),
+ args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b)
{
- if (parent != 0)
+ if (parent != 0 && broker != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtExchange = new management::Exchange (agent, this, parent, _name, durable);
+ mgmtExchange = new _qmf::Exchange (agent, this, parent, _name);
+ mgmtExchange->set_durable(durable);
+ mgmtExchange->set_autoDelete(false);
+ mgmtExchange->set_arguments(args);
if (!durable) {
- if (name == "")
- agent->addObject (mgmtExchange, 4, 1); // Special default exchange ID
- else if (name == "qpid.management")
- agent->addObject (mgmtExchange, 5, 1); // Special management exchange ID
- else
- agent->addObject (mgmtExchange);
+ if (name.empty()) {
+ agent->addObject (mgmtExchange, 0x1000000000000004LL); // Special default exchange ID
+ } else if (name == QPID_MANAGEMENT) {
+ agent->addObject (mgmtExchange, 0x1000000000000005LL); // Special management exchange ID
+ } else {
+ agent->addObject (mgmtExchange, agent->allocateId(this));
+ }
}
}
}
+
+ 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) QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value");
}
Exchange::~Exchange ()
@@ -73,12 +178,23 @@ Exchange::~Exchange ()
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
{
if (mgmtExchange != 0 && persistenceId == 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
- agent->addObject (mgmtExchange, id, 2);
+ ManagementAgent* agent = broker->getManagementAgent();
+ agent->addObject (mgmtExchange, 0x2000000000000000LL + id);
}
persistenceId = id;
}
@@ -87,30 +203,58 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe
{
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);
- return exchanges.declare(name, type, durable, args).first;
+ 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
+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
-{
+uint32_t Exchange::encodedSize() const
+{
return name.size() + 1/*short string size*/
+ 1 /*durable*/
+ getType().size() + 1/*short string size*/
- + args.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
@@ -118,30 +262,85 @@ 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)
+{
+ 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);
+}
+
Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* parent,
- FieldTable _args)
+ FieldTable _args, const string& origin)
: queue(_queue), key(_key), args(_args), mgmtBinding(0)
{
if (parent != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
- if (agent != 0)
- {
- ManagementObject* mo = queue->GetManagementObject();
- if (mo != 0)
- {
- uint64_t queueId = mo->getObjectId();
- mgmtBinding = new management::Binding (agent, this, (Manageable*) parent, queueId, key, args);
- agent->addObject (mgmtBinding);
- }
+ 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, args);
+ if (!origin.empty())
+ mgmtBinding->set_origin(origin);
+ agent->addObject (mgmtBinding, agent->allocateId(this));
+ static_cast<_qmf::Queue*>(mo)->inc_bindingCount();
+ }
+ }
}
}
}
Exchange::Binding::~Binding ()
{
- if (mgmtBinding != 0)
+ if (mgmtBinding != 0) {
+ ManagementObject* mo = queue->GetManagementObject();
+ if (mo != 0)
+ static_cast<_qmf::Queue*>(mo)->dec_bindingCount();
mgmtBinding->resourceDestroy ();
+ }
}
ManagementObject* Exchange::Binding::GetManagementObject () const
@@ -149,7 +348,13 @@ ManagementObject* Exchange::Binding::GetManagementObject () const
return (ManagementObject*) mgmtBinding;
}
-Manageable::status_t Exchange::Binding::ManagementMethod (uint32_t, Args&)
+Exchange::MatchQueue::MatchQueue(Queue::shared_ptr q) : queue(q) {}
+
+bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b)
{
- return Manageable::STATUS_UNKNOWN_METHOD;
+ return b->queue == queue;
+}
+
+void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) {
+ msg->getProperties<DeliveryProperties>()->setExchange(getName());
}
diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h
index f4ac4373e4..d630f7ae24 100644
--- a/cpp/src/qpid/broker/Exchange.h
+++ b/cpp/src/qpid/broker/Exchange.h
@@ -23,87 +23,171 @@
*/
#include <boost/shared_ptr.hpp>
-#include "Deliverable.h"
-#include "Queue.h"
-#include "MessageStore.h"
-#include "PersistableExchange.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Queue.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 "qpid/management/Exchange.h"
-#include "qpid/management/Binding.h"
+#include "qmf/org/apache/qpid/broker/Exchange.h"
+#include "qmf/org/apache/qpid/broker/Binding.h"
namespace qpid {
- namespace broker {
- using std::string;
- class ExchangeRegistry;
-
- class Exchange : public PersistableExchange, public management::Manageable {
- private:
- const string name;
- const bool durable;
- qpid::framing::FieldTable args;
- boost::shared_ptr<Exchange> alternate;
- uint32_t alternateUsers;
- mutable uint64_t persistenceId;
-
- protected:
- struct Binding : public management::Manageable {
- typedef boost::shared_ptr<Binding> shared_ptr;
- typedef std::vector<Binding::shared_ptr> vector;
-
- Queue::shared_ptr queue;
- const std::string key;
- const framing::FieldTable args;
- management::Binding* mgmtBinding;
-
- Binding(const std::string& key, Queue::shared_ptr queue, Exchange* parent = 0,
- framing::FieldTable args = framing::FieldTable ());
- ~Binding ();
- management::ManagementObject* GetManagementObject () const;
- management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args);
- };
-
- management::Exchange* mgmtExchange;
-
- public:
- typedef boost::shared_ptr<Exchange> shared_ptr;
-
- explicit Exchange(const string& name, management::Manageable* parent = 0);
- Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args,
- management::Manageable* parent = 0);
- virtual ~Exchange();
-
- const 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) { alternate = _alternate; }
- void incAlternateUsers() { alternateUsers++; }
- void decAlternateUsers() { alternateUsers--; }
- bool inUseAsAlternate() { return alternateUsers > 0; }
-
- virtual string getType() const = 0;
- virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0;
- virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0;
- virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args) = 0;
- virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args) = 0;
-
- //PersistableExchange:
- void setPersistenceId(uint64_t id) const;
- uint64_t getPersistenceId() const { return persistenceId; }
- uint32_t encodedSize() const;
- void encode(framing::Buffer& buffer) const;
-
- static Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer);
-
- // Manageable entry points
- management::ManagementObject* GetManagementObject (void) const;
- management::Manageable::status_t
- ManagementMethod (uint32_t, management::Args&) { return management::Manageable::STATUS_UNKNOWN_METHOD; }
- };
- }
-}
-
+namespace broker {
+
+class ExchangeRegistry;
+
+class 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;
+
+ Queue::shared_ptr queue;
+ const std::string key;
+ const framing::FieldTable args;
+ qmf::org::apache::qpid::broker::Binding* mgmtBinding;
+
+ Binding(const std::string& key, Queue::shared_ptr queue, Exchange* parent = 0,
+ framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string());
+ ~Binding();
+ 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 Queue::shared_ptr queue;
+ MatchQueue(Queue::shared_ptr q);
+ bool operator()(Exchange::Binding::shared_ptr b);
+ };
+
+ class FedBinding {
+ uint32_t localBindings;
+ std::set<std::string> originSet;
+ public:
+ FedBinding() : localBindings(0) {}
+ bool hasLocal() const { return localBindings != 0; }
+ bool addOrigin(const std::string& origin) {
+ if (origin.empty()) {
+ localBindings++;
+ return localBindings == 1;
+ }
+ originSet.insert(origin);
+ return true;
+ }
+ bool delOrigin(const std::string& origin) {
+ originSet.erase(origin);
+ return true;
+ }
+ bool delOrigin() {
+ if (localBindings > 0)
+ localBindings--;
+ return localBindings == 0;
+ }
+ uint32_t count() {
+ return localBindings + originSet.size();
+ }
+ };
+
+ 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_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;
+ virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
+ virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
+ virtual bool isBound(Queue::shared_ptr 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) = 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);
+
+protected:
+ qpid::sys::Mutex bridgeLock;
+ std::vector<DynamicBridge*> bridgeVector;
+ Broker* broker;
+
+ QPID_BROKER_EXTERN virtual void handleHelloRequest();
+ void propagateFedOp(const std::string& routingKey, const std::string& tags,
+ const std::string& op, const std::string& origin);
+};
+
+}}
#endif /*!_broker_Exchange.cpp_h*/
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp
index 45eb308680..951cdbd395 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.cpp
+++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp
@@ -19,15 +19,11 @@
*
*/
-#include "config.h"
-#include "ExchangeRegistry.h"
-#include "DirectExchange.h"
-#include "FanOutExchange.h"
-#include "HeadersExchange.h"
-#include "TopicExchange.h"
-#ifdef HAVE_XML
-#include "XmlExchange.h"
-#endif
+#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/ManagementExchange.h"
#include "qpid/framing/reply_exceptions.h"
@@ -36,42 +32,35 @@ using namespace qpid::sys;
using std::pair;
using qpid::framing::FieldTable;
-pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type)
- throw(UnknownExchangeTypeException){
+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)
- throw(UnknownExchangeTypeException){
+ 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));
+ 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));
+ 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));
+ 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));
+ exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker));
}else if (type == ManagementExchange::typeName) {
- exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent));
+ exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent, broker));
}
-#ifdef HAVE_XML
- else if (type == XmlExchange::typeName) {
- exchange = Exchange::shared_ptr(new XmlExchange(name, durable, args, parent));
- }
-#endif
else{
FunctionMap::iterator i = factory.find(type);
if (i == factory.end()) {
throw UnknownExchangeTypeException();
} else {
- exchange = i->second(name, durable, args, parent);
+ exchange = i->second(name, durable, args, parent, broker);
}
}
exchanges[name] = exchange;
@@ -82,6 +71,11 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c
}
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()) {
@@ -97,6 +91,10 @@ Exchange::shared_ptr ExchangeRegistry::get(const string& 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;
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h
index 7573e3e415..2b75a8f3cf 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.h
+++ b/cpp/src/qpid/broker/ExchangeRegistry.h
@@ -22,51 +22,72 @@
*
*/
-#include <map>
-#include <boost/function.hpp>
-#include "Exchange.h"
-#include "MessageStore.h"
+#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::function4<Exchange::shared_ptr, const std::string&,
- bool, const qpid::framing::FieldTable&, qpid::management::Manageable*> FactoryFunction;
-
- ExchangeRegistry () : parent(0) {}
- std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type)
- throw(UnknownExchangeTypeException);
- std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type,
- bool durable, const qpid::framing::FieldTable& args = framing::FieldTable())
- throw(UnknownExchangeTypeException);
- void destroy(const std::string& name);
- 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; }
-
- void registerType(const std::string& type, FactoryFunction);
- private:
- typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap;
- typedef std::map<std::string, FactoryFunction > FunctionMap;
-
- ExchangeMap exchanges;
- FunctionMap factory;
- qpid::sys::RWlock lock;
- management::Manageable* parent;
-
- };
-}
-}
+
+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/cpp/src/qpid/client/AsyncSession.h b/cpp/src/qpid/broker/ExpiryPolicy.cpp
index 150aabe191..64a12d918a 100644
--- a/cpp/src/qpid/client/AsyncSession.h
+++ b/cpp/src/qpid/broker/ExpiryPolicy.cpp
@@ -1,6 +1,3 @@
-#ifndef QPID_CLIENT_ASYNCSESSION_H
-#define QPID_CLIENT_ASYNCSESSION_H
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,18 +18,21 @@
* under the License.
*
*/
-#include "qpid/client/AsyncSession_0_10.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/broker/Message.h"
+#include "qpid/sys/Time.h"
namespace qpid {
-namespace client {
+namespace broker {
-/**
- * AsyncSession is an alias for Session_0_10
- *
- * \ingroup clientapi
- */
-typedef AsyncSession_0_10 AsyncSession;
+ExpiryPolicy::~ExpiryPolicy() {}
+
+void ExpiryPolicy::willExpire(Message&) {}
+
+bool ExpiryPolicy::hasExpired(Message& m) {
+ return m.getExpiration() < sys::AbsTime::now();
+}
-}} // namespace qpid::client
+void ExpiryPolicy::forget(Message&) {}
-#endif /*!QPID_CLIENT_ASYNCSESSION_H*/
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/ExpiryPolicy.h b/cpp/src/qpid/broker/ExpiryPolicy.h
new file mode 100644
index 0000000000..40e793bf2c
--- /dev/null
+++ b/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 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/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp
index 373e9ab1cc..6d840b50df 100644
--- a/cpp/src/qpid/broker/FanOutExchange.cpp
+++ b/cpp/src/qpid/broker/FanOutExchange.cpp
@@ -18,106 +18,102 @@
* under the License.
*
*/
-#include "FanOutExchange.h"
+#include "qpid/broker/FanOutExchange.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) :
- Exchange(_name, _parent)
+namespace
+{
+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");
+}
+
+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) :
- Exchange(_name, _durable, _args, _parent)
+ 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& /*routingKey*/, const FieldTable* /*args*/){
- RWlock::ScopedWlock locker(lock);
- std::vector<Binding::shared_ptr>::iterator i;
-
- // Add if not already present.
- for (i = bindings.begin (); i != bindings.end(); i++)
- if ((*i)->queue == queue)
- break;
-
- if (i == bindings.end()) {
- Binding::shared_ptr binding (new Binding ("", queue, this));
- bindings.push_back(binding);
- if (mgmtExchange != 0) {
- mgmtExchange->inc_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->inc_bindingCount();
+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))) {
+ propagate = fedBinding.addOrigin(fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ } else {
+ return false;
+ }
+ } else if (fedOp == fedOpUnbind) {
+ propagate = fedBinding.delOrigin(fedOrigin);
+ if (fedBinding.count() == 0)
+ unbind(queue, "", 0);
+ } else if (fedOp == fedOpReorigin) {
+ if (fedBinding.hasLocal()) {
+ propagateFedOp(string(), string(), fedOpBind, string());
}
- return true;
- } else {
- return false;
}
-}
-bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){
- RWlock::ScopedWlock locker(lock);
- std::vector<Binding::shared_ptr>::iterator i;
+ routeIVE();
+ if (propagate)
+ propagateFedOp(string(), fedTags, fedOp, fedOrigin);
+ return true;
+}
- for (i = bindings.begin (); i != bindings.end(); i++)
- if ((*i)->queue == queue)
- break;
+bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* /*args*/)
+{
+ bool propagate = false;
- if (i != bindings.end()) {
- bindings.erase(i);
+ if (bindings.remove_if(MatchQueue(queue))) {
+ propagate = fedBinding.delOrigin();
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->dec_bindingCount();
}
- return true;
} else {
return false;
}
-}
-
-void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){
- RWlock::ScopedRlock locker(lock);
- uint32_t count(0);
-
- for(std::vector<Binding::shared_ptr>::iterator i = bindings.begin(); i != bindings.end(); ++i, count++){
- msg.deliverTo((*i)->queue);
- if ((*i)->mgmtBinding != 0)
- (*i)->mgmtBinding->inc_msgMatched ();
- }
- if (mgmtExchange != 0)
- {
- mgmtExchange->inc_msgReceives ();
- mgmtExchange->inc_byteReceives (msg.contentSize ());
- if (count == 0)
- {
- mgmtExchange->inc_msgDrops ();
- mgmtExchange->inc_byteDrops (msg.contentSize ());
- }
- else
- {
- mgmtExchange->inc_msgRoutes (count);
- mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
- }
- }
+ 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)
{
- std::vector<Binding::shared_ptr>::iterator i;
-
- for (i = bindings.begin (); i != bindings.end(); i++)
- if ((*i)->queue == queue)
- break;
-
- return i != bindings.end();
+ BindingsArray::ConstPtr ptr = bindings.snapshot();
+ return ptr && std::find_if(ptr->begin(), ptr->end(), MatchQueue(queue)) != ptr->end();
}
diff --git a/cpp/src/qpid/broker/FanOutExchange.h b/cpp/src/qpid/broker/FanOutExchange.h
index 4bc92f6b28..7bcf6367cf 100644
--- a/cpp/src/qpid/broker/FanOutExchange.h
+++ b/cpp/src/qpid/broker/FanOutExchange.h
@@ -23,37 +23,47 @@
#include <map>
#include <vector>
-#include "Exchange.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
-#include "qpid/sys/Monitor.h"
-#include "Queue.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/broker/Queue.h"
namespace qpid {
namespace broker {
class FanOutExchange : public virtual Exchange {
- std::vector<Binding::shared_ptr> bindings;
- qpid::sys::RWlock lock;
-
+ typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> BindingsArray;
+ BindingsArray bindings;
+ FedBinding fedBinding;
public:
static const std::string typeName;
- FanOutExchange(const std::string& name, management::Manageable* parent = 0);
- FanOutExchange(const string& _name, bool _durable,
- const qpid::framing::FieldTable& _args,
- management::Manageable* parent = 0);
+ QPID_BROKER_EXTERN FanOutExchange(const std::string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN FanOutExchange(const 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);
+ 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);
- virtual void route(Deliverable& msg, 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);
- virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args);
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const string* const routingKey,
+ const qpid::framing::FieldTable* const args);
- virtual ~FanOutExchange();
+ QPID_BROKER_EXTERN virtual ~FanOutExchange();
+ virtual bool supportsDynamicBinding() { return true; }
};
}
diff --git a/cpp/src/qpid/broker/HandlerImpl.h b/cpp/src/qpid/broker/HandlerImpl.h
index 4c51e2a826..aae636e818 100644
--- a/cpp/src/qpid/broker/HandlerImpl.h
+++ b/cpp/src/qpid/broker/HandlerImpl.h
@@ -19,9 +19,9 @@
*
*/
-#include "SemanticState.h"
-#include "SessionContext.h"
-#include "ConnectionState.h"
+#include "qpid/broker/SemanticState.h"
+#include "qpid/broker/SessionContext.h"
+#include "qpid/broker/ConnectionState.h"
namespace qpid {
namespace broker {
diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp
index 54519a7bf6..38cc0e4050 100644
--- a/cpp/src/qpid/broker/HeadersExchange.cpp
+++ b/cpp/src/qpid/broker/HeadersExchange.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "HeadersExchange.h"
+#include "qpid/broker/HeadersExchange.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/log/Statement.h"
@@ -28,6 +28,7 @@
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.
@@ -42,16 +43,16 @@ namespace {
const std::string empty;
}
-HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent) :
- Exchange(_name, _parent)
+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) :
- Exchange(_name, _durable, _args, _parent)
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
{
if (mgmtExchange != 0)
mgmtExchange->set_type (typeName);
@@ -73,50 +74,26 @@ std::string HeadersExchange::getMatch(const FieldTable* args)
}
bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){
- RWlock::ScopedWlock locker(lock);
std::string what = getMatch(args);
if (what != all && what != any)
throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange."));
- Bindings::iterator i;
-
- for (i = bindings.begin(); i != bindings.end(); i++)
- if (i->first == *args && i->second->queue == queue)
- break;
-
- if (i == bindings.end()) {
- Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args));
- HeaderMap headerMap(*args, binding);
-
- bindings.push_back(headerMap);
+ Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args));
+ if (bindings.add_unless(binding, MatchArgs(queue, args))) {
if (mgmtExchange != 0) {
mgmtExchange->inc_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->inc_bindingCount();
}
+ routeIVE();
return true;
} else {
return false;
}
}
-bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){
- RWlock::ScopedWlock locker(lock);
- Bindings::iterator i;
- for (i = bindings.begin(); i != bindings.end(); i++) {
- if (bindingKey.empty() && args) {
- if (i->first == *args && i->second->queue == queue)
- break;
- } else {
- if (i->second->key == bindingKey && i->second->queue == queue)
- break;
- }
- }
-
- if (i != bindings.end()) {
- bindings.erase(i);
+bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable*){
+ if (bindings.remove_if(MatchKey(queue, bindingKey))) {
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->dec_bindingCount();
}
return true;
} else {
@@ -125,41 +102,43 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey,
}
-void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){
- if (!args) return;//can't match if there were no headers passed in
-
- RWlock::ScopedRlock locker(lock);
- uint32_t count(0);
-
- for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i, count++) {
- if (match(i->first, *args)) msg.deliverTo(i->second->queue);
- if (i->second->mgmtBinding != 0)
- i->second->mgmtBinding->inc_msgMatched ();
+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;
}
- if (mgmtExchange != 0)
+ PreRoute pr(msg, this);
+
+ ConstBindingList p = bindings.snapshot();
+ BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ if (p.get())
{
- mgmtExchange->inc_msgReceives ();
- mgmtExchange->inc_byteReceives (msg.contentSize ());
- if (count == 0)
- {
- mgmtExchange->inc_msgDrops ();
- mgmtExchange->inc_byteDrops (msg.contentSize ());
- }
- else
- {
- mgmtExchange->inc_msgRoutes (count);
- mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
+ for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i) {
+ if (match((*i)->args, *args)) {
+ b->push_back(*i);
+ }
}
}
+ doRoute(msg, b);
}
bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const args)
{
- for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i) {
- if ( (!args || equal(i->first, *args)) && (!queue || i->second->queue == queue)) {
- return true;
+ Bindings::ConstPtr p = bindings.snapshot();
+ if (p.get()){
+ for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i) {
+ if ( (!args || equal((*i)->args, *args)) && (!queue || (*i)->queue == queue)) {
+ return true;
+ }
}
}
return false;
@@ -227,5 +206,15 @@ bool HeadersExchange::equal(const FieldTable& a, const FieldTable& b) {
return true;
}
+HeadersExchange::MatchArgs::MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a) : queue(q), args(a) {}
+bool HeadersExchange::MatchArgs::operator()(Exchange::Binding::shared_ptr b)
+{
+ return b->queue == queue && b->args == *args;
+}
+HeadersExchange::MatchKey::MatchKey(Queue::shared_ptr q, const std::string& k) : queue(q), key(k) {}
+bool HeadersExchange::MatchKey::operator()(Exchange::Binding::shared_ptr b)
+{
+ return b->queue == queue && b->key == key;
+}
diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h
index 6e101e193a..6425b44251 100644
--- a/cpp/src/qpid/broker/HeadersExchange.h
+++ b/cpp/src/qpid/broker/HeadersExchange.h
@@ -22,10 +22,12 @@
#define _HeadersExchange_
#include <vector>
-#include "Exchange.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
-#include "qpid/sys/Monitor.h"
-#include "Queue.h"
+#include "qpid/sys/CopyOnWriteArray.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/Queue.h"
namespace qpid {
namespace broker {
@@ -33,34 +35,57 @@ namespace broker {
class HeadersExchange : public virtual Exchange {
typedef std::pair<qpid::framing::FieldTable, Binding::shared_ptr> HeaderMap;
- typedef std::vector<HeaderMap> Bindings;
+ typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Bindings;
+
+ struct MatchArgs
+ {
+ const Queue::shared_ptr queue;
+ const qpid::framing::FieldTable* args;
+ MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a);
+ bool operator()(Exchange::Binding::shared_ptr b);
+ };
+ struct MatchKey
+ {
+ const Queue::shared_ptr queue;
+ const std::string& key;
+ MatchKey(Queue::shared_ptr q, const std::string& k);
+ bool operator()(Exchange::Binding::shared_ptr b);
+ };
Bindings bindings;
- qpid::sys::RWlock lock;
+ qpid::sys::Mutex lock;
static std::string getMatch(const framing::FieldTable* args);
public:
static const std::string typeName;
- HeadersExchange(const string& name, management::Manageable* parent = 0);
- HeadersExchange(const string& _name, bool _durable,
- const qpid::framing::FieldTable& _args,
- management::Manageable* parent = 0);
+ QPID_BROKER_EXTERN HeadersExchange(const string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN HeadersExchange(const 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 string& routingKey, const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue,
+ const string& routingKey,
+ const qpid::framing::FieldTable* args);
virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args);
- virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const string& routingKey,
+ const qpid::framing::FieldTable* args);
- virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args);
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const string* const routingKey,
+ const qpid::framing::FieldTable* const args);
- virtual ~HeadersExchange();
+ QPID_BROKER_EXTERN virtual ~HeadersExchange();
- static bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs);
+ 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);
};
diff --git a/cpp/src/qpid/broker/IncomingExecutionContext.cpp b/cpp/src/qpid/broker/IncomingExecutionContext.cpp
deleted file mode 100644
index 6c6cae6740..0000000000
--- a/cpp/src/qpid/broker/IncomingExecutionContext.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "IncomingExecutionContext.h"
-#include "qpid/Exception.h"
-
-namespace qpid {
-namespace broker {
-
-using boost::intrusive_ptr;
-using qpid::framing::AccumulatedAck;
-using qpid::framing::SequenceNumber;
-using qpid::framing::SequenceNumberSet;
-
-void IncomingExecutionContext::noop()
-{
- complete(next());
-}
-
-void IncomingExecutionContext::flush()
-{
- for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ) {
- if ((*i)->isEnqueueComplete()) {
- complete((*i)->getCommandId());
- i = incomplete.erase(i);
- } else {
- i++;
- }
- }
- window.lwm = completed.mark;
-}
-
-void IncomingExecutionContext::sync()
-{
- while (completed.mark < window.hwm) {
- wait();
- }
-}
-
-void IncomingExecutionContext::sync(const SequenceNumber& point)
-{
- while (!isComplete(point)) {
- wait();
- }
-}
-
-/**
- * Every call to next() should be followed be either a call to
- * complete() - in the case of commands, which are always synchronous
- * - or track() - in the case of messages which may be asynchronously
- * stored.
- */
-SequenceNumber IncomingExecutionContext::next()
-{
- return ++window.hwm;
-}
-
-void IncomingExecutionContext::complete(const SequenceNumber& command)
-{
- completed.update(command, command);
-}
-
-void IncomingExecutionContext::track(intrusive_ptr<Message> msg)
-{
- if (msg->isEnqueueComplete()) {
- complete(msg->getCommandId());
- } else {
- incomplete.push_back(msg);
- }
-}
-
-bool IncomingExecutionContext::isComplete(const SequenceNumber& command)
-{
- if (command > window.hwm) {
- throw Exception(QPID_MSG("Bad sync request: point exceeds last command received ["
- << command.getValue() << " > " << window.hwm.getValue() << "]"));
- }
-
- return completed.covers(command);
-}
-
-
-const SequenceNumber& IncomingExecutionContext::getMark()
-{
- return completed.mark;
-}
-
-SequenceNumberSet IncomingExecutionContext::getRange()
-{
- SequenceNumberSet range;
- completed.collectRanges(range);
- return range;
-}
-
-void IncomingExecutionContext::wait()
-{
- check();
- // for IO flush on the store
- for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); i++) {
- (*i)->flush();
- }
- incomplete.front()->waitForEnqueueComplete();
- flush();
-}
-
-/**
- * This is a check of internal state consistency.
- */
-void IncomingExecutionContext::check()
-{
- if (incomplete.empty()) {
- if (window.hwm != completed.mark) {
- //can only happen if there is a call to next() without a
- //corresponding call to completed() or track() - or if
- //there is a logical error in flush() or
- //AccumulatedAck::update()
- throw Exception(QPID_MSG("Completion tracking error: window.hwm="
- << window.hwm.getValue() << ", completed.mark="
- << completed.mark.getValue()));
- }
- }
-}
-
-}}
-
diff --git a/cpp/src/qpid/broker/IncomingExecutionContext.h b/cpp/src/qpid/broker/IncomingExecutionContext.h
deleted file mode 100644
index 7380e9ae64..0000000000
--- a/cpp/src/qpid/broker/IncomingExecutionContext.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _IncomingExecutionContext_
-#define _IncomingExecutionContext_
-
-#include "Message.h"
-
-#include "qpid/framing/AccumulatedAck.h"
-#include "qpid/framing/SequenceNumber.h"
-
-#include <boost/intrusive_ptr.hpp>
-
-namespace qpid {
-namespace broker {
-
-class IncomingExecutionContext
-{
- typedef std::list<boost::intrusive_ptr<Message> > Messages;
- framing::Window window;
- framing::AccumulatedAck completed;
- Messages incomplete;
-
- bool isComplete(const framing::SequenceNumber& command);
- void check();
- void wait();
-public:
- void noop();
- void flush();
- void sync();
- void sync(const framing::SequenceNumber& point);
- framing::SequenceNumber next();
- void complete(const framing::SequenceNumber& command);
- void track(boost::intrusive_ptr<Message>);
-
- const framing::SequenceNumber& getMark();
- framing::SequenceNumberSet getRange();
-
-};
-
-
-}}
-
-#endif
diff --git a/cpp/src/qpid/broker/IncompleteMessageList.cpp b/cpp/src/qpid/broker/IncompleteMessageList.cpp
index dd7bbfc067..02265ab85c 100644
--- a/cpp/src/qpid/broker/IncompleteMessageList.cpp
+++ b/cpp/src/qpid/broker/IncompleteMessageList.cpp
@@ -18,34 +18,67 @@
* under the License.
*
*/
-#include "IncompleteMessageList.h"
-#include "Message.h"
+#include "qpid/broker/IncompleteMessageList.h"
namespace qpid {
namespace broker {
+IncompleteMessageList::IncompleteMessageList() :
+ callback(boost::bind(&IncompleteMessageList::enqueueComplete, this, _1))
+{}
+
+IncompleteMessageList::~IncompleteMessageList()
+{
+ sys::Mutex::ScopedLock l(lock);
+ for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ++i) {
+ (*i)->resetEnqueueCompleteCallback();
+ (*i)->resetDequeueCompleteCallback();
+ }
+}
+
void IncompleteMessageList::add(boost::intrusive_ptr<Message> msg)
{
+ sys::Mutex::ScopedLock l(lock);
+ msg->setEnqueueCompleteCallback(callback);
incomplete.push_back(msg);
}
-void IncompleteMessageList::process(CompletionListener l, bool sync)
+void IncompleteMessageList::enqueueComplete(const boost::intrusive_ptr<Message>& ) {
+ sys::Mutex::ScopedLock l(lock);
+ lock.notify();
+}
+
+void IncompleteMessageList::process(const CompletionListener& listen, bool sync)
{
+ sys::Mutex::ScopedLock l(lock);
while (!incomplete.empty()) {
boost::intrusive_ptr<Message>& msg = incomplete.front();
if (!msg->isEnqueueComplete()) {
if (sync){
- msg->flush();
- msg->waitForEnqueueComplete();
+ {
+ sys::Mutex::ScopedUnlock u(lock);
+ msg->flush(); // Can re-enter IncompleteMessageList::enqueueComplete
+ }
+ while (!msg->isEnqueueComplete())
+ lock.wait();
} else {
//leave the message as incomplete for now
return;
}
}
- l(msg);
+ listen(msg);
incomplete.pop_front();
}
}
+void IncompleteMessageList::each(const CompletionListener& listen) {
+ Messages snapshot;
+ {
+ sys::Mutex::ScopedLock l(lock);
+ snapshot = incomplete;
+ }
+ std::for_each(incomplete.begin(), incomplete.end(), listen); // FIXME aconway 2008-11-07: passed by ref or value?
+}
+
}}
diff --git a/cpp/src/qpid/broker/IncompleteMessageList.h b/cpp/src/qpid/broker/IncompleteMessageList.h
index 2cfd7bfee5..a4debd1233 100644
--- a/cpp/src/qpid/broker/IncompleteMessageList.h
+++ b/cpp/src/qpid/broker/IncompleteMessageList.h
@@ -21,25 +21,35 @@
#ifndef _IncompleteMessageList_
#define _IncompleteMessageList_
-#include <list>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/broker/Message.h"
#include <boost/intrusive_ptr.hpp>
#include <boost/function.hpp>
+#include <list>
namespace qpid {
namespace broker {
-class Message;
-
class IncompleteMessageList
{
typedef std::list< boost::intrusive_ptr<Message> > Messages;
+
+ void enqueueComplete(const boost::intrusive_ptr<Message>&);
+
+ sys::Monitor lock;
Messages incomplete;
+ Message::MessageCallback callback;
public:
- typedef boost::function<void(boost::intrusive_ptr<Message>)> CompletionListener;
-
- void add(boost::intrusive_ptr<Message> msg);
- void process(CompletionListener l, bool sync);
+ typedef Message::MessageCallback CompletionListener;
+
+ QPID_BROKER_EXTERN IncompleteMessageList();
+ QPID_BROKER_EXTERN ~IncompleteMessageList();
+
+ QPID_BROKER_EXTERN void add(boost::intrusive_ptr<Message> msg);
+ QPID_BROKER_EXTERN void process(const CompletionListener& l, bool sync);
+ void each(const CompletionListener& l);
};
diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp
index 05b759f695..cdba18ccf9 100644
--- a/cpp/src/qpid/broker/Link.cpp
+++ b/cpp/src/qpid/broker/Link.cpp
@@ -19,50 +19,61 @@
*
*/
-#include "Link.h"
-#include "LinkRegistry.h"
-#include "Broker.h"
-#include "Connection.h"
-#include "qpid/agent/ManagementAgent.h"
-#include "qpid/management/Link.h"
+#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::NotAllowedException;
+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;
+namespace _qmf = qmf::org::apache::qpid::broker;
Link::Link(LinkRegistry* _links,
MessageStore* _store,
string& _host,
uint16_t _port,
- bool _useSsl,
+ string& _transport,
bool _durable,
string& _authMechanism,
string& _username,
string& _password,
Broker* _broker,
- management::Manageable* parent)
- : links(_links), store(_store), host(_host), port(_port), useSsl(_useSsl), durable(_durable),
+ 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)
+ connection(0),
+ agent(0)
{
- if (parent != 0)
+ if (parent != 0 && broker != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtObject = new management::Link(agent, this, parent, _host, _port, _useSsl, _durable);
+ mgmtObject = new _qmf::Link(agent, this, parent, _host, _port, _transport, _durable);
if (!durable)
agent->addObject(mgmtObject);
}
@@ -73,7 +84,7 @@ Link::Link(LinkRegistry* _links,
Link::~Link ()
{
if (state == STATE_OPERATIONAL && connection != 0)
- connection->close();
+ connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
@@ -95,6 +106,7 @@ void Link::setStateLH (int newState)
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;
}
}
@@ -104,8 +116,9 @@ void Link::startConnectionLH ()
// 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, port, useSsl,
+ broker->connect (host, 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 (mgmtObject != 0)
@@ -115,27 +128,39 @@ void Link::startConnectionLH ()
void Link::established ()
{
- Mutex::ScopedLock mutex(lock);
+ stringstream addr;
+ addr << host << ":" << port;
- QPID_LOG (info, "Inter-broker link established to " << host << ":" << port);
- setStateLH(STATE_OPERATIONAL);
- currentInterval = 1;
- visitCount = 0;
- if (closing)
- destroy();
+ QPID_LOG (info, "Inter-broker link established to " << addr.str());
+ 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)
- QPID_LOG (warning, "Inter-broker link disconnected from " << host << ":" << port);
+ if (state == STATE_OPERATIONAL) {
+ stringstream addr;
+ addr << host << ":" << port;
+ QPID_LOG (warning, "Inter-broker link disconnected from " << addr.str());
+ agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
+ }
- for (Bridges::iterator i = active.begin(); i != active.end(); i++)
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ (*i)->closed();
created.push_back(*i);
+ }
active.clear();
if (state != STATE_FAILED)
@@ -149,32 +174,46 @@ void Link::closed (int, std::string text)
destroy();
}
-void Link::destroy ()
+void Link::checkClosePermission()
{
Mutex::ScopedLock mutex(lock);
- Bridges toDelete;
+
+ AclModule* acl = getBroker()->getAcl();
+ std::string userID = getUsername() + "@" + getBroker()->getOptions().realm;
+ if (acl && !acl->authorise(userID,acl::ACT_DELETE,acl::OBJ_LINK,"")){
+ throw NotAllowedException("ACL denied delete link request");
+ }
+}
- QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management");
- if (connection)
- connection->close(403, "closed by management");
- setStateLH(STATE_CLOSED);
+void Link::destroy ()
+{
+ Bridges toDelete;
+ {
+ Mutex::ScopedLock mutex(lock);
- // 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++)
- toDelete.push_back(*i);
- active.clear();
+ 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);
- for (Bridges::iterator i = created.begin(); i != created.end(); i++)
- toDelete.push_back(*i);
- created.clear();
+ // 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();
- // Now delete all bridges on this link.
+ 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);
}
@@ -186,21 +225,27 @@ void Link::add(Bridge::shared_ptr 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;
+ {
+ 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()) {
- bridge->cancel();
- active.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()
@@ -209,8 +254,9 @@ void Link::ioThreadProcessing()
if (state != STATE_OPERATIONAL)
return;
+ QPID_LOG(debug, "Link::ioThreadProcessing()");
- //process any pending creates
+ //process any pending creates and/or cancellations
if (!created.empty()) {
for (Bridges::iterator i = created.begin(); i != created.end(); ++i) {
active.push_back(*i);
@@ -218,34 +264,77 @@ void Link::ioThreadProcessing()
}
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;
- currentInterval *= 2;
- if (currentInterval > MAX_INTERVAL)
- currentInterval = MAX_INTERVAL;
- startConnectionLH();
+ //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() && connection != 0)
+ else if (state == STATE_OPERATIONAL && (!created.empty() || !cancellations.empty()) && connection != 0)
connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
}
+void Link::reconnect(const qpid::TcpAddress& a)
+{
+ Mutex::ScopedLock mutex(lock);
+ host = a.host;
+ port = a.port;
+ startConnectionLH();
+ if (mgmtObject != 0) {
+ stringstream errorString;
+ errorString << "Failed over to " << a;
+ mgmtObject->set_lastError(errorString.str());
+ }
+}
+
+bool Link::tryFailover()
+{
+ //TODO: urls only work for TCP at present, update when that has changed
+ TcpAddress next;
+ if (transport == Broker::TCP_TRANSPORT && urls.next(next) &&
+ (next.host != host || next.port != port)) {
+ links->changeAddress(TcpAddress(host, port), next);
+ QPID_LOG(debug, "Link failing over to " << host << ":" << port);
+ return true;
+ } else {
+ return false;
+ }
+}
+
uint Link::nextChannel()
{
Mutex::ScopedLock mutex(lock);
@@ -265,7 +354,7 @@ void Link::notifyConnectionForced(const string text)
void Link::setPersistenceId(uint64_t id) const
{
if (mgmtObject != 0 && persistenceId == 0) {
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker->getManagementAgent();
agent->addObject(mgmtObject, id);
}
persistenceId = id;
@@ -280,19 +369,20 @@ 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();
- bool useSsl(buffer.getOctet());
+ buffer.getShortString(transport);
bool durable(buffer.getOctet());
buffer.getShortString(authMechanism);
buffer.getShortString(username);
buffer.getShortString(password);
- return links.declare(host, port, useSsl, durable, authMechanism, username, password).first;
+ return links.declare(host, port, transport, durable, authMechanism, username, password).first;
}
void Link::encode(Buffer& buffer) const
@@ -300,7 +390,7 @@ void Link::encode(Buffer& buffer) const
buffer.putShortString(string("link"));
buffer.putShortString(host);
buffer.putShort(port);
- buffer.putOctet(useSsl ? 1 : 0);
+ buffer.putShortString(transport);
buffer.putOctet(durable ? 1 : 0);
buffer.putShortString(authMechanism);
buffer.putShortString(username);
@@ -312,7 +402,7 @@ uint32_t Link::encodedSize() const
return host.size() + 1 // short-string (host)
+ 5 // short-string ("link")
+ 2 // port
- + 1 // useSsl
+ + transport.size() + 1 // short-string(transport)
+ 1 // durable
+ authMechanism.size() + 1
+ username.size() + 1
@@ -324,27 +414,48 @@ ManagementObject* Link::GetManagementObject (void) const
return (ManagementObject*) mgmtObject;
}
-Manageable::status_t Link::ManagementMethod (uint32_t op, management::Args& args)
+Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& text)
{
switch (op)
{
- case management::Link::METHOD_CLOSE :
- closing = true;
- if (state != STATE_CONNECTING)
- destroy();
+ case _qmf::Link::METHOD_CLOSE :
+ checkClosePermission();
+ 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 management::Link::METHOD_BRIDGE :
- management::ArgsLinkBridge& iargs = (management::ArgsLinkBridge&) args;
+ 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)
- return Manageable::STATUS_INVALID_PARAMETER;
+ 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_srcIsLocal, iargs.i_tag, iargs.i_excludes,
+ iargs.i_dynamic, iargs.i_sync);
if (result.second && iargs.i_durable)
store->create(*result.first);
@@ -354,3 +465,17 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, management::Args& args
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/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h
index d425c49800..318eb5bd32 100644
--- a/cpp/src/qpid/broker/Link.h
+++ b/cpp/src/qpid/broker/Link.h
@@ -23,13 +23,15 @@
*/
#include <boost/shared_ptr.hpp>
-#include "MessageStore.h"
-#include "PersistableConfig.h"
-#include "Bridge.h"
+#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/Link.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/broker/Link.h"
#include <boost/ptr_container/ptr_vector.hpp>
namespace qpid {
@@ -47,30 +49,35 @@ namespace qpid {
MessageStore* store;
string host;
uint16_t port;
- bool useSsl;
+ string transport;
bool durable;
string authMechanism;
string username;
string password;
mutable uint64_t persistenceId;
- management::Link* mgmtObject;
+ 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;
@@ -78,6 +85,8 @@ namespace qpid {
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
+ void checkClosePermission(); // ACL check for explict mgmt call to close this link
public:
typedef boost::shared_ptr<Link> shared_ptr;
@@ -86,7 +95,7 @@ namespace qpid {
MessageStore* store,
string& host,
uint16_t port,
- bool useSsl,
+ string& transport,
bool durable,
string& authMechanism,
string& username,
@@ -106,12 +115,15 @@ namespace qpid {
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 TcpAddress&); //called by LinkRegistry
string getAuthMechanism() { return authMechanism; }
string getUsername() { return username; }
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;
@@ -123,8 +135,8 @@ namespace qpid {
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&);
+ management::ManagementObject* GetManagementObject(void) const;
+ management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&);
};
}
}
diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp
index 0703c276cf..f32587dd68 100644
--- a/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/cpp/src/qpid/broker/LinkRegistry.cpp
@@ -18,20 +18,49 @@
* under the License.
*
*/
-#include "LinkRegistry.h"
+#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::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
-LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), parent(0), store(0)
+// 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("")
{
- timer.add (intrusive_ptr<TimerTask> (new Periodic(*this)));
+}
+
+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) :
@@ -40,7 +69,8 @@ LinkRegistry::Periodic::Periodic (LinkRegistry& _links) :
void LinkRegistry::Periodic::fire ()
{
links.periodicMaintenance ();
- links.timer.add (intrusive_ptr<TimerTask> (new Periodic(links)));
+ setupNextFire();
+ links.timer->add(this);
}
void LinkRegistry::periodicMaintenance ()
@@ -49,13 +79,53 @@ void LinkRegistry::periodicMaintenance ()
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::TcpAddress& oldAddress, const qpid::TcpAddress& 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::TcpAddress& 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,
- bool useSsl,
+ string& transport,
bool durable,
string& authMechanism,
string& username,
@@ -72,9 +142,10 @@ pair<Link::shared_ptr, bool> LinkRegistry::declare(string& host,
{
Link::shared_ptr link;
- link = Link::shared_ptr (new Link (this, store, host, port, useSsl, durable,
+ 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);
}
@@ -90,9 +161,13 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host,
bool isQueue,
bool isLocal,
std::string& tag,
- std::string& excludes)
+ std::string& excludes,
+ bool dynamic,
+ uint16_t sync)
{
Mutex::ScopedLock locker(lock);
+ QPID_LOG(debug, "Bridge declared " << host << ": " << port << " from " << src << " to " << dest << " (" << key << ")");
+
stringstream keystream;
keystream << host << ":" << port;
string linkKey = string(keystream.str());
@@ -107,7 +182,7 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host,
BridgeMap::iterator b = bridges.find(bridgeKey);
if (b == bridges.end())
{
- management::ArgsLinkBridge args;
+ _qmf::ArgsLinkBridge args;
Bridge::shared_ptr bridge;
args.i_durable = durable;
@@ -118,6 +193,8 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host,
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(),
@@ -177,7 +254,6 @@ void LinkRegistry::destroy(const std::string& host,
void LinkRegistry::setStore (MessageStore* _store)
{
- assert (store == 0 && _store != 0);
store = _store;
}
@@ -185,66 +261,84 @@ MessageStore* LinkRegistry::getStore() const {
return store;
}
-void LinkRegistry::notifyConnection(const std::string& key, Connection* c)
+Link::shared_ptr LinkRegistry::findLink(const std::string& key)
{
Mutex::ScopedLock locker(lock);
LinkMap::iterator l = links.find(key);
- if (l != links.end())
- {
- l->second->established();
- l->second->setConnection(c);
+ 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)
{
- Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l != links.end())
- l->second->closed(0, "Closed by peer");
+ 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)
{
- Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l != links.end())
- l->second->notifyConnectionForced(text);
+ Link::shared_ptr link = findLink(key);
+ if (link) {
+ link->notifyConnectionForced(text);
+ }
}
std::string LinkRegistry::getAuthMechanism(const std::string& key)
{
- Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l != links.end())
- return l->second->getAuthMechanism();
+ Link::shared_ptr link = findLink(key);
+ if (link)
+ return link->getAuthMechanism();
return string("ANONYMOUS");
}
std::string LinkRegistry::getAuthCredentials(const std::string& key)
{
- Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l == links.end())
+ Link::shared_ptr link = findLink(key);
+ if (!link)
return string();
string result;
result += '\0';
- result += l->second->getUsername();
+ result += link->getUsername();
result += '\0';
- result += l->second->getPassword();
+ result += link->getPassword();
return result;
}
std::string LinkRegistry::getAuthIdentity(const std::string& key)
{
- Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l == links.end())
+ Link::shared_ptr link = findLink(key);
+ if (!link)
return string();
- return l->second->getUsername();
+ return link->getUsername();
}
+std::string LinkRegistry::createKey(const qpid::TcpAddress& a)
+{
+ stringstream keystream;
+ keystream << a.host << ":" << a.port;
+ return string(keystream.str());
+}
+
+void LinkRegistry::setPassive(bool p)
+{
+ Mutex::ScopedLock locker(lock);
+ passiveChanged = p != passive;
+ passive = p;
+ //will activate or passivate links on maintenance visit
+}
diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h
index 242c0d58ba..09a89298b6 100644
--- a/cpp/src/qpid/broker/LinkRegistry.h
+++ b/cpp/src/qpid/broker/LinkRegistry.h
@@ -23,23 +23,26 @@
*/
#include <map>
-#include "Link.h"
-#include "Bridge.h"
-#include "MessageStore.h"
-#include "Timer.h"
+#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>
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 TimerTask
+ struct Periodic : public sys::TimerTask
{
LinkRegistry& links;
@@ -48,28 +51,40 @@ namespace broker {
void fire();
};
- typedef std::map<std::string, Link::shared_ptr> LinkMap;
+ typedef std::map<std::string, boost::shared_ptr<Link> > LinkMap;
typedef std::map<std::string, Bridge::shared_ptr> BridgeMap;
+ typedef std::map<std::string, TcpAddress> AddressMap;
LinkMap links;
LinkMap linksToDestroy;
BridgeMap bridges;
BridgeMap bridgesToDestroy;
+ AddressMap reMappings;
qpid::sys::Mutex lock;
Broker* broker;
- Timer timer;
+ 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 TcpAddress& newAddress);
+ boost::shared_ptr<Link> findLink(const std::string& key);
+ static std::string createKey(const TcpAddress& address);
public:
+ LinkRegistry (); // Only used in store tests
LinkRegistry (Broker* _broker);
- std::pair<Link::shared_ptr, bool>
+ ~LinkRegistry();
+
+ std::pair<boost::shared_ptr<Link>, bool>
declare(std::string& host,
uint16_t port,
- bool useSsl,
+ std::string& transport,
bool durable,
std::string& authMechanism,
std::string& username,
@@ -84,7 +99,9 @@ namespace broker {
bool isQueue,
bool isLocal,
std::string& id,
- std::string& excludes);
+ std::string& excludes,
+ bool dynamic,
+ uint16_t sync);
void destroy(const std::string& host, const uint16_t port);
void destroy(const std::string& host,
@@ -114,6 +131,18 @@ namespace broker {
std::string getAuthMechanism (const std::string& key);
std::string getAuthCredentials (const std::string& key);
std::string getAuthIdentity (const std::string& key);
+
+ /**
+ * Called by links failing over to new address
+ */
+ void changeAddress(const TcpAddress& oldAddress, const TcpAddress& 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);
};
}
}
diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp
index 331bb5e716..47ca7a7ae8 100644
--- a/cpp/src/qpid/broker/Message.cpp
+++ b/cpp/src/qpid/broker/Message.cpp
@@ -19,8 +19,9 @@
*
*/
-#include "Message.h"
-#include "ExchangeRegistry.h"
+#include "qpid/broker/Message.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"
@@ -30,17 +31,43 @@
#include "qpid/framing/TypeFilter.h"
#include "qpid/log/Statement.h"
+#include <time.h>
+
using boost::intrusive_ptr;
-using namespace qpid::broker;
-using namespace qpid::framing;
+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 SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), publisher(0), adapter(0) {}
+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), enqueueCallback(0), dequeueCallback(0), requiredCredit(0) {}
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
@@ -71,9 +98,9 @@ const FieldTable* Message::getApplicationHeaders() const
return getAdapter().getApplicationHeaders(frames);
}
-bool Message::isPersistent()
+bool Message::isPersistent() const
{
- return getAdapter().isPersistent(frames);
+ return (getAdapter().isPersistent(frames) || forcePersistentPolicy);
}
bool Message::requiresAccept()
@@ -81,12 +108,16 @@ bool Message::requiresAccept()
return getAdapter().requiresAccept(frames);
}
-uint32_t Message::getRequiredCredit() const
+uint32_t Message::getRequiredCredit()
{
- //add up payload for all header and content frames in the frameset
- SumBodySize sum;
- frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>());
- return sum.getSize();
+ 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
@@ -96,7 +127,7 @@ void Message::encode(framing::Buffer& buffer) const
frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>());
//then encode the payload of each content frame
- EncodeBody f2(buffer);
+ framing::EncodeBody f2(buffer);
frames.map_if(f2, TypeFilter<CONTENT_BODY>());
}
@@ -141,9 +172,9 @@ 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;
- frame.setBody(AMQContentBody());
+ AMQFrame frame((AMQContentBody()));
frame.castBody<AMQContentBody>()->decode(buffer, buffer.available());
+ frame.setFirstSegment(false);
frames.append(frame);
} else {
//adjust header flags
@@ -154,17 +185,31 @@ void Message::decodeContent(framing::Buffer& buffer)
loaded = true;
}
-void Message::releaseContent(MessageStore* _store)
+void Message::tryReleaseContent()
{
- if (!store) {
- store = _store;
+ 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();
@@ -182,30 +227,37 @@ void Message::destroy()
}
}
-void Message::sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const
+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
{
- if (isContentReleased()) {
- //load content from store in chunks of maxContentSize
+ sys::Mutex::ScopedLock l(lock);
+ if (isContentReleased() && !frames.isComplete()) {
+ sys::Mutex::ScopedUnlock u(lock);
uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
- intrusive_ptr<const PersistableMessage> pmsg(this);
-
- bool done = false;
- for (uint64_t offset = 0; !done; offset += maxContentSize)
+ bool morecontent = true;
+ for (uint64_t offset = 0; morecontent; offset += maxContentSize)
{
- AMQFrame frame(in_place<AMQContentBody>());
- string& data = frame.castBody<AMQContentBody>()->getData();
-
- store->loadContent(queue, pmsg, data, offset, maxContentSize);
- done = data.size() < maxContentSize;
- frame.setBof(false);
- frame.setEof(true);
- if (offset > 0) {
- frame.setBos(false);
- }
- if (!done) {
- frame.setEos(false);
- }
- QPID_LOG(debug, "loaded frame for delivery: " << frame);
+ AMQFrame frame((AMQContentBody()));
+ morecontent = getContentFrame(queue, frame, maxContentSize, offset);
out.handle(frame);
}
} else {
@@ -253,14 +305,14 @@ bool Message::isContentLoaded() const
namespace
{
- const std::string X_QPID_TRACE("x-qpid.trace");
+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->getString(X_QPID_TRACE);
+ std::string traceStr = headers->getAsString(X_QPID_TRACE);
if (traceStr.size()) {
std::vector<std::string> trace = split(traceStr, ", ");
@@ -281,7 +333,7 @@ void Message::addTraceId(const std::string& id)
sys::Mutex::ScopedLock l(lock);
if (isA<MessageTransferBody>()) {
FieldTable& headers = getProperties<MessageProperties>()->getApplicationHeaders();
- std::string trace = headers.getString(X_QPID_TRACE);
+ 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) {
@@ -291,3 +343,86 @@ void Message::addTraceId(const std::string& id)
}
}
}
+
+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.
+ expiration = AbsTime(AbsTime::now(), Duration(props->getTtl() * TIME_MSEC));
+ setExpiryPolicy(e);
+ }
+}
+
+void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) {
+ expiryPolicy = e;
+ if (expiryPolicy)
+ expiryPolicy->willExpire(*this);
+}
+
+bool Message::hasExpired()
+{
+ return expiryPolicy && expiryPolicy->hasExpired(*this);
+}
+
+boost::intrusive_ptr<Message>& Message::getReplacementMessage(const Queue* qfor) const
+{
+ sys::Mutex::ScopedLock l(lock);
+ Replacement::iterator i = replacement.find(qfor);
+ if (i != replacement.end()){
+ return i->second;
+ }
+ return empty;
+}
+
+void Message::setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor)
+{
+ sys::Mutex::ScopedLock l(lock);
+ replacement[qfor] = msg;
+}
+
+void Message::allEnqueuesComplete() {
+ sys::Mutex::ScopedLock l(callbackLock);
+ MessageCallback* cb = enqueueCallback;
+ if (cb && *cb) (*cb)(intrusive_ptr<Message>(this));
+}
+
+void Message::allDequeuesComplete() {
+ sys::Mutex::ScopedLock l(callbackLock);
+ MessageCallback* cb = dequeueCallback;
+ if (cb && *cb) (*cb)(intrusive_ptr<Message>(this));
+}
+
+void Message::setEnqueueCompleteCallback(MessageCallback& cb) {
+ sys::Mutex::ScopedLock l(callbackLock);
+ enqueueCallback = &cb;
+}
+
+void Message::resetEnqueueCompleteCallback() {
+ sys::Mutex::ScopedLock l(callbackLock);
+ enqueueCallback = 0;
+}
+
+void Message::setDequeueCompleteCallback(MessageCallback& cb) {
+ sys::Mutex::ScopedLock l(callbackLock);
+ dequeueCallback = &cb;
+}
+
+void Message::resetDequeueCompleteCallback() {
+ sys::Mutex::ScopedLock l(callbackLock);
+ dequeueCallback = 0;
+}
+
+framing::FieldTable& Message::getOrInsertHeaders()
+{
+ return getProperties<MessageProperties>()->getApplicationHeaders();
+}
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h
index 0a95fedea6..375fa9ce26 100644
--- a/cpp/src/qpid/broker/Message.h
+++ b/cpp/src/qpid/broker/Message.h
@@ -22,33 +22,38 @@
*
*/
-#include <string>
-#include <vector>
-#include <boost/shared_ptr.hpp>
-#include <boost/variant.hpp>
-#include "PersistableMessage.h"
-#include "MessageAdapter.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/PersistableMessage.h"
+#include "qpid/broker/MessageAdapter.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/sys/Mutex.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:
- Message(const framing::SequenceNumber& id = framing::SequenceNumber());
- ~Message();
+ typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback;
+
+ QPID_BROKER_EXTERN Message(const framing::SequenceNumber& id = framing::SequenceNumber());
+ QPID_BROKER_EXTERN ~Message();
uint64_t getPersistenceId() const { return persistenceId; }
void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; }
@@ -61,16 +66,22 @@ public:
const framing::SequenceNumber& getCommandId() { return frames.getId(); }
- uint64_t contentSize() const;
+ QPID_BROKER_EXTERN uint64_t contentSize() const;
- std::string getRoutingKey() const;
+ QPID_BROKER_EXTERN std::string getRoutingKey() const;
const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const;
- std::string getExchangeName() const;
+ QPID_BROKER_EXTERN std::string getExchangeName() const;
bool isImmediate() const;
- const framing::FieldTable* getApplicationHeaders() const;
- bool isPersistent();
+ QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() 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; }
+
framing::FrameSet& getFrames() { return frames; }
const framing::FrameSet& getFrames() const { return frames; }
@@ -84,15 +95,24 @@ public:
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() const;
+ uint32_t getRequiredCredit();
void encode(framing::Buffer& buffer) const;
void encodeContent(framing::Buffer& buffer) const;
@@ -110,26 +130,44 @@ public:
uint32_t encodedHeaderSize() const;
uint32_t encodedContentSize() const;
- void decodeHeader(framing::Buffer& buffer);
- void decodeContent(framing::Buffer& buffer);
+ QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer);
+ QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer);
- /**
- * Releases the in-memory content data held by this
- * message. Must pass in a store from which the data can
- * be reloaded.
- */
- void releaseContent(MessageStore* store);
+ void QPID_BROKER_EXTERN tryReleaseContent();
+ void releaseContent();
+ void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead
void destroy();
- void sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const;
+ 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;
- bool isContentLoaded() 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();
+
+ boost::intrusive_ptr<Message>& getReplacementMessage(const Queue* qfor) const;
+ void setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor);
+
+ /** Call cb when enqueue is complete, may call immediately. Holds cb by reference. */
+ void setEnqueueCompleteCallback(MessageCallback& cb);
+ void resetEnqueueCompleteCallback();
+
+ /** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */
+ void setDequeueCompleteCallback(MessageCallback& cb);
+ void resetDequeueCompleteCallback();
private:
+ typedef std::map<const Queue*,boost::intrusive_ptr<Message> > Replacement;
+
+ MessageAdapter& getAdapter() const;
+ void allEnqueuesComplete();
+ void allDequeuesComplete();
+
mutable sys::Mutex lock;
framing::FrameSet frames;
mutable boost::shared_ptr<Exchange> exchange;
@@ -137,12 +175,22 @@ public:
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;
- MessageAdapter& getAdapter() const;
+ mutable Replacement replacement;
+ mutable boost::intrusive_ptr<Message> empty;
+
+ sys::Mutex callbackLock;
+ MessageCallback* enqueueCallback;
+ MessageCallback* dequeueCallback;
+
+ uint32_t requiredCredit;
};
}}
diff --git a/cpp/src/qpid/broker/MessageAdapter.cpp b/cpp/src/qpid/broker/MessageAdapter.cpp
index 12f01494de..c0c1c4445a 100644
--- a/cpp/src/qpid/broker/MessageAdapter.cpp
+++ b/cpp/src/qpid/broker/MessageAdapter.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "MessageAdapter.h"
+#include "qpid/broker/MessageAdapter.h"
#include "qpid/framing/DeliveryProperties.h"
#include "qpid/framing/MessageProperties.h"
diff --git a/cpp/src/qpid/broker/MessageBuilder.cpp b/cpp/src/qpid/broker/MessageBuilder.cpp
index eda71ed3da..b1a2b77b05 100644
--- a/cpp/src/qpid/broker/MessageBuilder.cpp
+++ b/cpp/src/qpid/broker/MessageBuilder.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,10 +18,11 @@
* under the License.
*
*/
-#include "MessageBuilder.h"
+#include "qpid/broker/MessageBuilder.h"
-#include "Message.h"
-#include "MessageStore.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"
@@ -29,11 +30,13 @@ using boost::intrusive_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
-namespace
+namespace
{
std::string type_str(uint8_t type);
+ const std::string QPID_MANAGEMENT("qpid.management");
}
-MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) :
+
+MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) :
state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {}
void MessageBuilder::handle(AMQFrame& frame)
@@ -48,14 +51,13 @@ void MessageBuilder::handle(AMQFrame& frame)
if (type == CONTENT_BODY) {
//TODO: rethink how to handle non-existent headers(?)...
//didn't get a header: add in a dummy
- AMQFrame header;
- header.setBody(AMQHeaderBody());
+ AMQFrame header((AMQHeaderBody()));
header.setBof(false);
header.setEof(false);
- message->getFrames().append(header);
+ message->getFrames().append(header);
} else if (type != HEADER_BODY) {
throw CommandInvalidException(
- QPID_MSG("Invalid frame sequence for message, expected header or content got "
+ QPID_MSG("Invalid frame sequence for message, expected header or content got "
<< type_str(type) << ")"));
}
state = CONTENT;
@@ -72,8 +74,13 @@ void MessageBuilder::handle(AMQFrame& frame)
} else {
message->getFrames().append(frame);
//have we reached the staging limit? if so stage message and release content
- if (state == CONTENT && stagingThreshold && message->getFrames().getContentSize() >= stagingThreshold) {
- message->releaseContent(store);
+ if (state == CONTENT
+ && stagingThreshold
+ && message->getFrames().getContentSize() >= stagingThreshold
+ && !NullMessageStore::isNullStore(store)
+ && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */)
+ {
+ message->releaseContent();
staging = true;
}
}
@@ -89,6 +96,7 @@ void MessageBuilder::end()
void MessageBuilder::start(const SequenceNumber& id)
{
message = intrusive_ptr<Message>(new Message(id));
+ message->setStore(store);
state = METHOD;
staging = false;
}
@@ -101,7 +109,7 @@ 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)
+std::string type_str(uint8_t type)
{
switch(type) {
case METHOD_BODY: return METHOD_BODY_S;
@@ -117,7 +125,7 @@ std::string type_str(uint8_t type)
void MessageBuilder::checkType(uint8_t expected, uint8_t actual)
{
if (expected != actual) {
- throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected "
+ throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected "
<< type_str(expected) << " got " << type_str(actual) << ")"));
}
}
diff --git a/cpp/src/qpid/broker/MessageBuilder.h b/cpp/src/qpid/broker/MessageBuilder.h
index 395de024ab..e63c108097 100644
--- a/cpp/src/qpid/broker/MessageBuilder.h
+++ b/cpp/src/qpid/broker/MessageBuilder.h
@@ -21,6 +21,7 @@
#ifndef _MessageBuilder_
#define _MessageBuilder_
+#include "qpid/broker/BrokerImportExport.h"
#include "qpid/framing/FrameHandler.h"
#include "qpid/framing/SequenceNumber.h"
#include "qpid/RefCounted.h"
@@ -34,10 +35,11 @@ namespace qpid {
class MessageBuilder : public framing::FrameHandler{
public:
- MessageBuilder(MessageStore* const store, uint64_t stagingThreshold);
- void handle(framing::AMQFrame& frame);
+ QPID_BROKER_EXTERN MessageBuilder(MessageStore* const store,
+ uint64_t stagingThreshold);
+ QPID_BROKER_EXTERN void handle(framing::AMQFrame& frame);
boost::intrusive_ptr<Message> getMessage() { return message; }
- void start(const framing::SequenceNumber& id);
+ QPID_BROKER_EXTERN void start(const framing::SequenceNumber& id);
void end();
private:
enum State {DORMANT, METHOD, HEADER, CONTENT};
diff --git a/cpp/src/qpid/broker/MessageDelivery.cpp b/cpp/src/qpid/broker/MessageDelivery.cpp
deleted file mode 100644
index a757d191e7..0000000000
--- a/cpp/src/qpid/broker/MessageDelivery.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "MessageDelivery.h"
-
-#include "DeliveryToken.h"
-#include "Message.h"
-#include "Queue.h"
-#include "qpid/framing/FrameHandler.h"
-#include "qpid/framing/MessageTransferBody.h"
-
-
-using namespace boost;
-using namespace qpid::broker;
-using namespace qpid::framing;
-
-namespace qpid{
-namespace broker{
-
-struct BaseToken : DeliveryToken
-{
- virtual ~BaseToken() {}
- virtual AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId id) = 0;
-};
-
-struct MessageDeliveryToken : BaseToken
-{
- const std::string destination;
- const uint8_t confirmMode;
- const uint8_t acquireMode;
- const bool isPreview;
-
- MessageDeliveryToken(const std::string& d, uint8_t c, uint8_t a, bool p) :
- destination(d), confirmMode(c), acquireMode(a), isPreview(p) {}
-
- AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId /*id*/)
- {
- //may need to set the redelivered flag:
- if (msg->getRedelivered()){
- msg->getProperties<DeliveryProperties>()->setRedelivered(true);
- }
- return AMQFrame(in_place<MessageTransferBody>(
- ProtocolVersion(), destination, confirmMode, acquireMode));
- }
-};
-
-}
-}
-
-DeliveryToken::shared_ptr MessageDelivery::getMessageDeliveryToken(const std::string& destination,
- uint8_t confirmMode, uint8_t acquireMode)
-{
- return DeliveryToken::shared_ptr(new MessageDeliveryToken(destination, confirmMode, acquireMode, false));
-}
-
-void MessageDelivery::deliver(QueuedMessage& msg,
- framing::FrameHandler& handler,
- DeliveryId id,
- DeliveryToken::shared_ptr token,
- uint16_t framesize)
-{
- //currently a message published from one class and delivered to
- //another may well have the wrong headers; however we will only
- //have one content class for 0-10 proper
-
- boost::shared_ptr<BaseToken> t = dynamic_pointer_cast<BaseToken>(token);
- AMQFrame method = t->sendMethod(msg.payload, id);
- method.setEof(false);
- handler.handle(method);
- msg.payload->sendHeader(handler, framesize);
- msg.payload->sendContent(*(msg.queue), handler, framesize);
-}
diff --git a/cpp/src/qpid/broker/MessageDelivery.h b/cpp/src/qpid/broker/MessageDelivery.h
deleted file mode 100644
index cfde9ee307..0000000000
--- a/cpp/src/qpid/broker/MessageDelivery.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef _broker_MessageDelivery_h
-#define _broker_MessageDelivery_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 "DeliveryId.h"
-#include "Consumer.h"
-#include "qpid/framing/FrameHandler.h"
-
-namespace qpid {
-namespace broker {
-
-class DeliveryToken;
-class Message;
-class Queue;
-
-/**
- * TODO: clean this up; we don't need it anymore in its current form
- *
- * Encapsulates the different options for message delivery currently supported.
- */
-class MessageDelivery {
-public:
- static boost::shared_ptr<DeliveryToken> getMessageDeliveryToken(const std::string& destination,
- uint8_t confirmMode,
- uint8_t acquireMode);
-
- static void deliver(QueuedMessage& msg, framing::FrameHandler& out,
- DeliveryId deliveryTag, boost::shared_ptr<DeliveryToken> token, uint16_t framesize);
-};
-
-}
-}
-
-
-#endif /*!_broker_MessageDelivery_h*/
diff --git a/cpp/src/qpid/broker/MessageStore.h b/cpp/src/qpid/broker/MessageStore.h
index 4c4c21dfba..143e860ec7 100644
--- a/cpp/src/qpid/broker/MessageStore.h
+++ b/cpp/src/qpid/broker/MessageStore.h
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,12 +21,12 @@
#ifndef _MessageStore_
#define _MessageStore_
-#include "PersistableExchange.h"
-#include "PersistableMessage.h"
-#include "PersistableQueue.h"
-#include "PersistableConfig.h"
-#include "RecoveryManager.h"
-#include "TransactionalStore.h"
+#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>
@@ -46,12 +46,16 @@ class MessageStore : public TransactionalStore, public Recoverable {
public:
/**
- * init the store, call before any other call. If not called, store
- * is free to pick any defaults
- *
- * @param options Options object provided by concrete store plug in.
+ * If called after initialization but before recovery, will discard the database
+ * and reinitialize using an empty store dir. If the parameter 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 bool init(const Options* options) = 0;
+ virtual void truncateInit(const bool pushDownStoreFiles = false) = 0;
/**
* Record the existence of a durable queue
@@ -62,7 +66,7 @@ class MessageStore : public TransactionalStore, public Recoverable {
* Destroy a durable queue
*/
virtual void destroy(PersistableQueue& queue) = 0;
-
+
/**
* Record the existence of a durable exchange
*/
@@ -72,17 +76,17 @@ class MessageStore : public TransactionalStore, public Recoverable {
* Destroy a durable exchange
*/
virtual void destroy(const PersistableExchange& exchange) = 0;
-
+
/**
* Record a binding
*/
- virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ 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,
+ virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue,
const std::string& key, const framing::FieldTable& args) = 0;
/**
@@ -102,10 +106,10 @@ class MessageStore : public TransactionalStore, public Recoverable {
* 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.
+ * 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
@@ -119,7 +123,7 @@ class MessageStore : public TransactionalStore, public Recoverable {
*/
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
@@ -128,18 +132,18 @@ class MessageStore : public TransactionalStore, public Recoverable {
* content should be loaded, not the headers or related
* meta-data).
*/
- virtual void loadContent(const qpid::broker::PersistableQueue& queue,
+ 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.
+ * 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
@@ -149,7 +153,7 @@ class MessageStore : public TransactionalStore, public Recoverable {
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
@@ -157,7 +161,7 @@ class MessageStore : public TransactionalStore, public Recoverable {
*
* 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
@@ -173,22 +177,22 @@ class MessageStore : public TransactionalStore, public Recoverable {
*
* 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
+ *
+ * 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(){}
};
diff --git a/cpp/src/qpid/broker/MessageStoreModule.cpp b/cpp/src/qpid/broker/MessageStoreModule.cpp
index c9528b9d98..5f7cceebd3 100644
--- a/cpp/src/qpid/broker/MessageStoreModule.cpp
+++ b/cpp/src/qpid/broker/MessageStoreModule.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,25 +19,33 @@
*
*/
-#include "MessageStoreModule.h"
+#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 namespace qpid::broker;
using qpid::framing::FieldTable;
-MessageStoreModule::MessageStoreModule(MessageStore* _store) : store(_store) {}
+namespace qpid {
+namespace broker {
+
+MessageStoreModule::MessageStoreModule(boost::shared_ptr<MessageStore>& _store)
+ : store(_store) {}
MessageStoreModule::~MessageStoreModule()
{
- delete store;
}
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));
@@ -58,13 +66,13 @@ void MessageStoreModule::destroy(const PersistableExchange& exchange)
TRANSFER_EXCEPTION(store->destroy(exchange));
}
-void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q,
+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,
+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));
@@ -102,7 +110,7 @@ void MessageStoreModule::appendContent(const intrusive_ptr<const PersistableMess
}
void MessageStoreModule::loadContent(
- const qpid::broker::PersistableQueue& queue,
+ const qpid::broker::PersistableQueue& queue,
const intrusive_ptr<const PersistableMessage>& msg,
string& data, uint64_t offset, uint32_t length)
{
@@ -162,3 +170,10 @@ 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/cpp/src/qpid/broker/MessageStoreModule.h b/cpp/src/qpid/broker/MessageStoreModule.h
index a16ef4de21..56b5a3c1ae 100644
--- a/cpp/src/qpid/broker/MessageStoreModule.h
+++ b/cpp/src/qpid/broker/MessageStoreModule.h
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,11 +21,12 @@
#ifndef _MessageStoreModule_
#define _MessageStoreModule_
-#include "MessageStore.h"
-#include "Queue.h"
-#include "RecoveryManager.h"
+#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 {
@@ -35,11 +36,12 @@ namespace broker {
*/
class MessageStoreModule : public MessageStore
{
- MessageStore* store;
+ boost::shared_ptr<MessageStore> store;
public:
- MessageStoreModule(MessageStore* store);
+ 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);
@@ -51,9 +53,9 @@ class MessageStoreModule : public MessageStore
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,
+ void bind(const PersistableExchange& exchange, const PersistableQueue& queue,
const std::string& key, const framing::FieldTable& args);
- void unbind(const PersistableExchange& exchange, const PersistableQueue& queue,
+ 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);
@@ -61,7 +63,7 @@ class MessageStoreModule : public MessageStore
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,
+ void loadContent(const qpid::broker::PersistableQueue& queue,
const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data,
uint64_t offset, uint32_t length);
@@ -73,7 +75,8 @@ class MessageStoreModule : public MessageStore
const PersistableQueue& queue);
uint32_t outstandingQueueAIO(const PersistableQueue& queue);
void flush(const qpid::broker::PersistableQueue& queue);
-
+ bool isNull() const;
+
~MessageStoreModule();
};
diff --git a/cpp/src/qpid/broker/NameGenerator.cpp b/cpp/src/qpid/broker/NameGenerator.cpp
index 8484f921e9..e7f193d546 100644
--- a/cpp/src/qpid/broker/NameGenerator.cpp
+++ b/cpp/src/qpid/broker/NameGenerator.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "NameGenerator.h"
+#include "qpid/broker/NameGenerator.h"
#include <sstream>
using namespace qpid::broker;
diff --git a/cpp/src/qpid/broker/NullMessageStore.cpp b/cpp/src/qpid/broker/NullMessageStore.cpp
index e1c7fe240d..6339b655f8 100644
--- a/cpp/src/qpid/broker/NullMessageStore.cpp
+++ b/cpp/src/qpid/broker/NullMessageStore.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,9 +19,11 @@
*
*/
-#include "NullMessageStore.h"
-#include "RecoveryManager.h"
+#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>
@@ -32,47 +34,41 @@ namespace broker{
const std::string nullxid = "";
-class DummyCtxt : public TPCTransactionContext
+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)
+ static std::string getXid(TransactionContext& ctxt)
{
DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt));
return c ? c->xid : nullxid;
}
};
-}
+NullMessageStore::NullMessageStore() : nextPersistenceId(1) {
+ QPID_LOG(info, "No message store configured, persistence is disabled.");
}
-using namespace qpid::broker;
-
-NullMessageStore::NullMessageStore(bool _warn) : warn(_warn), 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*/)
{
- QPID_LOG(info, "Queue '" << queue.getName()
- << "' will not be durable. Persistence not enabled.");
queue.setPersistenceId(nextPersistenceId++);
}
-void NullMessageStore::destroy(PersistableQueue&)
-{
-}
+void NullMessageStore::destroy(PersistableQueue&) {}
void NullMessageStore::create(const PersistableExchange& exchange, const framing::FieldTable& /*args*/)
{
- QPID_LOG(info, "Exchange'" << exchange.getName()
- << "' will not be durable. Persistence not enabled.");
exchange.setPersistenceId(nextPersistenceId++);
}
-void NullMessageStore::destroy(const PersistableExchange& )
-{}
+void NullMessageStore::destroy(const PersistableExchange& ) {}
void NullMessageStore::bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){}
@@ -80,47 +76,31 @@ void NullMessageStore::unbind(const PersistableExchange&, const PersistableQueue
void NullMessageStore::create(const PersistableConfig& config)
{
- QPID_LOG(info, "Persistence not enabled, configuration not stored.");
config.setPersistenceId(nextPersistenceId++);
}
-void NullMessageStore::destroy(const PersistableConfig&)
-{
- QPID_LOG(info, "Persistence not enabled, configuration not stored.");
-}
+void NullMessageStore::destroy(const PersistableConfig&) {}
-void NullMessageStore::recover(RecoveryManager&)
-{
- QPID_LOG(info, "Persistence not enabled, no recovery attempted.");
-}
+void NullMessageStore::recover(RecoveryManager&) {}
-void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&)
-{
- QPID_LOG(info, "Can't stage message. Persistence not enabled.");
-}
+void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&) {}
-void NullMessageStore::destroy(PersistableMessage&)
-{
-}
+void NullMessageStore::destroy(PersistableMessage&) {}
-void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const string&)
-{
- QPID_LOG(info, "Can't append content. Persistence not enabled.");
-}
+void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const string&) {}
void NullMessageStore::loadContent(const qpid::broker::PersistableQueue&,
const intrusive_ptr<const PersistableMessage>&,
string&, uint64_t, uint32_t)
{
- QPID_LOG(info, "Can't load content. Persistence not enabled.");
+ throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled");
}
void NullMessageStore::enqueue(TransactionContext*,
const intrusive_ptr<PersistableMessage>& msg,
- const PersistableQueue& queue)
+ const PersistableQueue&)
{
- msg->enqueueComplete();
- QPID_LOG(info, "Message is not durably recorded on '" << queue.getName() << "'. Persistence not enabled.");
+ msg->enqueueComplete();
}
void NullMessageStore::dequeue(TransactionContext*,
@@ -130,18 +110,15 @@ void NullMessageStore::dequeue(TransactionContext*,
msg->dequeueComplete();
}
-void NullMessageStore::flush(const qpid::broker::PersistableQueue&)
-{
-}
+void NullMessageStore::flush(const qpid::broker::PersistableQueue&) {}
-uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& )
-{
+uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) {
return 0;
}
std::auto_ptr<TransactionContext> NullMessageStore::begin()
{
- return std::auto_ptr<TransactionContext>();
+ return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt());
}
std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string& xid)
@@ -168,3 +145,21 @@ void NullMessageStore::collectPreparedXids(std::set<string>& out)
{
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/cpp/src/qpid/broker/NullMessageStore.h b/cpp/src/qpid/broker/NullMessageStore.h
index 4b8d02d555..e148ec4d51 100644
--- a/cpp/src/qpid/broker/NullMessageStore.h
+++ b/cpp/src/qpid/broker/NullMessageStore.h
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -22,8 +22,9 @@
#define _NullMessageStore_
#include <set>
-#include "MessageStore.h"
-#include "Queue.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Queue.h"
#include <boost/intrusive_ptr.hpp>
@@ -36,47 +37,58 @@ namespace broker {
class NullMessageStore : public MessageStore
{
std::set<std::string> prepared;
- const bool warn;
uint64_t nextPersistenceId;
public:
- NullMessageStore(bool warn = false);
+ QPID_BROKER_EXTERN NullMessageStore();
- virtual bool init(const Options* options);
- virtual std::auto_ptr<TransactionContext> begin();
- virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
- virtual void prepare(TPCTransactionContext& txn);
- virtual void commit(TransactionContext& txn);
- virtual void abort(TransactionContext& txn);
- virtual void collectPreparedXids(std::set<std::string>& xids);
+ 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);
- virtual void create(PersistableQueue& queue, const framing::FieldTable& args);
- virtual void destroy(PersistableQueue& queue);
- virtual void create(const PersistableExchange& exchange, const framing::FieldTable& args);
- virtual void destroy(const PersistableExchange& exchange);
+ 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);
- virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue,
- const std::string& key, const framing::FieldTable& args);
- virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue,
- const std::string& key, const framing::FieldTable& args);
- virtual void create(const PersistableConfig& config);
- virtual void destroy(const PersistableConfig& config);
- virtual void recover(RecoveryManager& queues);
- virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg);
- virtual void destroy(PersistableMessage& msg);
- virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
- const std::string& 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);
- virtual void enqueue(TransactionContext* ctxt,
- const boost::intrusive_ptr<PersistableMessage>& msg,
- const PersistableQueue& queue);
- virtual void dequeue(TransactionContext* ctxt,
- const boost::intrusive_ptr<PersistableMessage>& msg,
- const PersistableQueue& queue);
- virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue);
- virtual void flush(const qpid::broker::PersistableQueue& queue);
+ 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*);
};
}
diff --git a/cpp/src/qpid/broker/PersistableConfig.h b/cpp/src/qpid/broker/PersistableConfig.h
index 914e91ea80..8ddb84d129 100644
--- a/cpp/src/qpid/broker/PersistableConfig.h
+++ b/cpp/src/qpid/broker/PersistableConfig.h
@@ -23,7 +23,7 @@
*/
#include <string>
-#include "Persistable.h"
+#include "qpid/broker/Persistable.h"
namespace qpid {
namespace broker {
diff --git a/cpp/src/qpid/broker/PersistableExchange.h b/cpp/src/qpid/broker/PersistableExchange.h
index 683b740ddc..e1a0853247 100644
--- a/cpp/src/qpid/broker/PersistableExchange.h
+++ b/cpp/src/qpid/broker/PersistableExchange.h
@@ -23,7 +23,7 @@
*/
#include <string>
-#include "Persistable.h"
+#include "qpid/broker/Persistable.h"
namespace qpid {
namespace broker {
diff --git a/cpp/src/qpid/broker/PersistableMessage.cpp b/cpp/src/qpid/broker/PersistableMessage.cpp
index 3bf390faf3..303a0501f4 100644
--- a/cpp/src/qpid/broker/PersistableMessage.cpp
+++ b/cpp/src/qpid/broker/PersistableMessage.cpp
@@ -20,12 +20,25 @@
*/
-#include "PersistableMessage.h"
-#include "MessageStore.h"
+#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() :
+ asyncEnqueueCounter(0),
+ asyncDequeueCounter(0),
+ store(0)
+{}
+
void PersistableMessage::flush()
{
syncList copy;
@@ -45,4 +58,126 @@ void PersistableMessage::flush()
}
}
+void PersistableMessage::setContentReleased()
+{
+ contentReleaseState.released = true;
+}
+
+bool PersistableMessage::isContentReleased() const
+{
+ return contentReleaseState.released;
+}
+
+bool PersistableMessage::isEnqueueComplete() {
+ sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
+ return asyncEnqueueCounter == 0;
+}
+
+void PersistableMessage::enqueueComplete() {
+ bool notify = false;
+ {
+ sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
+ if (asyncEnqueueCounter > 0) {
+ if (--asyncEnqueueCounter == 0) {
+ notify = true;
+ }
+ }
+ }
+ if (notify) {
+ allEnqueuesComplete();
+ sys::ScopedLock<sys::Mutex> l(storeLock);
+ if (store) {
+ for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) {
+ PersistableQueue::shared_ptr q(i->lock());
+ if (q) q->notifyDurableIOComplete();
+ }
+ }
+ }
+}
+
+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);
+ enqueueAsync();
+}
+
+void PersistableMessage::enqueueAsync() {
+ sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
+ asyncEnqueueCounter++;
+}
+
+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;
+}
+
+}}
+
+
diff --git a/cpp/src/qpid/broker/PersistableMessage.h b/cpp/src/qpid/broker/PersistableMessage.h
index 7ed54c0ff0..7d49491dfd 100644
--- a/cpp/src/qpid/broker/PersistableMessage.h
+++ b/cpp/src/qpid/broker/PersistableMessage.h
@@ -26,10 +26,11 @@
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
-#include "Persistable.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Persistable.h"
#include "qpid/framing/amqp_types.h"
-#include "qpid/sys/Monitor.h"
-#include "PersistableQueue.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/PersistableQueue.h"
namespace qpid {
namespace broker {
@@ -41,10 +42,11 @@ class MessageStore;
*/
class PersistableMessage : public Persistable
{
- sys::Monitor asyncEnqueueLock;
- sys::Monitor asyncDequeueLock;
+ typedef std::list< boost::weak_ptr<PersistableQueue> > syncList;
+ sys::Mutex asyncEnqueueLock;
+ sys::Mutex asyncDequeueLock;
sys::Mutex storeLock;
-
+
/**
* Tracks the number of outstanding asynchronous enqueue
* operations. When the message is enqueued asynchronously the
@@ -62,15 +64,33 @@ class PersistableMessage : public Persistable
* dequeues.
*/
int asyncDequeueCounter;
-protected:
- typedef std::list< boost::weak_ptr<PersistableQueue> > syncList;
+
+ void enqueueAsync();
+ void dequeueAsync();
+
syncList synclist;
+ struct ContentReleaseState
+ {
+ bool blocked;
+ bool requested;
+ bool released;
+
+ ContentReleaseState();
+ };
+ ContentReleaseState contentReleaseState;
+
+ protected:
+ /** Called when all enqueues are complete for this message. */
+ virtual void allEnqueuesComplete() = 0;
+ /** Called when all dequeues are complete for this message. */
+ virtual void allDequeuesComplete() = 0;
+
+ void setContentReleased();
+
MessageStore* store;
- bool contentReleased;
-
- inline void setContentReleased() {contentReleased = true; }
-public:
+
+ public:
typedef boost::shared_ptr<PersistableMessage> shared_ptr;
/**
@@ -78,105 +98,39 @@ public:
*/
virtual uint32_t encodedHeaderSize() const = 0;
- virtual ~PersistableMessage() {};
+ virtual ~PersistableMessage();
- PersistableMessage():
- asyncEnqueueCounter(0),
- asyncDequeueCounter(0),
- store(0),
- contentReleased(false)
- {}
+ PersistableMessage();
void flush();
- inline bool isContentReleased()const {return contentReleased; }
-
- inline void waitForEnqueueComplete() {
- sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock);
- while (asyncEnqueueCounter > 0) {
- asyncEnqueueLock.wait();
- }
- }
-
- inline bool isEnqueueComplete() {
- sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock);
- return asyncEnqueueCounter == 0;
- }
-
- inline void enqueueComplete() {
- bool notify = false;
- {
- sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock);
- if (asyncEnqueueCounter > 0) {
- if (--asyncEnqueueCounter == 0) {
- asyncEnqueueLock.notify();
- notify = true;
- }
- }
- }
- if (notify) {
- sys::ScopedLock<sys::Mutex> l(storeLock);
- if (store) {
- for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) {
- PersistableQueue::shared_ptr q(i->lock());
- if (q) q->notifyDurableIOComplete();
- }
- }
- }
- }
-
- inline void enqueueAsync(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);
- }
- enqueueAsync();
- }
-
- inline void enqueueAsync() {
- sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock);
- asyncEnqueueCounter++;
- }
-
- inline bool isDequeueComplete() {
- sys::ScopedLock<sys::Monitor> l(asyncDequeueLock);
- return asyncDequeueCounter == 0;
- }
+ QPID_BROKER_EXTERN bool isContentReleased() const;
+
+ QPID_BROKER_EXTERN void setStore(MessageStore*);
+ void requestContentRelease();
+ void blockContentRelease();
+ bool checkContentReleasable();
+
+ virtual QPID_BROKER_EXTERN bool isPersistent() const = 0;
+
+ QPID_BROKER_EXTERN bool isEnqueueComplete();
+
+ QPID_BROKER_EXTERN void enqueueComplete();
+
+ QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue,
+ MessageStore* _store);
+
+
+ QPID_BROKER_EXTERN bool isDequeueComplete();
- inline void dequeueComplete() {
-
- sys::ScopedLock<sys::Monitor> l(asyncDequeueLock);
- if (asyncDequeueCounter > 0) {
- if (--asyncDequeueCounter == 0) {
- asyncDequeueLock.notify();
- }
- }
- }
-
- inline void waitForDequeueComplete() {
- sys::ScopedLock<sys::Monitor> l(asyncDequeueLock);
- while (asyncDequeueCounter > 0) {
- asyncDequeueLock.wait();
- }
- }
-
- inline void 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();
- }
-
- inline void dequeueAsync() {
- sys::ScopedLock<sys::Monitor> l(asyncDequeueLock);
- asyncDequeueCounter++;
- }
+ 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);
};
diff --git a/cpp/src/qpid/broker/PersistableQueue.h b/cpp/src/qpid/broker/PersistableQueue.h
index 9236814ae3..8d85d36fef 100644
--- a/cpp/src/qpid/broker/PersistableQueue.h
+++ b/cpp/src/qpid/broker/PersistableQueue.h
@@ -23,7 +23,7 @@
*/
#include <string>
-#include "Persistable.h"
+#include "qpid/broker/Persistable.h"
#include "qpid/management/Manageable.h"
#include <boost/shared_ptr.hpp>
diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp
index 40dfb80da2..f4231f2397 100644
--- a/cpp/src/qpid/broker/Queue.cpp
+++ b/cpp/src/qpid/broker/Queue.cpp
@@ -19,19 +19,23 @@
*
*/
-#include "Broker.h"
-#include "Queue.h"
-#include "Exchange.h"
-#include "DeliverableMessage.h"
-#include "MessageStore.h"
-#include "QueueRegistry.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/QueueRegistry.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/Monitor.h"
#include "qpid/sys/Time.h"
-#include "qpid/management/ArgsQueuePurge.h"
+#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h"
#include <iostream>
#include <algorithm>
@@ -49,11 +53,34 @@ 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 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");
+//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) :
+ Manageable* parent,
+ Broker* b) :
name(_name),
autodelete(_autodelete),
@@ -62,22 +89,31 @@ Queue::Queue(const string& _name, bool _autodelete,
consumerCount(0),
exclusive(0),
noLocal(false),
+ lastValueQueue(false),
+ lastValueQueueNoBrowse(false),
+ persistLastNode(false),
+ inLastNodeFailure(false),
persistenceId(0),
policyExceeded(false),
- mgmtObject(0)
+ mgmtObject(0),
+ eventMode(0),
+ eventMgr(0),
+ insertSeqNo(0),
+ broker(b)
{
- if (parent != 0)
+ if (parent != 0 && broker != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtObject = new management::Queue (agent, this, parent, _name, _store != 0, _autodelete, _owner != 0);
+ mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete, _owner != 0);
// Add the object to the management agent only if this queue is not durable.
// If it's durable, we will add it later when the queue is assigned a persistenceId.
- if (store == 0)
- agent->addObject (mgmtObject);
+ if (store == 0) {
+ agent->addObject (mgmtObject, agent->allocateId(this));
+ }
}
}
}
@@ -90,8 +126,12 @@ Queue::~Queue()
void Queue::notifyDurableIOComplete()
{
- Mutex::ScopedLock locker(messageLock);
- notify();
+ QueueListeners::NotificationSet copy;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ listeners.populate(copy);
+ }
+ copy.notify();
}
bool isLocalTo(const OwnershipToken* token, boost::intrusive_ptr<Message>& msg)
@@ -129,35 +169,31 @@ void Queue::deliver(boost::intrusive_ptr<Message>& msg){
} else {
// if no store then mark as enqueued
if (!enqueue(0, msg)){
- if (mgmtObject != 0) {
- mgmtObject->inc_msgTotalEnqueues ();
- mgmtObject->inc_byteTotalEnqueues (msg->contentSize ());
- }
push(msg);
msg->enqueueComplete();
}else {
- if (mgmtObject != 0) {
- mgmtObject->inc_msgTotalEnqueues ();
- mgmtObject->inc_byteTotalEnqueues (msg->contentSize ());
- mgmtObject->inc_msgPersistEnqueues ();
- mgmtObject->inc_bytePersistEnqueues (msg->contentSize ());
- }
push(msg);
}
+ mgntEnqStats(msg);
QPID_LOG(debug, "Message " << msg << " enqueued on " << name << "[" << this << "]");
}
}
+void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg)
+{
+ if (policy.get()) policy->recoverEnqueued(msg);
+}
void Queue::recover(boost::intrusive_ptr<Message>& msg){
- push(msg);
- msg->enqueueComplete(); // mark the message as enqueued
- if (mgmtObject != 0) {
- mgmtObject->inc_msgTotalEnqueues ();
- mgmtObject->inc_byteTotalEnqueues (msg->contentSize ());
- mgmtObject->inc_msgPersistEnqueues ();
- mgmtObject->inc_bytePersistEnqueues (msg->contentSize ());
+ 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);
}
+ msg->enqueueComplete(); // mark the message as enqueued
+ mgntEnqStats(msg);
if (store && !msg->isContentLoaded()) {
//content has not been loaded, need to ensure that lazy loading mode is set:
@@ -168,121 +204,188 @@ void Queue::recover(boost::intrusive_ptr<Message>& msg){
void Queue::process(boost::intrusive_ptr<Message>& msg){
push(msg);
- if (mgmtObject != 0) {
- mgmtObject->inc_msgTotalEnqueues ();
- mgmtObject->inc_byteTotalEnqueues (msg->contentSize ());
+ mgntEnqStats(msg);
+ if (mgmtObject != 0){
mgmtObject->inc_msgTxnEnqueues ();
mgmtObject->inc_byteTxnEnqueues (msg->contentSize ());
- if (msg->isPersistent ()) {
- mgmtObject->inc_msgPersistEnqueues ();
- mgmtObject->inc_bytePersistEnqueues (msg->contentSize ());
- }
}
}
void Queue::requeue(const QueuedMessage& msg){
+ QueueListeners::NotificationSet copy;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (!isEnqueued(msg)) return;
+ msg.payload->enqueueComplete(); // mark the message as enqueued
+ messages.insert(lower_bound(messages.begin(), messages.end(), msg), 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() ){
+ enqueue(0, msg.payload);
+ }
+ }
+ }
+ copy.notify();
+}
+
+void Queue::clearLVQIndex(const QueuedMessage& msg){
+ const framing::FieldTable* ft = msg.payload ? msg.payload->getApplicationHeaders() : 0;
+ if (lastValueQueue && ft){
+ string key = ft->getAsString(qpidVQMatchProperty);
+ lvq.erase(key);
+ }
+}
+
+bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message)
+{
Mutex::ScopedLock locker(messageLock);
- msg.payload->enqueueComplete(); // mark the message as enqueued
- messages.push_front(msg);
- notify();
+ QPID_LOG(debug, "Attempting to acquire message at " << position);
+
+ Messages::iterator i = findAt(position);
+ if (i != messages.end() ) {
+ message = *i;
+ if (lastValueQueue) {
+ clearLVQIndex(*i);
+ }
+ QPID_LOG(debug,
+ "Acquired message at " << i->position << " from " << name);
+ messages.erase(i);
+ return true;
+ }
+ QPID_LOG(debug, "Could not acquire message at " << position << " from " << name << "; no message at that position");
+ return false;
}
bool Queue::acquire(const QueuedMessage& msg) {
Mutex::ScopedLock locker(messageLock);
QPID_LOG(debug, "attempting to acquire " << msg.position);
- for (Messages::iterator i = messages.begin(); i != messages.end(); i++) {
- if (i->position == msg.position) {
- messages.erase(i);
- QPID_LOG(debug, "Match found, acquire succeeded: " << i->position << " == " << msg.position);
- return true;
- } else {
- QPID_LOG(debug, "No match: " << i->position << " != " << msg.position);
- }
+ Messages::iterator i = findAt(msg.position);
+ if ((i != messages.end() && !lastValueQueue) // note that in some cases payload not be set
+ || (lastValueQueue && (i->position == msg.position) &&
+ msg.payload.get() == checkLvqReplace(*i).payload.get()) ) {
+
+ clearLVQIndex(msg);
+ QPID_LOG(debug,
+ "Match found, acquire succeeded: " <<
+ i->position << " == " << msg.position);
+ messages.erase(i);
+ return true;
+ } else {
+ QPID_LOG(debug, "No match: " << i->position << " != " << msg.position);
}
+
QPID_LOG(debug, "Acquire failed for " << msg.position);
return false;
}
-bool Queue::getNextMessage(QueuedMessage& m, Consumer& c)
+void Queue::notifyListener()
{
- if (c.preAcquires()) {
- return consumeNextMessage(m, c);
+ QueueListeners::NotificationSet set;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (messages.size()) {
+ listeners.populate(set);
+ }
+ }
+ set.notify();
+}
+
+bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+{
+ 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);
}
}
-bool Queue::checkForMessages(Consumer& c)
+bool Queue::checkForMessages(Consumer::shared_ptr c)
{
Mutex::ScopedLock locker(messageLock);
if (messages.empty()) {
//no message available, register consumer for notification
//when this changes
- addListener(c);
+ listeners.addListener(c);
return false;
} else {
- QueuedMessage msg = messages.front();
+ QueuedMessage msg = getFront();
if (store && !msg.payload->isEnqueueComplete()) {
//though a message is on the queue, it has not yet been
//enqueued and so is not available for consumption yet,
//register consumer for notification when this changes
- addListener(c);
+ listeners.addListener(c);
return false;
} else {
//check that consumer has sufficient credit for the
//message (if it does not, no need to register it for
//notification as the consumer itself will handle the
//credit allocation required to change this condition).
- return c.accept(msg.payload);
+ return c->accept(msg.payload);
}
}
}
-bool Queue::consumeNextMessage(QueuedMessage& m, Consumer& 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 << "'");
- addListener(c);
- return false;
+ listeners.addListener(c);
+ return NO_MESSAGES;
} else {
- QueuedMessage msg = messages.front();
- if (store && !msg.payload->isEnqueueComplete()) {
- QPID_LOG(debug, "Messages not ready to dispatch on queue '" << name << "'");
- addListener(c);
- return false;
+ QueuedMessage msg = getFront();
+ if (msg.payload->hasExpired()) {
+ QPID_LOG(debug, "Message expired from queue '" << name << "'");
+ popAndDequeue();
+ continue;
}
-
- if (c.filter(msg.payload)) {
- if (c.accept(msg.payload)) {
+
+ if (c->filter(msg.payload)) {
+ if (c->accept(msg.payload)) {
m = msg;
- messages.pop_front();
- return true;
+ popMsg(msg);
+ 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 false;
+ return CANT_CONSUME;
}
} else {
//consumer will never want this message
QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
- return false;
+ return CANT_CONSUME;
}
}
}
}
-bool Queue::browseNextMessage(QueuedMessage& m, Consumer& c)
+bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
{
QueuedMessage msg(this);
while (seek(msg, c)) {
- if (c.filter(msg.payload)) {
- if (c.accept(msg.payload)) {
+ if (c->filter(msg.payload) && !msg.payload->hasExpired()) {
+ if (c->accept(msg.payload)) {
//consumer wants the message
- c.position = msg.position;
+ c->position = msg.position;
m = msg;
+ if (!lastValueQueueNoBrowse) clearLVQIndex(msg);
+ if (lastValueQueue) {
+ boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this);
+ if (replacement.get()) m.payload = replacement;
+ }
return true;
} else {
//browser hasn't got enough credit for the message
@@ -291,70 +394,89 @@ bool Queue::browseNextMessage(QueuedMessage& m, Consumer& c)
}
} else {
//consumer will never want this message, continue seeking
- c.position = msg.position;
+ c->position = msg.position;
QPID_LOG(debug, "Browser skipping message from '" << name << "'");
}
}
return false;
}
-/**
- * notify listeners that there may be messages to process
- */
-void Queue::notify()
-{
- if (listeners.empty()) return;
-
- Listeners copy(listeners);
- listeners.clear();
- for_each(copy.begin(), copy.end(), mem_fun(&Consumer::notify));
-}
-
-void Queue::removeListener(Consumer& c)
-{
- Mutex::ScopedLock locker(messageLock);
- Listeners::iterator i = std::find(listeners.begin(), listeners.end(), &c);
- if (i != listeners.end()) listeners.erase(i);
-}
-
-void Queue::addListener(Consumer& c)
+void Queue::removeListener(Consumer::shared_ptr c)
{
- Listeners::iterator i = std::find(listeners.begin(), listeners.end(), &c);
- if (i == listeners.end()) listeners.push_back(&c);
+ QueueListeners::NotificationSet set;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ listeners.removeListener(c);
+ if (messages.size()) {
+ listeners.populate(set);
+ }
+ }
+ set.notify();
}
-bool Queue::dispatch(Consumer& c)
+bool Queue::dispatch(Consumer::shared_ptr c)
{
QueuedMessage msg(this);
if (getNextMessage(msg, c)) {
- c.deliver(msg);
+ c->deliver(msg);
return true;
} else {
return false;
}
}
-bool Queue::seek(QueuedMessage& msg, Consumer& c) {
+// Find the next message
+bool Queue::seek(QueuedMessage& msg, Consumer::shared_ptr c) {
Mutex::ScopedLock locker(messageLock);
- if (!messages.empty() && messages.back().position > c.position) {
- if (c.position < messages.front().position) {
- msg = messages.front();
+ if (!messages.empty() && messages.back().position > c->position) {
+ if (c->position < getFront().position) {
+ msg = getFront();
return true;
} else {
- //TODO: can improve performance of this search, for now just searching linearly from end
- Messages::reverse_iterator pos;
- for (Messages::reverse_iterator i = messages.rbegin(); i != messages.rend() && i->position > c.position; i++) {
- pos = i;
+ Messages::iterator pos = findAt(c->position);
+ if (pos != messages.end() && pos+1 != messages.end()) {
+ msg = *(pos+1);
+ return true;
}
- msg = *pos;
- return true;
}
}
- addListener(c);
+ listeners.addListener(c);
return false;
}
-void Queue::consume(Consumer& c, bool requestExclusive){
+Queue::Messages::iterator Queue::findAt(SequenceNumber pos) {
+
+ if(!messages.empty()){
+ QueuedMessage compM;
+ compM.position = pos;
+ unsigned long diff = pos.getValue() - messages.front().position.getValue();
+ long maxEnd = diff < messages.size()? diff : messages.size();
+
+ Messages::iterator i = lower_bound(messages.begin(),messages.begin()+maxEnd,compM);
+ if (i!= messages.end() && i->position == pos)
+ return i;
+ }
+ return messages.end(); // no match found.
+}
+
+
+QueuedMessage Queue::find(SequenceNumber pos) const {
+
+ Mutex::ScopedLock locker(messageLock);
+ if(!messages.empty()){
+ QueuedMessage compM;
+ compM.position = pos;
+ unsigned long diff = pos.getValue() - messages.front().position.getValue();
+ long maxEnd = diff < messages.size()? diff : messages.size();
+
+ Messages::const_iterator i = lower_bound(messages.begin(),messages.begin()+maxEnd,compM);
+ if (i != messages.end())
+ return *i;
+ }
+ return QueuedMessage();
+}
+
+void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){
Mutex::ScopedLock locker(consumerLock);
if(exclusive) {
throw ResourceLockedException(
@@ -364,7 +486,7 @@ void Queue::consume(Consumer& c, bool requestExclusive){
throw ResourceLockedException(
QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied."));
} else {
- exclusive = c.getSession();
+ exclusive = c->getSession();
}
}
consumerCount++;
@@ -372,7 +494,7 @@ void Queue::consume(Consumer& c, bool requestExclusive){
mgmtObject->inc_consumerCount ();
}
-void Queue::cancel(Consumer& c){
+void Queue::cancel(Consumer::shared_ptr c){
removeListener(c);
Mutex::ScopedLock locker(consumerLock);
consumerCount--;
@@ -386,12 +508,35 @@ QueuedMessage Queue::get(){
QueuedMessage msg(this);
if(!messages.empty()){
- msg = messages.front();
- messages.pop_front();
+ msg = getFront();
+ popMsg(msg);
}
return msg;
}
+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) {
+ Messages expired;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ for (Messages::iterator i = messages.begin(); i != messages.end();) {
+ if (lastValueQueue) checkLvqReplace(*i);
+ if (i->payload->hasExpired()) {
+ expired.push_back(*i);
+ i = messages.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+ for_each(expired.begin(), expired.end(), bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
+ }
+}
+
/**
* purge - for purging all or some messages on a queue
* depending on the purge_request
@@ -406,106 +551,252 @@ uint32_t Queue::purge(const uint32_t purge_request){
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())
- {
+ while((!purge_request || purge_count--) && !messages.empty()) {
popAndDequeue();
- count++;
+ count++;
}
return count;
}
-void Queue::push(boost::intrusive_ptr<Message>& msg){
- Mutex::ScopedLock locker(messageLock);
- messages.push_back(QueuedMessage(this, msg, ++sequence));
- if (policy.get()) {
- policy->enqueued(msg->contentSize());
- if (policy->limitExceeded()) {
- if (!policyExceeded) {
- policyExceeded = true;
- QPID_LOG(info, "Queue size exceeded policy for " << name);
- }
- if (store) {
- QPID_LOG(debug, "Message " << msg << " on " << name << " released from memory");
- msg->releaseContent(store);
- } else {
- QPID_LOG(error, "Message " << msg << " on " << name
- << " exceeds the policy for the queue but can't be released from memory as the queue is not durable");
- throw ResourceLimitExceededException(QPID_MSG("Policy exceeded for " << name << " " << *policy));
- }
- } else {
- if (policyExceeded) {
- policyExceeded = false;
- QPID_LOG(info, "Queue size within policy for " << name);
- }
+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 = getFront();
+ boost::intrusive_ptr<Message> msg = qmsg.payload;
+ destq->deliver(msg); // deliver message to the destination queue
+ popMsg(qmsg);
+ dequeue(0, qmsg);
+ count++;
+ }
+ return count;
+}
+
+void Queue::popMsg(QueuedMessage& qmsg)
+{
+ const framing::FieldTable* ft = qmsg.payload->getApplicationHeaders();
+ if (lastValueQueue && ft){
+ string key = ft->getAsString(qpidVQMatchProperty);
+ lvq.erase(key);
+ }
+ messages.pop_front();
+ ++dequeueTracker;
+}
+
+void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
+ QueueListeners::NotificationSet copy;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ QueuedMessage qm(this, msg, ++sequence);
+ if (insertSeqNo) msg->getOrInsertHeaders().setInt64(seqNoKey, sequence);
+
+ LVQ::iterator i;
+ const framing::FieldTable* ft = msg->getApplicationHeaders();
+ if (lastValueQueue && ft){
+ string key = ft->getAsString(qpidVQMatchProperty);
+
+ i = lvq.find(key);
+ if (i == lvq.end() || (broker && broker->isClusterUpdatee())) {
+ messages.push_back(qm);
+ listeners.populate(copy);
+ lvq[key] = msg;
+ }else {
+ boost::intrusive_ptr<Message> old = i->second->getReplacementMessage(this);
+ if (!old) old = i->second;
+ i->second->setReplacementMessage(msg,this);
+ if (isRecovery) {
+ //can't issue new requests for the store until
+ //recovery is complete
+ pendingDequeues.push_back(QueuedMessage(qm.queue, old, qm.position));
+ } else {
+ Mutex::ScopedUnlock u(messageLock);
+ dequeue(0, QueuedMessage(qm.queue, old, qm.position));
+ }
+ }
+ }else {
+ messages.push_back(qm);
+ listeners.populate(copy);
+ }
+ if (eventMode) {
+ if (eventMgr) eventMgr->enqueued(qm);
+ else QPID_LOG(warning, "Enqueue manager not set, events not generated for " << getName());
+ }
+ if (policy.get()) {
+ policy->enqueued(qm);
}
}
- notify();
+ copy.notify();
+}
+
+QueuedMessage Queue::getFront()
+{
+ QueuedMessage msg = messages.front();
+ if (lastValueQueue) {
+ boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this);
+ if (replacement.get()) msg.payload = replacement;
+ }
+ return msg;
+}
+
+QueuedMessage& Queue::checkLvqReplace(QueuedMessage& msg)
+{
+ boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this);
+ if (replacement.get()) {
+ const framing::FieldTable* ft = replacement->getApplicationHeaders();
+ if (ft) {
+ string key = ft->getAsString(qpidVQMatchProperty);
+ if (lvq.find(key) != lvq.end()){
+ lvq[key] = replacement;
+ }
+ }
+ msg.payload = replacement;
+ }
+ return msg;
}
/** function only provided for unit tests, or code not in critical message path */
-uint32_t Queue::getMessageCount() const{
+uint32_t Queue::getEnqueueCompleteMessageCount() const
+{
Mutex::ScopedLock locker(messageLock);
-
- uint32_t count =0;
+ uint32_t count = 0;
for ( Messages::const_iterator i = messages.begin(); i != messages.end(); ++i ) {
+ //NOTE: don't need to use checkLvqReplace() here as it
+ //is only relevant for LVQ which does not support persistence
+ //so the enqueueComplete check has no effect
if ( i->payload->isEnqueueComplete() ) count ++;
}
return count;
}
-uint32_t Queue::getConsumerCount() const{
+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{
+bool Queue::canAutoDelete() const
+{
Mutex::ScopedLock locker(consumerLock);
return autodelete && !consumerCount;
}
+void Queue::clearLastNodeFailure()
+{
+ inLastNodeFailure = false;
+}
+
+void Queue::setLastNodeFailure()
+{
+ if (persistLastNode){
+ Mutex::ScopedLock locker(messageLock);
+ try {
+ for ( Messages::iterator i = messages.begin(); i != messages.end(); ++i ) {
+ if (lastValueQueue) checkLvqReplace(*i);
+ // don't force a message twice to disk.
+ if(!i->payload->isStoredOnQueue(shared_from_this())) {
+ i->payload->forcePersistent();
+ if (i->payload->isForcedPersistent() ){
+ enqueue(0, i->payload);
+ }
+ }
+ }
+ } 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 Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg, bool suppressPolicyCheck)
{
+ if (policy.get() && !suppressPolicyCheck) {
+ Messages 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()) {
msg->addTraceId(traceId);
}
- if (msg->isPersistent() && store) {
+ if ((msg->isPersistent() || msg->checkContentReleasable()) && store) {
msg->enqueueAsync(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);
store->enqueue(ctxt, pmsg, *this);
return true;
}
- //msg->enqueueAsync(); // increments intrusive ptr cnt
+ 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, boost::intrusive_ptr<Message> msg)
+bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
{
{
Mutex::ScopedLock locker(messageLock);
- dequeued(msg);
+ if (!isEnqueued(msg)) return false;
+ if (!ctxt) {
+ dequeued(msg);
+ }
}
- if (msg->isPersistent() && store) {
- msg->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);
+ 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;
}
- //msg->dequeueAsync(); // decrements intrusive ptr cnt
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()
{
- boost::intrusive_ptr<Message> msg = messages.front().payload;
- messages.pop_front();
+ QueuedMessage msg = getFront();
+ popMsg(msg);
dequeue(0, msg);
}
@@ -513,29 +804,16 @@ void Queue::popAndDequeue()
* Updates policy and management when a message has been dequeued,
* expects messageLock to be held
*/
-void Queue::dequeued(boost::intrusive_ptr<Message>& msg)
+void Queue::dequeued(const QueuedMessage& msg)
{
- if (policy.get()) policy->dequeued(msg->contentSize());
- if (mgmtObject != 0){
- mgmtObject->inc_msgTotalDequeues ();
- mgmtObject->inc_byteTotalDequeues (msg->contentSize());
- if (msg->isPersistent ()){
- mgmtObject->inc_msgPersistDequeues ();
- mgmtObject->inc_bytePersistDequeues (msg->contentSize());
- }
+ if (policy.get()) policy->dequeued(msg);
+ mgntDeqStats(msg.payload);
+ if (eventMode == ENQUEUE_AND_DEQUEUE && eventMgr) {
+ eventMgr->dequeued(msg);
}
}
-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");
-}
-
void Queue::create(const FieldTable& _settings)
{
settings = _settings;
@@ -545,26 +823,56 @@ void Queue::create(const FieldTable& _settings)
configure(_settings);
}
-void Queue::configure(const FieldTable& _settings)
+void Queue::configure(const FieldTable& _settings, bool recovering)
{
- std::auto_ptr<QueuePolicy> _policy(new QueuePolicy(_settings));
- if (_policy->getMaxCount() || _policy->getMaxSize()) {
- setPolicy(_policy);
+
+ eventMode = _settings.getAsInt(qpidQueueEventGeneration);
+
+ if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK &&
+ (!store || NullMessageStore::isNullStore(store) || (eventMode && eventMgr && !eventMgr->isSync()) )) {
+ if ( NullMessageStore::isNullStore(store)) {
+ QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName());
+ } else if (eventMgr && !eventMgr->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));
}
//set this regardless of owner to allow use of no-local with exclusive consumers also
noLocal = _settings.get(qpidNoLocal);
- QPID_LOG(debug, "Configured queue with no-local=" << noLocal);
+ QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal);
+
+ lastValueQueue= _settings.get(qpidLastValueQueue);
+ if (lastValueQueue) QPID_LOG(debug, "Configured queue as Last Value Queue for: " << getName());
- traceId = _settings.getString(qpidTraceIdentity);
- std::string excludeList = _settings.getString(qpidTraceExclude);
+ lastValueQueueNoBrowse = _settings.get(qpidLastValueQueueNoBrowse);
+ if (lastValueQueueNoBrowse){
+ QPID_LOG(debug, "Configured queue as Last Value Queue No Browse for: " << getName());
+ lastValueQueue = lastValueQueueNoBrowse;
+ }
+
+ 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>());
+
if (mgmtObject != 0)
mgmtObject->set_arguments (_settings);
+
+ if ( isDurable() && ! getPersistenceId() && ! recovering )
+ store->create(*this, _settings);
}
void Queue::destroy()
@@ -572,7 +880,7 @@ void Queue::destroy()
if (alternateExchange.get()) {
Mutex::ScopedLock locker(messageLock);
while(!messages.empty()){
- DeliverableMessage msg(messages.front().payload);
+ DeliverableMessage msg(getFront().payload);
alternateExchange->route(msg, msg.getMessage().getRoutingKey(),
msg.getMessage().getApplicationHeaders());
popAndDequeue();
@@ -617,8 +925,8 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const
{
if (mgmtObject != 0 && persistenceId == 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
- agent->addObject (mgmtObject, _persistenceId, 3);
+ ManagementAgent* agent = broker->getManagementAgent();
+ agent->addObject (mgmtObject, 0x3000000000000000LL + _persistenceId);
if (externalQueueStore) {
ManagementObject* childObj = externalQueueStore->GetManagementObject();
@@ -629,24 +937,40 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const
persistenceId = _persistenceId;
}
-void Queue::encode(framing::Buffer& buffer) const
+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*/ + settings.size();
+ 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::decode(QueueRegistry& queues, framing::Buffer& buffer)
+Queue::shared_ptr Queue::decode ( QueueRegistry& queues, Buffer& buffer, bool recovering )
{
string name;
buffer.getShortString(name);
std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true);
buffer.get(result.first->settings);
- result.first->configure(result.first->settings);
+ result.first->configure(result.first->settings, recovering );
+ 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;
}
@@ -654,6 +978,12 @@ Queue::shared_ptr Queue::decode(QueueRegistry& queues, framing::Buffer& buffer)
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()
@@ -721,8 +1051,7 @@ ManagementObject* Queue::GetManagementObject (void) const
return (ManagementObject*) mgmtObject;
}
-Manageable::status_t Queue::ManagementMethod (uint32_t methodId,
- Args& args)
+Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string&)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
@@ -730,8 +1059,8 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId,
switch (methodId)
{
- case management::Queue::METHOD_PURGE :
- management::ArgsQueuePurge iargs = dynamic_cast<const management::ArgsQueuePurge&>(args);
+ case _qmf::Queue::METHOD_PURGE :
+ _qmf::ArgsQueuePurge& iargs = (_qmf::ArgsQueuePurge&) args;
purge (iargs.i_request);
status = Manageable::STATUS_OK;
break;
@@ -739,3 +1068,63 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId,
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::setQueueEventManager(QueueEvents& mgr)
+{
+ eventMgr = &mgr;
+}
+
+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)
+{
+ if (m.payload) {
+ if (policy.get()) {
+ policy->recoverEnqueued(m.payload);
+ policy->enqueued(m);
+ }
+ mgntEnqStats(m.payload);
+ enqueue ( 0, m.payload, true );
+ } 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; }
diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h
index 8b8ba8278f..5b177f1cf2 100644
--- a/cpp/src/qpid/broker/Queue.h
+++ b/cpp/src/qpid/broker/Queue.h
@@ -21,31 +21,38 @@
* under the License.
*
*/
-#include "OwnershipToken.h"
-#include "Consumer.h"
-#include "Message.h"
-#include "PersistableQueue.h"
-#include "QueuePolicy.h"
-#include "QueueBindings.h"
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/OwnershipToken.h"
+#include "qpid/broker/Consumer.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueBindings.h"
+#include "qpid/broker/QueueListeners.h"
+#include "qpid/broker/RateTracker.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/sys/Monitor.h"
#include "qpid/management/Manageable.h"
-#include "qpid/management/Queue.h"
+#include "qmf/org/apache/qpid/broker/Queue.h"
#include "qpid/framing/amqp_types.h"
-#include <vector>
-#include <memory>
-#include <deque>
-
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/intrusive_ptr.hpp>
+#include <list>
+#include <vector>
+#include <memory>
+#include <deque>
+#include <algorithm>
+
namespace qpid {
namespace broker {
class Broker;
class MessageStore;
+ class QueueEvents;
class QueueRegistry;
class TransactionContext;
class Exchange;
@@ -60,8 +67,10 @@ namespace qpid {
*/
class Queue : public boost::enable_shared_from_this<Queue>,
public PersistableQueue, public management::Manageable {
- typedef qpid::InlineVector<Consumer*, 5> Listeners;
+
typedef std::deque<QueuedMessage> Messages;
+ typedef std::map<string,boost::intrusive_ptr<Message> > LVQ;
+ enum ConsumeCode {NO_MESSAGES=0, CANT_CONSUME=1, CONSUMED=2};
const string name;
const bool autodelete;
@@ -70,10 +79,16 @@ namespace qpid {
uint32_t consumerCount;
OwnershipToken* exclusive;
bool noLocal;
+ bool lastValueQueue;
+ bool lastValueQueueNoBrowse;
+ bool persistLastNode;
+ bool inLastNodeFailure;
std::string traceId;
std::vector<std::string> traceExclude;
- Listeners listeners;
+ QueueListeners listeners;
Messages messages;
+ Messages pendingDequeues;//used to avoid dequeuing during recovery
+ LVQ lvq;
mutable qpid::sys::Mutex consumerLock;
mutable qpid::sys::Mutex messageLock;
mutable qpid::sys::Mutex ownershipLock;
@@ -82,25 +97,59 @@ namespace qpid {
std::auto_ptr<QueuePolicy> policy;
bool policyExceeded;
QueueBindings bindings;
+ std::string alternateExchangeName;
boost::shared_ptr<Exchange> alternateExchange;
framing::SequenceNumber sequence;
- management::Queue* mgmtObject;
-
- void push(boost::intrusive_ptr<Message>& msg);
+ qmf::org::apache::qpid::broker::Queue* mgmtObject;
+ RateTracker dequeueTracker;
+ int eventMode;
+ QueueEvents* eventMgr;
+ bool insertSeqNo;
+ std::string seqNoKey;
+ Broker* broker;
+
+ void push(boost::intrusive_ptr<Message>& msg, bool isRecovery=false);
void setPolicy(std::auto_ptr<QueuePolicy> policy);
- bool seek(QueuedMessage& msg, Consumer& position);
- bool getNextMessage(QueuedMessage& msg, Consumer& c);
- bool consumeNextMessage(QueuedMessage& msg, Consumer& c);
- bool browseNextMessage(QueuedMessage& msg, Consumer& c);
+ 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 notify();
- void removeListener(Consumer&);
- void addListener(Consumer&);
+ void removeListener(Consumer::shared_ptr);
bool isExcluded(boost::intrusive_ptr<Message>& msg);
- void dequeued(boost::intrusive_ptr<Message>& msg);
+ void dequeued(const QueuedMessage& msg);
void popAndDequeue();
+ QueuedMessage getFront();
+ QueuedMessage& checkLvqReplace(QueuedMessage& msg);
+ void clearLVQIndex(const QueuedMessage& msg);
+
+ 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());
+ }
+ }
+ }
+
+ Messages::iterator findAt(framing::SequenceNumber pos);
public:
@@ -109,58 +158,73 @@ namespace qpid {
typedef std::vector<shared_ptr> vector;
- Queue(const string& name, bool autodelete = false,
- MessageStore* const store = 0,
- const OwnershipToken* const owner = 0,
- management::Manageable* parent = 0);
- ~Queue();
+ QPID_BROKER_EXTERN Queue(const 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();
- bool dispatch(Consumer&);
+ QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr);
/**
* Check whether there would be a message available for
* dispatch to this consumer. If not, the consumer will be
* notified of events that may have changed this
* situation.
*/
- bool checkForMessages(Consumer&);
+ bool checkForMessages(Consumer::shared_ptr);
void create(const qpid::framing::FieldTable& settings);
- void configure(const qpid::framing::FieldTable& settings);
+
+ // "recovering" means we are doing a MessageStore recovery.
+ QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings,
+ bool recovering = false);
void destroy();
- void bound(const string& exchange, const string& key, const qpid::framing::FieldTable& args);
- void unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref);
+ QPID_BROKER_EXTERN void bound(const string& exchange,
+ const string& key,
+ const qpid::framing::FieldTable& args);
+ QPID_BROKER_EXTERN void unbind(ExchangeRegistry& exchanges,
+ Queue::shared_ptr shared_ref);
- bool acquire(const QueuedMessage& msg);
+ 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.
*/
- void deliver(boost::intrusive_ptr<Message>& msg);
+ 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.
*/
- void process(boost::intrusive_ptr<Message>& msg);
+ 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.
*/
- void requeue(const QueuedMessage& msg);
+ QPID_BROKER_EXTERN void requeue(const QueuedMessage& msg);
/**
* Used during recovery to add stored messages back to the queue
*/
- void recover(boost::intrusive_ptr<Message>& msg);
+ QPID_BROKER_EXTERN void recover(boost::intrusive_ptr<Message>& msg);
- void consume(Consumer& c, bool exclusive = false);
- void cancel(Consumer& c);
+ 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); //defaults to all messages
+ QPID_BROKER_EXTERN void purgeExpired();
- uint32_t getMessageCount() const;
- uint32_t getConsumerCount() const;
+ //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 string& getName() const { return name; }
bool isExclusiveOwner(const OwnershipToken* const o) const;
void releaseExclusiveOwnership();
@@ -171,17 +235,50 @@ namespace qpid {
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 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)
*/
- bool dequeue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg);
+ 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 enqueued(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
*/
- QueuedMessage get();
+ QPID_BROKER_EXTERN QueuedMessage get();
+
+ /** Get the message at position pos */
+ QPID_BROKER_EXTERN QueuedMessage find(framing::SequenceNumber pos) const;
const QueuePolicy* getPolicy();
@@ -195,7 +292,8 @@ namespace qpid {
void encode(framing::Buffer& buffer) const;
uint32_t encodedSize() const;
- static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer);
+ // "recovering" means we are doing a MessageStore recovery.
+ static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer, bool recovering = false );
static void tryAutoDelete(Broker& broker, Queue::shared_ptr);
virtual void setExternalQueueStore(ExternalQueueStore* inst);
@@ -203,7 +301,51 @@ namespace qpid {
// Manageable entry points
management::ManagementObject* GetManagementObject (void) const;
management::Manageable::status_t
- ManagementMethod (uint32_t methodId, management::Args& args);
+ 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);
+ if (lastValueQueue) {
+ for (Messages::iterator i = messages.begin(); i != messages.end(); ++i) {
+ f(checkLvqReplace(*i));
+ }
+ } else {
+ std::for_each(messages.begin(), messages.end(), f);
+ }
+ }
+
+ /** Apply f to each QueueBinding on the queue */
+ template <class F> void eachBinding(F f) {
+ bindings.eachBinding(f);
+ }
+
+ void popMsg(QueuedMessage& qmsg);
+
+ /** 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();
+ int getEventMode();
+ void setQueueEventManager(QueueEvents&);
+ QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key);
+ /**
+ * Notify queue that recovery has completed.
+ */
+ void recoveryComplete(ExchangeRegistry& exchanges);
+
+ // For cluster update
+ QueueListeners& getListeners();
+
+ /**
+ * 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);
};
}
}
diff --git a/cpp/src/qpid/broker/QueueBindings.cpp b/cpp/src/qpid/broker/QueueBindings.cpp
index 95e529f47e..3f43a8ef68 100644
--- a/cpp/src/qpid/broker/QueueBindings.cpp
+++ b/cpp/src/qpid/broker/QueueBindings.cpp
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "QueueBindings.h"
-#include "ExchangeRegistry.h"
+#include "qpid/broker/QueueBindings.h"
+#include "qpid/broker/ExchangeRegistry.h"
#include "qpid/framing/reply_exceptions.h"
using qpid::framing::FieldTable;
@@ -29,7 +29,7 @@ using namespace qpid::broker;
void QueueBindings::add(const string& exchange, const string& key, const FieldTable& args)
{
- bindings.push_back(new Binding(exchange, key, args));
+ bindings.push_back(QueueBinding(exchange, key, args));
}
void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue)
@@ -37,11 +37,10 @@ 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&) {
- }
+ } catch (const NotFoundException&) {}
}
}
-QueueBindings::Binding::Binding(const string& _exchange, const string& _key, const FieldTable& _args)
+QueueBinding::QueueBinding(const string& _exchange, const string& _key, const FieldTable& _args)
: exchange(_exchange), key(_key), args(_args)
{}
diff --git a/cpp/src/qpid/broker/QueueBindings.h b/cpp/src/qpid/broker/QueueBindings.h
index b9b0f7c15c..1b90ba5540 100644
--- a/cpp/src/qpid/broker/QueueBindings.h
+++ b/cpp/src/qpid/broker/QueueBindings.h
@@ -24,32 +24,38 @@
#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
{
- struct Binding{
- const std::string exchange;
- const std::string key;
- const qpid::framing::FieldTable args;
- Binding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args);
- };
-
- typedef boost::ptr_list<Binding> Bindings;
- Bindings bindings;
+ public:
-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/cpp/src/qpid/broker/QueueCleaner.cpp b/cpp/src/qpid/broker/QueueCleaner.cpp
new file mode 100644
index 0000000000..c80fe89035
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueCleaner.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/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), parent(p) {}
+
+void QueueCleaner::Task::fire()
+{
+ parent.fired();
+}
+
+void QueueCleaner::fired()
+{
+ queues.eachQueue(boost::bind(&Queue::purgeExpired, _1));
+ task->setupNextFire();
+ timer.add(task);
+}
+
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/QueueCleaner.h b/cpp/src/qpid/broker/QueueCleaner.h
new file mode 100644
index 0000000000..11c2d180ac
--- /dev/null
+++ b/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/cpp/src/qpid/broker/QueueEvents.cpp b/cpp/src/qpid/broker/QueueEvents.cpp
new file mode 100644
index 0000000000..bba054b0b8
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueEvents.cpp
@@ -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 "qpid/broker/QueueEvents.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;
+}
+
+
+QueueEvents::Event::Event(EventType t, const QueuedMessage& m) : type(t), msg(m) {}
+
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/QueueEvents.h b/cpp/src/qpid/broker/QueueEvents.h
new file mode 100644
index 0000000000..c42752133e
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueEvents.h
@@ -0,0 +1,84 @@
+#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();
+ //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/cpp/src/qpid/broker/QueueListeners.cpp b/cpp/src/qpid/broker/QueueListeners.cpp
new file mode 100644
index 0000000000..951de2184a
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueListeners.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/QueueListeners.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace broker {
+
+void QueueListeners::addListener(Consumer::shared_ptr c)
+{
+ if (c->preAcquires()) {
+ add(consumers, c);
+ } else {
+ add(browsers, c);
+ }
+}
+
+void QueueListeners::removeListener(Consumer::shared_ptr c)
+{
+ if (c->preAcquires()) {
+ remove(consumers, c);
+ } else {
+ remove(browsers, c);
+ }
+}
+
+void QueueListeners::populate(NotificationSet& set)
+{
+ if (consumers.size()) {
+ set.consumer = consumers.front();
+ consumers.erase(consumers.begin());
+ } else {
+ // Don't swap the vectors, hang on to the memory allocated.
+ set.browsers = browsers;
+ browsers.clear();
+ }
+}
+
+void QueueListeners::add(Listeners& listeners, Consumer::shared_ptr c)
+{
+ Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c);
+ if (i == listeners.end()) 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
+ std::find(browsers.begin(), browsers.end(), c) != browsers.end() ||
+ std::find(consumers.begin(), consumers.end(), c) != consumers.end();
+}
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/QueueListeners.h b/cpp/src/qpid/broker/QueueListeners.h
new file mode 100644
index 0000000000..51ef58eb06
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueListeners.h
@@ -0,0 +1,75 @@
+#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 <vector>
+
+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::vector<Consumer::shared_ptr> Listeners;
+
+ class NotificationSet
+ {
+ public:
+ void notify();
+ private:
+ Listeners browsers;
+ Consumer::shared_ptr consumer;
+ friend class QueueListeners;
+ };
+
+ void addListener(Consumer::shared_ptr);
+ void removeListener(Consumer::shared_ptr);
+ void populate(NotificationSet&);
+ bool contains(Consumer::shared_ptr c) const;
+
+ 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/cpp/src/qpid/broker/QueuePolicy.cpp b/cpp/src/qpid/broker/QueuePolicy.cpp
index 08838aac79..a8aa674c53 100644
--- a/cpp/src/qpid/broker/QueuePolicy.cpp
+++ b/cpp/src/qpid/broker/QueuePolicy.cpp
@@ -18,40 +18,99 @@
* under the License.
*
*/
-#include "QueuePolicy.h"
+#include "qpid/broker/QueuePolicy.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"
using namespace qpid::broker;
using namespace qpid::framing;
-QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize) :
- maxCount(_maxCount), maxSize(_maxSize), count(0), size(0) {}
-
-QueuePolicy::QueuePolicy(const FieldTable& settings) :
- maxCount(getInt(settings, maxCountKey, 0)),
- maxSize(getInt(settings, maxSizeKey, defaultMaxSize)), count(0), size(0) {}
+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) {}
void QueuePolicy::enqueued(uint64_t _size)
{
- if (maxCount) count++;
+ if (maxCount) ++count;
if (maxSize) size += _size;
}
void QueuePolicy::dequeued(uint64_t _size)
{
- if (maxCount) count--;
- if (maxSize) size -= _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)
+{
+ enqueued(m->contentSize());
}
-bool QueuePolicy::limitExceeded()
+void QueuePolicy::enqueueAborted(boost::intrusive_ptr<Message> m)
{
- return (maxSize && size > maxSize) || (maxCount && count > maxCount);
+ 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);
+ if (maxSize) settings.setInt(maxSizeKey, maxSize);
+ settings.setString(typeKey, type);
}
@@ -62,27 +121,195 @@ int QueuePolicy::getInt(const FieldTable& settings, const std::string& key, int
else return defaultValue;
}
+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)
+{
+ return a.position < b.position;
+}
+
+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 (QueuePolicy::checkLimit(m)) return true;//if haven't hit limit, ok to accept
+
+ QueuedMessage oldest;
+ if (queue.empty()) {
+ QPID_LOG(debug, "Message too large for ring queue " << name
+ << " [" << *this << "] "
+ << ": message size = " << m->contentSize() << " bytes");
+ return false;
+ }
+ 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");
+ return true;
+ } else {
+ 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");
+ //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
+ return false;
+ }
+}
+
+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 = getInt(settings, maxCountKey, 0);
+ uint32_t maxSize = getInt(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, current=" << p.size;
+ else out << "size: unlimited";
out << "; ";
if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count;
- else out << "count unlimited, current=" << p.count;
+ else out << "count: unlimited";
+ out << "; type=" << p.type;
return out;
}
}
}
+
diff --git a/cpp/src/qpid/broker/QueuePolicy.h b/cpp/src/qpid/broker/QueuePolicy.h
index 4511a63b64..b2937e94c7 100644
--- a/cpp/src/qpid/broker/QueuePolicy.h
+++ b/cpp/src/qpid/broker/QueuePolicy.h
@@ -21,40 +21,100 @@
#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 const std::string maxCountKey;
- static const std::string maxSizeKey;
-
- static uint64_t defaultMaxSize;
+namespace broker {
+
+class QueuePolicy
+{
+ static uint64_t defaultMaxSize;
- const uint32_t maxCount;
- const uint64_t maxSize;
- uint32_t count;
- uint64_t size;
+ uint32_t maxCount;
+ uint64_t maxSize;
+ const std::string type;
+ uint32_t count;
+ uint64_t size;
+ bool policyExceeded;
- static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue);
-
- public:
- QueuePolicy(uint32_t maxCount, uint64_t maxSize);
- QueuePolicy(const qpid::framing::FieldTable& settings);
- void enqueued(uint64_t size);
- void dequeued(uint64_t size);
- void update(qpid::framing::FieldTable& settings);
- bool limitExceeded();
- uint32_t getMaxCount() const { return maxCount; }
- uint64_t getMaxSize() const { return maxSize; }
-
- static void setDefaultMaxSize(uint64_t);
- friend std::ostream& operator<<(std::ostream&, const QueuePolicy&);
- };
- }
-}
+ static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue);
+
+ 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/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp
index 61bdb0ffde..4b1fa62709 100644
--- a/cpp/src/qpid/broker/QueueRegistry.cpp
+++ b/cpp/src/qpid/broker/QueueRegistry.cpp
@@ -18,7 +18,8 @@
* under the License.
*
*/
-#include "QueueRegistry.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/QueueEvents.h"
#include "qpid/log/Statement.h"
#include <sstream>
#include <assert.h>
@@ -26,8 +27,8 @@
using namespace qpid::broker;
using namespace qpid::sys;
-QueueRegistry::QueueRegistry() :
- counter(1), store(0), parent(0) {}
+QueueRegistry::QueueRegistry(Broker* b) :
+ counter(1), store(0), events(0), parent(0), lastNode(false), broker(b) {}
QueueRegistry::~QueueRegistry(){}
@@ -41,8 +42,10 @@ QueueRegistry::declare(const string& declareName, bool durable,
QueueMap::iterator i = queues.find(name);
if (i == queues.end()) {
- Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent));
+ Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker));
queues[name] = queue;
+ if (lastNode) queue->setLastNodeFailure();
+ if (events) queue->setQueueEventManager(*events);
return std::pair<Queue::shared_ptr, bool>(queue, true);
} else {
@@ -84,10 +87,27 @@ string QueueRegistry::generateName(){
void QueueRegistry::setStore (MessageStore* _store)
{
- assert (store == 0 && _store != 0);
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;
+}
+
+void QueueRegistry::setQueueEvents(QueueEvents* e)
+{
+ events = e;
+}
diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h
index f7be1c551a..72a91dff24 100644
--- a/cpp/src/qpid/broker/QueueRegistry.h
+++ b/cpp/src/qpid/broker/QueueRegistry.h
@@ -21,14 +21,19 @@
#ifndef _QueueRegistry_
#define _QueueRegistry_
-#include <map>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Queue.h"
#include "qpid/sys/Mutex.h"
-#include "Queue.h"
#include "qpid/management/Manageable.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <map>
namespace qpid {
namespace broker {
+class QueueEvents;
+
/**
* A registry of queues indexed by queue name.
*
@@ -36,10 +41,10 @@ namespace broker {
* are deleted when and only when they are no longer in use.
*
*/
-class QueueRegistry{
+class QueueRegistry {
public:
- QueueRegistry();
- ~QueueRegistry();
+ QPID_BROKER_EXTERN QueueRegistry(Broker* b = 0);
+ QPID_BROKER_EXTERN ~QueueRegistry();
/**
* Declare a queue.
@@ -47,8 +52,11 @@ class QueueRegistry{
* @return The queue and a boolean flag which is true if the queue
* was created by this declare call false if it already existed.
*/
- std::pair<Queue::shared_ptr, bool> declare(const string& name, bool durable = false, bool autodelete = false,
- const OwnershipToken* owner = 0);
+ QPID_BROKER_EXTERN std::pair<Queue::shared_ptr, bool> declare
+ (const string& name,
+ bool durable = false,
+ bool autodelete = false,
+ const OwnershipToken* owner = 0);
/**
* Destroy the named queue.
@@ -62,7 +70,7 @@ class QueueRegistry{
* subsequent calls to find or declare with the same name.
*
*/
- void destroy (const string& name);
+ QPID_BROKER_EXTERN void destroy(const string& name);
template <class Test> bool destroyIf(const string& name, Test test)
{
qpid::sys::RWlock::ScopedWlock locker(lock);
@@ -77,13 +85,15 @@ class QueueRegistry{
/**
* Find the named queue. Return 0 if not found.
*/
- Queue::shared_ptr find(const string& name);
+ QPID_BROKER_EXTERN Queue::shared_ptr find(const string& name);
/**
* Generate unique queue name.
*/
string generateName();
+ void setQueueEvents(QueueEvents*);
+
/**
* Set the store to use. May only be called once.
*/
@@ -98,22 +108,37 @@ class QueueRegistry{
* 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<string, Queue::shared_ptr> QueueMap;
QueueMap queues;
- qpid::sys::RWlock lock;
+ 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 string& name);
};
-}
-}
+}} // namespace qpid::broker
#endif
diff --git a/cpp/src/qpid/broker/QueuedMessage.h b/cpp/src/qpid/broker/QueuedMessage.h
new file mode 100644
index 0000000000..35e48b11f3
--- /dev/null
+++ b/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/cpp/src/qpid/broker/RateFlowcontrol.h b/cpp/src/qpid/broker/RateFlowcontrol.h
new file mode 100644
index 0000000000..99f9d2c0c4
--- /dev/null
+++ b/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/cpp/src/qpid/broker/RateTracker.cpp b/cpp/src/qpid/broker/RateTracker.cpp
new file mode 100644
index 0000000000..048349b658
--- /dev/null
+++ b/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/cpp/src/qpid/broker/RateTracker.h b/cpp/src/qpid/broker/RateTracker.h
new file mode 100644
index 0000000000..0c20b37312
--- /dev/null
+++ b/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/cpp/src/qpid/broker/RecoverableExchange.h b/cpp/src/qpid/broker/RecoverableExchange.h
index 76d0d2ecdf..ca6cc1541e 100644
--- a/cpp/src/qpid/broker/RecoverableExchange.h
+++ b/cpp/src/qpid/broker/RecoverableExchange.h
@@ -40,7 +40,9 @@ public:
/**
* Recover binding. Nb: queue must have been recovered earlier.
*/
- virtual void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args) = 0;
+ virtual void bind(const std::string& queue,
+ const std::string& routingKey,
+ qpid::framing::FieldTable& args) = 0;
virtual ~RecoverableExchange() {};
};
diff --git a/cpp/src/qpid/broker/RecoverableMessage.h b/cpp/src/qpid/broker/RecoverableMessage.h
index f755fdf727..c98857ceb0 100644
--- a/cpp/src/qpid/broker/RecoverableMessage.h
+++ b/cpp/src/qpid/broker/RecoverableMessage.h
@@ -37,6 +37,7 @@ 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.
diff --git a/cpp/src/qpid/broker/RecoverableQueue.h b/cpp/src/qpid/broker/RecoverableQueue.h
index b32bae7f07..49f05f97a1 100644
--- a/cpp/src/qpid/broker/RecoverableQueue.h
+++ b/cpp/src/qpid/broker/RecoverableQueue.h
@@ -22,7 +22,7 @@
*
*/
-#include "RecoverableMessage.h"
+#include "qpid/broker/RecoverableMessage.h"
#include <boost/shared_ptr.hpp>
namespace qpid {
diff --git a/cpp/src/qpid/broker/RecoverableTransaction.h b/cpp/src/qpid/broker/RecoverableTransaction.h
index 7fe34b6756..1b7d94bd1a 100644
--- a/cpp/src/qpid/broker/RecoverableTransaction.h
+++ b/cpp/src/qpid/broker/RecoverableTransaction.h
@@ -24,8 +24,8 @@
#include <boost/shared_ptr.hpp>
-#include "RecoverableMessage.h"
-#include "RecoverableQueue.h"
+#include "qpid/broker/RecoverableMessage.h"
+#include "qpid/broker/RecoverableQueue.h"
namespace qpid {
namespace broker {
diff --git a/cpp/src/qpid/broker/RecoveredDequeue.cpp b/cpp/src/qpid/broker/RecoveredDequeue.cpp
index e2d70964fb..658fd5a89e 100644
--- a/cpp/src/qpid/broker/RecoveredDequeue.cpp
+++ b/cpp/src/qpid/broker/RecoveredDequeue.cpp
@@ -18,22 +18,29 @@
* under the License.
*
*/
-#include "RecoveredDequeue.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) {}
+RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg)
+{
+ queue->recoverPrepared(msg);
+}
-bool RecoveredDequeue::prepare(TransactionContext*) throw(){
+bool RecoveredDequeue::prepare(TransactionContext*) throw()
+{
//should never be called; transaction has already prepared if an enqueue is recovered
return false;
}
-void RecoveredDequeue::commit() throw(){
+void RecoveredDequeue::commit() throw()
+{
+ queue->enqueueAborted(msg);
}
-void RecoveredDequeue::rollback() throw(){
+void RecoveredDequeue::rollback() throw()
+{
msg->enqueueComplete();
queue->process(msg);
}
diff --git a/cpp/src/qpid/broker/RecoveredDequeue.h b/cpp/src/qpid/broker/RecoveredDequeue.h
index 276e1f4c5c..67b37db5f9 100644
--- a/cpp/src/qpid/broker/RecoveredDequeue.h
+++ b/cpp/src/qpid/broker/RecoveredDequeue.h
@@ -21,11 +21,11 @@
#ifndef _RecoveredDequeue_
#define _RecoveredDequeue_
-#include "Deliverable.h"
-#include "Message.h"
-#include "MessageStore.h"
-#include "Queue.h"
-#include "TxOp.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/TxOp.h"
#include <boost/intrusive_ptr.hpp>
@@ -45,6 +45,10 @@ namespace qpid {
virtual void commit() throw();
virtual void rollback() throw();
virtual ~RecoveredDequeue(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ Queue::shared_ptr getQueue() const { return queue; }
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
};
}
}
diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/cpp/src/qpid/broker/RecoveredEnqueue.cpp
index 1984a5d4a8..48faa0942c 100644
--- a/cpp/src/qpid/broker/RecoveredEnqueue.cpp
+++ b/cpp/src/qpid/broker/RecoveredEnqueue.cpp
@@ -18,12 +18,15 @@
* under the License.
*
*/
-#include "RecoveredEnqueue.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) {}
+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
@@ -36,5 +39,6 @@ void RecoveredEnqueue::commit() throw(){
}
void RecoveredEnqueue::rollback() throw(){
+ queue->enqueueAborted(msg);
}
diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.h b/cpp/src/qpid/broker/RecoveredEnqueue.h
index 6525179769..09f928f098 100644
--- a/cpp/src/qpid/broker/RecoveredEnqueue.h
+++ b/cpp/src/qpid/broker/RecoveredEnqueue.h
@@ -21,11 +21,11 @@
#ifndef _RecoveredEnqueue_
#define _RecoveredEnqueue_
-#include "Deliverable.h"
-#include "Message.h"
-#include "MessageStore.h"
-#include "Queue.h"
-#include "TxOp.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/MessageStore.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/TxOp.h"
#include <boost/intrusive_ptr.hpp>
@@ -45,6 +45,11 @@ namespace qpid {
virtual void commit() throw();
virtual void rollback() throw();
virtual ~RecoveredEnqueue(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+
+ Queue::shared_ptr getQueue() const { return queue; }
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+
};
}
}
diff --git a/cpp/src/qpid/broker/RecoveryManager.h b/cpp/src/qpid/broker/RecoveryManager.h
index 7dcbe3a2b0..2929e92250 100644
--- a/cpp/src/qpid/broker/RecoveryManager.h
+++ b/cpp/src/qpid/broker/RecoveryManager.h
@@ -21,12 +21,12 @@
#ifndef _RecoveryManager_
#define _RecoveryManager_
-#include "RecoverableExchange.h"
-#include "RecoverableQueue.h"
-#include "RecoverableMessage.h"
-#include "RecoverableTransaction.h"
-#include "RecoverableConfig.h"
-#include "TransactionalStore.h"
+#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 {
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
index b058978ccf..12ac2d2bfd 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
+++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
@@ -18,21 +18,22 @@
* under the License.
*
*/
-#include "RecoveryManagerImpl.h"
-
-#include "Message.h"
-#include "Queue.h"
-#include "Link.h"
-#include "Bridge.h"
-#include "RecoveredEnqueue.h"
-#include "RecoveredDequeue.h"
+#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 namespace qpid;
-using namespace qpid::broker;
using boost::dynamic_pointer_cast;
using boost::intrusive_ptr;
+namespace qpid {
+namespace broker {
+
RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links,
DtxManager& _dtxMgr, uint64_t _stagingThreshold)
: queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr), stagingThreshold(_stagingThreshold) {}
@@ -44,10 +45,10 @@ class RecoverableMessageImpl : public RecoverableMessage
intrusive_ptr<Message> msg;
const uint64_t stagingThreshold;
public:
- RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold)
- : msg(_msg), stagingThreshold(_stagingThreshold) {}
+ RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold);
~RecoverableMessageImpl() {};
void setPersistenceId(uint64_t id);
+ void setRedelivered();
bool loadContent(uint64_t available);
void decodeContent(framing::Buffer& buffer);
void recover(Queue::shared_ptr queue);
@@ -59,7 +60,7 @@ class RecoverableQueueImpl : public RecoverableQueue
{
Queue::shared_ptr queue;
public:
- RecoverableQueueImpl(Queue::shared_ptr& _queue) : queue(_queue) {}
+ RecoverableQueueImpl(const boost::shared_ptr<Queue>& _queue) : queue(_queue) {}
~RecoverableQueueImpl() {};
void setPersistenceId(uint64_t id);
uint64_t getPersistenceId() const;
@@ -78,7 +79,7 @@ class RecoverableExchangeImpl : public RecoverableExchange
public:
RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {}
void setPersistenceId(uint64_t id);
- void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args);
+ void bind(const std::string& queue, const std::string& routingKey, qpid::framing::FieldTable& args);
};
class RecoverableConfigImpl : public RecoverableConfig
@@ -102,18 +103,24 @@ public:
RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Buffer& buffer)
{
- return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(Exchange::decode(exchanges, buffer), queues));
+ 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::decode(queues, buffer);
+ Queue::shared_ptr queue = Queue::decode(queues, buffer, true);
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) {
+ } catch (const framing::NotFoundException& /*e*/) {
//assume no default exchange has been declared
}
return RecoverableQueue::shared_ptr(new RecoverableQueueImpl(queue));
@@ -149,7 +156,16 @@ RecoverableConfig::shared_ptr RecoveryManagerImpl::recoverConfig(framing::Buffer
void RecoveryManagerImpl::recoveryComplete()
{
- //TODO (finalise binding setup etc)
+ //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, uint64_t _stagingThreshold) : msg(_msg), stagingThreshold(_stagingThreshold)
+{
+ if (!msg->isPersistent()) {
+ msg->forcePersistent(); // set so that message will get dequeued from store.
+ }
}
bool RecoverableMessageImpl::loadContent(uint64_t available)
@@ -172,6 +188,11 @@ 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);
@@ -181,7 +202,7 @@ void RecoverableQueueImpl::setPersistenceId(uint64_t id)
{
queue->setPersistenceId(id);
}
-
+
uint64_t RecoverableQueueImpl::getPersistenceId() const
{
return queue->getPersistenceId();
@@ -215,10 +236,13 @@ void RecoverableConfigImpl::setPersistenceId(uint64_t id)
bridge->setPersistenceId(id);
}
-void RecoverableExchangeImpl::bind(string& queueName, string& key, framing::FieldTable& args)
+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)
@@ -251,3 +275,5 @@ void RecoverableTransactionImpl::enqueue(RecoverableQueue::shared_ptr queue, Rec
{
dynamic_pointer_cast<RecoverableQueueImpl>(queue)->enqueue(buffer, message);
}
+
+}}
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.h b/cpp/src/qpid/broker/RecoveryManagerImpl.h
index cd34d464f5..6fbbfc4a6c 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.h
+++ b/cpp/src/qpid/broker/RecoveryManagerImpl.h
@@ -22,11 +22,11 @@
#define _RecoveryManagerImpl_
#include <list>
-#include "DtxManager.h"
-#include "ExchangeRegistry.h"
-#include "QueueRegistry.h"
-#include "LinkRegistry.h"
-#include "RecoveryManager.h"
+#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 {
diff --git a/cpp/src/qpid/broker/RetryList.cpp b/cpp/src/qpid/broker/RetryList.cpp
new file mode 100644
index 0000000000..8f600c086d
--- /dev/null
+++ b/cpp/src/qpid/broker/RetryList.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/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(TcpAddress& address)
+{
+ while (urlIndex < urls.size()) {
+ while (addressIndex < urls[urlIndex].size()) {
+ const TcpAddress* tcp = urls[urlIndex][addressIndex++].get<TcpAddress>();
+ if (tcp) {
+ address = *tcp;
+ 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/cpp/src/qpid/broker/RetryList.h b/cpp/src/qpid/broker/RetryList.h
new file mode 100644
index 0000000000..f87adf2c8d
--- /dev/null
+++ b/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(TcpAddress& 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/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp
index 136cf6f785..0e509c8d93 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -19,17 +19,25 @@
*
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
-#include "Connection.h"
+#include "qpid/broker/Connection.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/reply_exceptions.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 boost::format;
+using boost::str;
namespace qpid {
namespace broker {
@@ -39,12 +47,15 @@ class NullAuthenticator : public SaslAuthenticator
{
Connection& connection;
framing::AMQP_ClientProxy::Connection client;
+ std::string realm;
+ const bool encrypt;
public:
- NullAuthenticator(Connection& connection);
+ 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
@@ -54,62 +65,132 @@ 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);
public:
- CyrusAuthenticator(Connection& connection);
+ 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 getUid(std::string& uid);
+ void getError(std::string& error);
+ 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)
+{
+ 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*/)
+{
+ throw Exception("Requested authentication but SASL unavailable");
+}
+
+void SaslAuthenticator::fini(void)
+{
+ return;
+}
+
#endif
std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c)
{
+ static bool needWarning = true;
if (c.getBroker().getOptions().auth) {
- return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c));
+ return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
} else {
- return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c));
+ QPID_LOG(debug, "SASL: No Authentication Performed");
+ needWarning = false;
+ return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
}
}
-NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {}
+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)
{
- QPID_LOG(warning, "SASL: No Authentication Performed");
+ if (encrypt) {
+ QPID_LOG(error, "Rejected un-encrypted connection.");
+ throw ConnectionForcedException("Connection must be encrypted.");
+ }
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);
+ 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, 0);
+ 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) : sasl_conn(0), connection(c), client(c.getOutput())
+
+CyrusAuthenticator::CyrusAuthenticator(Connection& c, bool _encrypt) :
+ sasl_conn(0), connection(c), client(c.getOutput()), encrypt(_encrypt)
{
init();
}
@@ -145,6 +226,39 @@ void CyrusAuthenticator::init()
// 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.
+ sasl_ssf_t external_ssf = (sasl_ssf_t) connection.getSSF();
+ 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 );
+
+ secprops.maxbufsize = 65535;
+ secprops.property_names = 0;
+ secprops.property_values = 0;
+ secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */
+
+ int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
+ if (result != SASL_OK) {
+ throw framing::InternalErrorException(QPID_MSG("SASL error: " << result));
+ }
+
}
CyrusAuthenticator::~CyrusAuthenticator()
@@ -155,6 +269,23 @@ CyrusAuthenticator::~CyrusAuthenticator()
}
}
+void CyrusAuthenticator::getError(string& error)
+{
+ error = string(sasl_errdetail(sasl_conn));
+}
+
+void CyrusAuthenticator::getUid(string& uid)
+{
+ int code;
+ const void* ptr;
+
+ code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr);
+ if (SASL_OK != code)
+ return;
+
+ uid = string(const_cast<char*>(static_cast<const char*>(ptr)));
+}
+
void CyrusAuthenticator::getMechanisms(Array& mechanisms)
{
const char *separator = " ";
@@ -239,7 +370,7 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen
connection.setUserId(const_cast<char*>(static_cast<const char*>(uid)));
- client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax());
} else if (SASL_CONTINUE == code) {
string challenge_str(challenge, challenge_len);
@@ -264,6 +395,24 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen
}
}
}
+
+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) {
+ QPID_LOG(info, "Installing security layer, SSF: "<< ssf);
+ securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize));
+ }
+ return securityLayer;
+}
+
#endif
}}
diff --git a/cpp/src/qpid/broker/SaslAuthenticator.h b/cpp/src/qpid/broker/SaslAuthenticator.h
index c2d4ecf7c0..8ddaeb19a4 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.h
+++ b/cpp/src/qpid/broker/SaslAuthenticator.h
@@ -24,6 +24,7 @@
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/AMQP_ClientProxy.h"
#include "qpid/Exception.h"
+#include "qpid/sys/SecurityLayer.h"
#include <memory>
namespace qpid {
@@ -38,6 +39,15 @@ public:
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 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);
+ static void fini(void);
static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection);
};
diff --git a/cpp/src/qpid/broker/SecureConnection.cpp b/cpp/src/qpid/broker/SecureConnection.cpp
new file mode 100644
index 0000000000..74aec239ca
--- /dev/null
+++ b/cpp/src/qpid/broker/SecureConnection.cpp
@@ -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.
+ *
+ */
+#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)
+{
+ securityLayer = sl;
+ securityLayer->init(codec.get());
+}
+
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SecureConnection.h b/cpp/src/qpid/broker/SecureConnection.h
new file mode 100644
index 0000000000..4a0cc50e34
--- /dev/null
+++ b/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>);
+ 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/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/cpp/src/qpid/broker/SecureConnectionFactory.cpp
new file mode 100644
index 0000000000..5a31dbceeb
--- /dev/null
+++ b/cpp/src/qpid/broker/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/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/log/Statement.h"
+
+namespace qpid {
+namespace broker {
+
+using framing::ProtocolVersion;
+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,
+ unsigned int conn_ssf ) {
+ 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, conn_ssf, 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,
+ unsigned int conn_ssf) {
+ // 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, conn_ssf, 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/cpp/src/qpid/broker/SecureConnectionFactory.h b/cpp/src/qpid/broker/SecureConnectionFactory.h
new file mode 100644
index 0000000000..b1af6d4a0f
--- /dev/null
+++ b/cpp/src/qpid/broker/SecureConnectionFactory.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 _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,
+ unsigned int conn_ssf);
+
+ sys::ConnectionCodec*
+ create(sys::OutputControl&, const std::string& id,
+ unsigned int conn_ssf);
+
+ private:
+ Broker& broker;
+};
+
+}}
+
+
+#endif
diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp
index 4d5c4e7537..e9b6aad967 100644
--- a/cpp/src/qpid/broker/SemanticState.cpp
+++ b/cpp/src/qpid/broker/SemanticState.cpp
@@ -19,21 +19,23 @@
*
*/
-#include "SessionState.h"
-#include "Connection.h"
-#include "DeliverableMessage.h"
-#include "DtxAck.h"
-#include "DtxTimeout.h"
-#include "Message.h"
-#include "Queue.h"
-#include "SessionContext.h"
-#include "TxAccept.h"
-#include "TxPublish.h"
+#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/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/ptr_map.h"
-#include "AclModule.h"
+#include "qpid/broker/AclModule.h"
#include <boost/bind.hpp>
#include <boost/format.hpp>
@@ -49,30 +51,35 @@
namespace qpid {
namespace broker {
-using std::mem_fun_ref;
+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),
- prefetchSize(0),
- prefetchCount(0),
tagGenerator("sgen"),
dtxSelected(false),
- outputTasks(ss)
+ authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()),
+ userID(getSession().getConnection().getUserId()),
+ defaultRealm(getSession().getBroker().getOptions().realm)
{
- outstanding.reset();
acl = getSession().getBroker().getAcl();
}
SemanticState::~SemanticState() {
//cancel all consumers
for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
- cancel(*ptr_map_ptr(i));
+ cancel(i->second);
}
if (dtxBuffer.get()) {
@@ -85,23 +92,20 @@ bool SemanticState::exists(const string& consumerTag){
return consumers.find(consumerTag) != consumers.end();
}
-void SemanticState::consume(DeliveryToken::shared_ptr token, string& tagInOut,
- Queue::shared_ptr queue, bool nolocal, bool ackRequired, bool acquire,
- bool exclusive, const FieldTable*)
+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)
{
- if(tagInOut.empty())
- tagInOut = tagGenerator.generate();
- std::auto_ptr<ConsumerImpl> c(new ConsumerImpl(this, token, tagInOut, queue, ackRequired, nolocal, acquire));
- queue->consume(*c, exclusive);//may throw exception
- outputTasks.addOutputTask(c.get());
- consumers.insert(tagInOut, c.release());
+ 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;
}
void SemanticState::cancel(const string& tag){
ConsumerImplMap::iterator i = consumers.find(tag);
if (i != consumers.end()) {
- cancel(*ptr_map_ptr(i));
- consumers.erase(i);
+ 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));
@@ -149,7 +153,7 @@ void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join)
throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx"));
}
dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid));
- txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer);
+ txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
if (join) {
mgr.join(xid, dtxBuffer);
} else {
@@ -217,7 +221,7 @@ void SemanticState::resumeDtx(const std::string& xid)
checkDtxTimeout();
dtxBuffer->setSuspended(false);
- txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer);
+ txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
}
void SemanticState::checkDtxTimeout()
@@ -231,36 +235,70 @@ void SemanticState::checkDtxTimeout()
void SemanticState::record(const DeliveryRecord& delivery)
{
unacked.push_back(delivery);
- delivery.addTo(outstanding);
}
-bool SemanticState::checkPrefetch(intrusive_ptr<Message>& msg)
-{
- bool countOk = !prefetchCount || prefetchCount > unacked.size();
- bool sizeOk = !prefetchSize || prefetchSize > msg->contentSize() + outstanding.size || unacked.empty();
- return countOk && sizeOk;
-}
+const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
- DeliveryToken::shared_ptr _token,
- const string& _name,
- Queue::shared_ptr _queue,
- bool ack,
- bool _nolocal,
- bool _acquire
- ) :
+ 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),
- token(_token),
name(_name),
queue(_queue),
ackExpected(ack),
- nolocal(_nolocal),
acquire(_acquire),
blocked(true),
- windowing(true),
+ windowing(true),
+ exclusive(_exclusive),
+ resumeId(_resumeId),
+ resumeTtl(_resumeTtl),
+ arguments(_arguments),
msgCredit(0),
- byteCredit(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 ,arguments);
+ agent->addObject (mgmtObject, agent->allocateId(this));
+ 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()
{
@@ -270,29 +308,49 @@ OwnershipToken* SemanticState::ConsumerImpl::getSession()
bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg)
{
allocateCredit(msg.payload);
- DeliveryId deliveryTag =
- parent->deliveryAdapter.deliver(msg, token);
+ 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(DeliveryRecord(msg, queue, name, token, deliveryTag, acquire, !ackExpected));
+ parent->record(record);
}
if (acquire && !ackExpected) {
- queue->dequeue(0, msg.payload);
+ queue->dequeue(0, msg);
}
+ if (mgmtObject) { mgmtObject->inc_delivered(); }
return true;
}
-bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message> msg)
+bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message>)
{
- return !(nolocal &&
- &parent->getSession().getConnection() == msg->getPublisher());
+ return true;
}
bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg)
{
- blocked = !(filter(msg) && checkCredit(msg) && (!ackExpected || parent->checkPrefetch(msg)));
+ // 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)
{
uint32_t originalMsgCredit = msgCredit;
@@ -303,7 +361,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg)
if (byteCredit != 0xFFFFFFFF) {
byteCredit -= msg->getRequiredCredit();
}
- QPID_LOG(debug, "Credit allocated for '" << name << "' on " << parent
+ QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this)
<< ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit
<< " now bytes: " << byteCredit << " msgs: " << msgCredit);
@@ -311,23 +369,27 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg)
bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg)
{
- if (msgCredit == 0 || (byteCredit != 0xFFFFFFFF && byteCredit < msg->getRequiredCredit())) {
- QPID_LOG(debug, "Not enough credit for '" << name << "' on " << parent
- << ", bytes: " << byteCredit << " msgs: " << msgCredit);
- return false;
- } else {
- QPID_LOG(debug, "Credit available for '" << name << "' on " << parent
- << " bytes: " << byteCredit << " msgs: " << msgCredit);
- return true;
- }
+ 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() {}
+SemanticState::ConsumerImpl::~ConsumerImpl()
+{
+ if (mgmtObject != 0)
+ mgmtObject->resourceDestroy ();
+}
-void SemanticState::cancel(ConsumerImpl& c)
+void SemanticState::cancel(ConsumerImpl::shared_ptr c)
{
- outputTasks.removeOutputTask(&c);
- Queue::shared_ptr queue = c.getQueue();
+ c->disableNotify();
+ if (session.isAttached())
+ session.getConnection().outputTasks.removeOutputTask(c.get());
+ Queue::shared_ptr queue = c->getQueue();
if(queue) {
queue->cancel(c);
if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
@@ -345,30 +407,48 @@ void SemanticState::handle(intrusive_ptr<Message> msg) {
} else {
DeliverableMessage deliverable(msg);
route(msg, deliverable);
+ if (msg->checkContentReleasable()) {
+ msg->releaseContent();
+ }
}
}
+namespace
+{
+const std::string nullstring;
+}
+
void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) {
+ msg->setTimestamp(getSession().getBroker().getExpiryPolicy());
+
std::string exchangeName = msg->getExchangeName();
- //TODO: the following should be hidden behind message (using MessageAdapter or similar)
- if (msg->isA<MessageTransferBody>()) {
- msg->getProperties<DeliveryProperties>()->setExchange(exchangeName);
- }
- if (!cacheExchange || cacheExchange->getName() != exchangeName){
+ if (!cacheExchange || cacheExchange->getName() != exchangeName)
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 && id.append("@").append(defaultRealm) != userID)
+ {
+ 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::PUBLISH,acl::EXCHANGE,exchangeName, msg->getRoutingKey() ))
- throw NotAllowedException("ACL denied exhange publish request");
+ if (acl && acl->doTransferAcl())
+ {
+ if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() ))
+ throw NotAllowedException(QPID_MSG(userID << " cannot publish to " <<
+ exchangeName << " with routing-key " << msg->getRoutingKey()));
}
cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders());
if (!strategy.delivered) {
- //TODO:if reject-unroutable, then reject
- //else route to alternate exchange
+ //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());
}
@@ -380,30 +460,27 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) {
}
void SemanticState::requestDispatch()
-{
- for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
- requestDispatch(*ptr_map_ptr(i));
- }
+{
+ for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++)
+ i->second->requestDispatch();
}
-void SemanticState::requestDispatch(ConsumerImpl& c)
-{
- if(c.isBlocked())
- outputTasks.activateOutput();
- // TODO aconway 2008-07-16: we could directly call
- // c.doOutput();
- // since we are in the connections thread but for consistency
- // activateOutput() will set it up to be called in the next write idle.
- // Current cluster code depends on this, review cluster code to change.
+void SemanticState::ConsumerImpl::requestDispatch()
+{
+ if (blocked) {
+ parent->session.getConnection().outputTasks.addOutputTask(this);
+ parent->session.getConnection().outputTasks.activateOutput();
+ blocked = false;
+ }
}
-void SemanticState::complete(DeliveryRecord& delivery)
+bool SemanticState::complete(DeliveryRecord& delivery)
{
- delivery.subtractFrom(outstanding);
ConsumerImplMap::iterator i = consumers.find(delivery.getTag());
if (i != consumers.end()) {
- ptr_map_ptr(i)->complete(delivery);
+ i->second->complete(delivery);
}
+ return delivery.isRedundant();
}
void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery)
@@ -420,10 +497,9 @@ void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery)
void SemanticState::recover(bool requeue)
{
if(requeue){
- outstanding.reset();
//take copy and clear unacked as requeue may result in redelivery to this session
//which will in turn result in additions to unacked
- std::list<DeliveryRecord> copy = unacked;
+ DeliveryRecords copy = unacked;
unacked.clear();
for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue));
}else{
@@ -431,27 +507,13 @@ void SemanticState::recover(bool requeue)
//unconfirmed messages re redelivered and therefore have their
//id adjusted, confirmed messages are not and so the ordering
//w.r.t id is lost
- unacked.sort();
- }
-}
-
-bool SemanticState::get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected)
-{
- QueuedMessage msg = queue->get();
- if(msg.payload){
- DeliveryId myDeliveryTag = deliveryAdapter.deliver(msg, token);
- if(ackExpected){
- unacked.push_back(DeliveryRecord(msg, queue, myDeliveryTag));
- }
- return true;
- }else{
- return false;
+ sort(unacked.begin(), unacked.end());
}
}
-DeliveryId SemanticState::redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token)
+void SemanticState::deliver(DeliveryRecord& msg, bool sync)
{
- return deliveryAdapter.deliver(msg, token);
+ return deliveryAdapter.deliver(msg, sync);
}
SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination)
@@ -460,7 +522,7 @@ SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination)
if (i == consumers.end()) {
throw NotFoundException(QPID_MSG("Unknown destination " << destination));
} else {
- return *ptr_map_ptr(i);
+ return *(i->second);
}
}
@@ -478,7 +540,7 @@ void SemanticState::addByteCredit(const std::string& destination, uint32_t value
{
ConsumerImpl& c = find(destination);
c.addByteCredit(value);
- requestDispatch(c);
+ c.requestDispatch();
}
@@ -486,7 +548,7 @@ void SemanticState::addMessageCredit(const std::string& destination, uint32_t va
{
ConsumerImpl& c = find(destination);
c.addMessageCredit(value);
- requestDispatch(c);
+ c.requestDispatch();
}
void SemanticState::flush(const std::string& destination)
@@ -503,30 +565,48 @@ void SemanticState::stop(const std::string& destination)
void SemanticState::ConsumerImpl::setWindowMode()
{
windowing = true;
+ if (mgmtObject){
+ mgmtObject->set_creditMode("WINDOW");
+ }
}
void SemanticState::ConsumerImpl::setCreditMode()
{
windowing = false;
+ if (mgmtObject){
+ mgmtObject->set_creditMode("CREDIT");
+ }
}
void SemanticState::ConsumerImpl::addByteCredit(uint32_t value)
{
if (byteCredit != 0xFFFFFFFF) {
- byteCredit += value;
+ if (value == 0xFFFFFFFF) byteCredit = value;
+ else byteCredit += value;
}
}
void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value)
{
if (msgCredit != 0xFFFFFFFF) {
- msgCredit += value;
+ 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(queue->dispatch(*this))
+ while(haveCredit() && queue->dispatch(shared_from_this()))
;
stop();
}
@@ -550,20 +630,8 @@ Queue::shared_ptr SemanticState::getQueue(const string& name) const {
}
AckRange SemanticState::findRange(DeliveryId first, DeliveryId last)
-{
- ack_iterator start = find_if(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::matchOrAfter, _1, first));
- ack_iterator end = start;
-
- if (start != unacked.end()) {
- if (first == last) {
- //just acked single element (move end past it)
- ++end;
- } else {
- //need to find end (position it just after the last record in range)
- end = find_if(start, unacked.end(), boost::bind(&DeliveryRecord::after, _1, last));
- }
- }
- return AckRange(start, end);
+{
+ return DeliveryRecord::findRange(unacked, first, last);
}
void SemanticState::acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired)
@@ -591,29 +659,62 @@ void SemanticState::reject(DeliveryId first, DeliveryId last)
}
bool SemanticState::ConsumerImpl::hasOutput() {
- return queue->checkForMessages(*this);
+ return queue->checkForMessages(shared_from_this());
}
bool SemanticState::ConsumerImpl::doOutput()
{
- //TODO: think through properly
- return queue->dispatch(*this);
+ return haveCredit() && queue->dispatch(shared_from_this());
}
-void SemanticState::ConsumerImpl::notify()
+void SemanticState::ConsumerImpl::enableNotify()
{
- //TODO: think through properly
- parent->outputTasks.activateOutput();
+ Mutex::ScopedLock l(lock);
+ notifyEnabled = true;
}
+void SemanticState::ConsumerImpl::disableNotify()
+{
+ Mutex::ScopedLock l(lock);
+ notifyEnabled = false;
+}
-void SemanticState::accepted(DeliveryId first, DeliveryId last)
+bool SemanticState::ConsumerImpl::isNotifyEnabled() const {
+ Mutex::ScopedLock l(lock);
+ return notifyEnabled;
+}
+
+void SemanticState::ConsumerImpl::notify()
{
- AckRange range = findRange(first, last);
+ Mutex::ScopedLock l(lock);
+ 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) {
if (txBuffer.get()) {
//in transactional mode, don't dequeue or remove, just
//maintain set of acknowledged messages:
- accumulatedAck.add(first, last);
+ accumulatedAck.add(commands);
if (dtxBuffer.get()) {
//if enlisted in a dtx, copy the relevant slice from
@@ -623,25 +724,48 @@ void SemanticState::accepted(DeliveryId first, DeliveryId last)
dtxBuffer->enlist(txAck);
//mark the relevant messages as 'ended' in unacked
- for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::setEnded));
-
//if the messages are already completed, they can be
//removed from the record
- unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant));
-
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&DeliveryRecord::setEnded, _1)));
+ unacked.erase(removed, unacked.end());
}
} else {
- for_each(range.start, range.end, boost::bind(&DeliveryRecord::accept, _1, (TransactionContext*) 0));
- unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant));
+ 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(DeliveryId first, DeliveryId last)
-{
- AckRange range = findRange(first, last);
- for_each(range.start, range.end, boost::bind(&SemanticState::complete, this, _1));
- unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant));
+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/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h
index e03d5ec89b..e5e3f909f1 100644
--- a/cpp/src/qpid/broker/SemanticState.h
+++ b/cpp/src/qpid/broker/SemanticState.h
@@ -22,29 +22,30 @@
*
*/
-#include "Consumer.h"
-#include "Deliverable.h"
-#include "DeliveryAdapter.h"
-#include "DeliveryRecord.h"
-#include "DeliveryToken.h"
-#include "DtxBuffer.h"
-#include "DtxManager.h"
-#include "NameGenerator.h"
-#include "Prefetch.h"
-#include "TxBuffer.h"
+#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/shared_ptr.h"
-#include "AclModule.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/intrusive_ptr.hpp>
+#include <boost/cast.hpp>
namespace qpid {
namespace broker {
@@ -55,36 +56,54 @@ class SessionContext;
* SemanticState holds the L3 and L4 state of an open session, whether
* attached to a channel or suspended.
*/
-class SemanticState : public sys::OutputTask,
- private boost::noncopyable
-{
- class ConsumerImpl : public Consumer, public sys::OutputTask
+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 DeliveryToken::shared_ptr token;
const string name;
const Queue::shared_ptr queue;
const bool ackExpected;
- const bool nolocal;
const bool acquire;
bool blocked;
bool windowing;
+ bool exclusive;
+ 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:
- ConsumerImpl(SemanticState* parent, DeliveryToken::shared_ptr token,
+ typedef boost::shared_ptr<ConsumerImpl> shared_ptr;
+
+ ConsumerImpl(SemanticState* parent,
const string& name, Queue::shared_ptr queue,
- bool ack, bool nolocal, bool acquire);
+ 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();
@@ -93,50 +112,68 @@ class SemanticState : public sys::OutputTask,
void flush();
void stop();
void complete(DeliveryRecord&);
- Queue::shared_ptr getQueue() { return queue; }
- bool isBlocked() const { return blocked; }
+ Queue::shared_ptr getQueue() const { return queue; }
+ bool isBlocked() const { return blocked; }
+ bool setBlocked(bool set) { std::swap(set, blocked); return set; }
bool hasOutput();
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);
};
- typedef boost::ptr_map<std::string,ConsumerImpl> ConsumerImplMap;
+ private:
+ typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap;
typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap;
SessionContext& session;
DeliveryAdapter& deliveryAdapter;
- Queue::shared_ptr defaultQueue;
ConsumerImplMap consumers;
- uint32_t prefetchSize;
- uint16_t prefetchCount;
- Prefetch outstanding;
NameGenerator tagGenerator;
- std::list<DeliveryRecord> unacked;
+ DeliveryRecords unacked;
TxBuffer::shared_ptr txBuffer;
DtxBuffer::shared_ptr dtxBuffer;
bool dtxSelected;
DtxBufferMap suspendedXids;
framing::SequenceSet accumulatedAck;
boost::shared_ptr<Exchange> cacheExchange;
- sys::AggregateOutput outputTasks;
AclModule* acl;
-
+ const bool authMsg;
+ const string userID;
+ const string defaultRealm;
+
void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy);
- void record(const DeliveryRecord& delivery);
- bool checkPrefetch(boost::intrusive_ptr<Message>& msg);
void checkDtxTimeout();
- ConsumerImpl& find(const std::string& destination);
- void complete(DeliveryRecord&);
+
+ bool complete(DeliveryRecord&);
AckRange findRange(DeliveryId first, DeliveryId last);
void requestDispatch();
- void requestDispatch(ConsumerImpl&);
- void cancel(ConsumerImpl&);
+ void cancel(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.
@@ -146,16 +183,13 @@ class SemanticState : public sys::OutputTask,
*/
Queue::shared_ptr getQueue(const std::string& name) const;
- uint32_t setPrefetchSize(uint32_t size){ return prefetchSize = size; }
- uint16_t setPrefetchCount(uint16_t n){ return prefetchCount = n; }
-
bool exists(const string& consumerTag);
- /**
- *@param tagInOut - if empty it is updated with the generated token.
- */
- void consume(DeliveryToken::shared_ptr token, string& tagInOut, Queue::shared_ptr queue,
- bool nolocal, bool ackRequired, bool acquire, bool exclusive, const framing::FieldTable* = 0);
+ void consume(const string& destination,
+ Queue::shared_ptr queue,
+ bool ackRequired, bool acquire, bool exclusive,
+ const string& resumeId=string(), uint64_t resumeTtl=0,
+ const framing::FieldTable& = framing::FieldTable());
void cancel(const string& tag);
@@ -166,7 +200,6 @@ class SemanticState : public sys::OutputTask,
void flush(const std::string& destination);
void stop(const std::string& destination);
- bool get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected);
void startTx();
void commit(MessageStore* const store);
void rollback();
@@ -176,17 +209,29 @@ class SemanticState : public sys::OutputTask,
void suspendDtx(const std::string& xid);
void resumeDtx(const std::string& xid);
void recover(bool requeue);
- DeliveryId redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token);
+ 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);
- bool hasOutput() { return outputTasks.hasOutput(); }
- bool doOutput() { return outputTasks.doOutput(); }
- //final 0-10 spec (completed and accepted are distinct):
- void completed(DeliveryId deliveryTag, DeliveryId endTag);
- void accepted(DeliveryId deliveryTag, DeliveryId endTag);
+ void completed(const framing::SequenceSet& commands);
+ void accepted(const framing::SequenceSet& commands);
+
+ void attached();
+ void detached();
+
+ // 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
diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp
index 03022b00bb..a7743d95ab 100644
--- a/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -15,17 +15,23 @@
* limitations under the License.
*
*/
-#include "SessionAdapter.h"
-#include "Connection.h"
-#include "DeliveryToken.h"
-#include "MessageDelivery.h"
-#include "Queue.h"
+#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/constants.h"
+#include "qpid/framing/enum.h"
#include "qpid/log/Statement.h"
-#include "qpid/amqp_0_10/exceptions.h"
#include "qpid/framing/SequenceSet.h"
+#include "qpid/management/ManagementAgent.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>
@@ -35,6 +41,9 @@ 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;
@@ -48,23 +57,24 @@ SessionAdapter::SessionAdapter(SemanticState& 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){
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- std::map<std::string, std::string> params;
- params.insert(make_pair("TYPE", type));
- params.insert(make_pair("ALT", alternateExchange));
- params.insert(make_pair("PAS", std::string(passive ? "Y" : "N") ));
- params.insert(make_pair("DURA", std::string(durable ? "Y" : "N")));
- if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::EXCHANGE,exchange,&params) )
- throw NotAllowedException("ACL denied exhange declare request");
- }
-
+ AclModule* acl = getBroker().getAcl();
+ 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, std::string(passive ? _TRUE : _FALSE) ));
+ params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,&params) )
+ throw NotAllowedException(QPID_MSG("ACL denied exchange declare request from " << getConnection().getUserId()));
+ }
+
//TODO: implement autoDelete
Exchange::shared_ptr alternate;
if (!alternateExchange.empty()) {
@@ -75,21 +85,31 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const
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().getExchanges().declare(exchange, type, durable, args);
if (response.second) {
- if (durable) {
- getBroker().getStore().create(*response.first, args);
- }
if (alternate) {
response.first->setAlternate(alternate);
alternate->incAlternateUsers();
}
+ if (durable) {
+ getBroker().getStore().create(*response.first, args);
+ }
} else {
checkType(response.first, type);
checkAlternate(response.first, alternate);
}
- }catch(UnknownExchangeTypeException& e){
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(), getConnection().getUserId(), exchange, type,
+ alternateExchange, durable, false, args,
+ response.second ? "created" : "existing"));
+
+ }catch(UnknownExchangeTypeException& /*e*/){
throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type));
}
}
@@ -104,57 +124,62 @@ void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchang
void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate)
{
- if (alternate && alternate != exchange->getAlternate())
- throw NotAllowedException(
- QPID_MSG("Exchange declared with alternate-exchange "
- << exchange->getAlternate()->getName() << ", requested "
- << alternate->getName()));
+ 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*/){
-
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::DELETE,acl::EXCHANGE,name,NULL) )
- throw NotAllowedException("ACL denied exhange delete request");
+void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/)
+{
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) )
+ throw NotAllowedException(QPID_MSG("ACL denied exchange delete request from " << getConnection().getUserId()));
}
-
//TODO: implement unused
Exchange::shared_ptr exchange(getBroker().getExchanges().get(name));
if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange."));
if (exchange->isDurable()) getBroker().getStore().destroy(*exchange);
if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers();
getBroker().getExchanges().destroy(name);
-}
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventExchangeDelete(getConnection().getUrl(), getConnection().getUserId(), name));
+}
ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name)
{
-
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::ACCESS,acl::EXCHANGE,name,NULL) )
- throw NotAllowedException("ACL denied exhange query request");
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) )
+ throw NotAllowedException(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) {
+ } 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){
+ const FieldTable& arguments)
+{
+ 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, routingKey));
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::BIND,acl::EXCHANGE,exchangeName,routingKey) )
- throw NotAllowedException("ACL denied exhange bind request");
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,&params))
+ throw NotAllowedException(QPID_MSG("ACL denied exchange bind request from " << getConnection().getUserId()));
}
Queue::shared_ptr queue = getQueue(queueName);
@@ -166,30 +191,29 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName,
if (exchange->isDurable() && queue->isDurable()) {
getBroker().getStore().bind(*exchange, *queue, routingKey, arguments);
}
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventBind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, exchangeRoutingKey, arguments));
}
}else{
- throw NotFoundException(
- "Bind failed. No such exchange: " + exchangeName);
+ throw NotFoundException("Bind failed. No such exchange: " + exchangeName);
}
}
-void
-SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
- const string& exchangeName,
- const string& routingKey)
+void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
+ const string& exchangeName,
+ const string& routingKey)
{
-
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- std::map<std::string, std::string> params;
- params.insert(make_pair("QN", queueName));
- params.insert(make_pair("RKEY", routingKey));
- if (!acl->authorise(getConnection().getUserId(),acl::UNBIND,acl::EXCHANGE,exchangeName,&params) )
- throw NotAllowedException("ACL denied exchange unbind request");
+ 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, routingKey));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,&params) )
+ throw NotAllowedException(QPID_MSG("ACL denied exchange unbind request from " << getConnection().getUserId()));
}
-
Queue::shared_ptr queue = getQueue(queueName);
if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName);
@@ -197,10 +221,14 @@ SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName);
//TODO: revise unbind to rely solely on binding key (not args)
- if (exchange->unbind(queue, routingKey, 0) && exchange->isDurable() && queue->isDurable()) {
- getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable());
- }
+ if (exchange->unbind(queue, routingKey, 0)) {
+ if (exchange->isDurable() && queue->isDurable())
+ getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable());
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventUnbind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, routingKey));
+ }
}
ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName,
@@ -208,16 +236,15 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string
const std::string& key,
const framing::FieldTable& args)
{
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- std::map<std::string, std::string> params;
- params.insert(make_pair("QUEUE", queueName));
- params.insert(make_pair("RKEY", queueName));
- if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::EXCHANGE,exchangeName,&params) )
- throw NotAllowedException("ACL denied exhange bound request");
+ 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 NotAllowedException(QPID_MSG("ACL denied exchange bound request from " << getConnection().getUserId()));
}
-
+
Exchange::shared_ptr exchange;
try {
exchange = getBroker().getExchanges().get(exchangeName);
@@ -229,7 +256,7 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string
}
if (!exchange) {
- return ExchangeBoundResult(true, false, false, false, false);
+ 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)) {
@@ -268,7 +295,6 @@ void SessionAdapter::QueueHandlerImpl::destroyExclusiveQueues()
exclusiveQueues.erase(exclusiveQueues.begin());
}
}
-
bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const
{
@@ -278,13 +304,12 @@ bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const
QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name)
{
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::ACCESS,acl::QUEUE,name,NULL) )
- throw NotAllowedException("ACL denied queue query request");
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,NULL) )
+ throw NotAllowedException(QPID_MSG("ACL denied queue query request from " << getConnection().getUserId()));
}
-
+
Queue::shared_ptr queue = session.getBroker().getQueues().find(name);
if (queue) {
@@ -304,20 +329,23 @@ QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name)
}
void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange,
- bool passive, bool durable, bool exclusive,
- bool autoDelete, const qpid::framing::FieldTable& arguments){
-
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- std::map<std::string, std::string> params;
- params.insert(make_pair("ALT", alternateExchange));
- params.insert(make_pair("PAS", std::string(passive ? "Y" : "N") ));
- params.insert(make_pair("DURA", std::string(durable ? "Y" : "N")));
- params.insert(make_pair("EXCLUS", std::string(exclusive ? "Y" : "N")));
- params.insert(make_pair("AUTOD", std::string(autoDelete ? "Y" : "N")));
- if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::QUEUE,name,&params) )
- throw NotAllowedException("ACL denied queue create request");
+ bool passive, bool durable, bool exclusive,
+ bool autoDelete, const qpid::framing::FieldTable& arguments)
+{
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) ));
+ 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 NotAllowedException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId()));
}
Exchange::shared_ptr alternate;
@@ -326,17 +354,16 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
}
Queue::shared_ptr queue;
if (passive && !name.empty()) {
- queue = getQueue(name);
+ queue = getQueue(name);
//TODO: check alternate-exchange is as expected
} else {
- std::pair<Queue::shared_ptr, bool> queue_created =
- getBroker().getQueues().declare(
- name, durable,
- autoDelete,
- exclusive ? this : 0);
- queue = queue_created.first;
- assert(queue);
- if (queue_created.second) { // This is a new queue
+ std::pair<Queue::shared_ptr, bool> queue_created =
+ getBroker().getQueues().declare(name, durable,
+ autoDelete,
+ exclusive ? &session : 0);
+ queue = queue_created.first;
+ assert(queue);
+ if (queue_created.second) { // This is a new queue
if (alternate) {
queue->setAlternateExchange(alternate);
alternate->incAlternateUsers();
@@ -345,48 +372,56 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
//apply settings & create persistent record if required
queue_created.first->create(arguments);
- //add default binding:
- getBroker().getExchanges().getDefault()->bind(queue, name, 0);
+ //add default binding:
+ getBroker().getExchanges().getDefault()->bind(queue, name, 0);
queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments);
//handle automatic cleanup:
- if (exclusive) {
- exclusiveQueues.push_back(queue);
- }
- } else {
- if (exclusive && queue->setExclusiveOwner(this)) {
- exclusiveQueues.push_back(queue);
+ 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, arguments,
+ queue_created.second ? "created" : "existing"));
}
- if (exclusive && !queue->isExclusiveOwner(this))
- throw ResourceLockedException(
- QPID_MSG("Cannot grant exclusive access to queue "
- << queue->getName()));
+
+ 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::PURGE,acl::QUEUE,queue,NULL) )
- throw NotAllowedException("ACL denied queue purge request");
+ AclModule* acl = getBroker().getAcl();
+ if (acl)
+ {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_PURGE,acl::OBJ_QUEUE,queue,NULL) )
+ throw NotAllowedException(QPID_MSG("ACL denied queue purge request from " << getConnection().getUserId()));
}
getQueue(queue)->purge();
}
void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty){
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::DELETE,acl::QUEUE,queue,NULL) )
- throw NotAllowedException("ACL denied queue delete request");
+ AclModule* acl = getBroker().getAcl();
+ if (acl)
+ {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_QUEUE,queue,NULL) )
+ throw NotAllowedException(QPID_MSG("ACL denied queue delete request from " << getConnection().getUserId()));
}
- ChannelException error(0, "");
Queue::shared_ptr q = getQueue(queue);
+ if (q->hasExclusiveOwner() && !q->isExclusiveOwner(&session))
+ throw ResourceLockedException(QPID_MSG("Cannot delete queue "
+ << queue << "; it is exclusive to another session"));
if(ifEmpty && q->getMessageCount() > 0){
throw PreconditionFailedException("Queue not empty.");
}else if(ifUnused && q->getConsumerCount() > 0){
@@ -400,16 +435,18 @@ void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnuse
q->destroy();
getBroker().getQueues().destroy(queue);
q->unbind(getBroker().getExchanges(), q);
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventQueueDelete(getConnection().getUrl(), getConnection().getUserId(), queue));
}
}
-
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)),
- acceptOp(boost::bind(&SemanticState::accepted, &state, _1, _2))
+ rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2))
{}
//
@@ -431,37 +468,47 @@ void SessionAdapter::MessageHandlerImpl::release(const SequenceSet& transfers, b
void
SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
- const string& destination,
- uint8_t acceptMode,
- uint8_t acquireMode,
- bool exclusive,
- const string& /*resumeId*/,//TODO implement resume behaviour
- uint64_t /*resumeTtl*/,
- const FieldTable& arguments)
+ 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)
- {
- // add flags as needed
- if (!acl->authorise(getConnection().getUserId(),acl::CONSUME,acl::QUEUE,queueName,NULL) )
- throw NotAllowedException("ACL denied Queue subscribe request");
+ AclModule* acl = getBroker().getAcl();
+ if (acl)
+ {
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CONSUME,acl::OBJ_QUEUE,queueName,NULL) )
+ throw NotAllowedException(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))
+ throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue "
+ << queue->getName()));
+
+ state.consume(destination, queue,
+ acceptMode == 0, acquireMode == 0, exclusive,
+ resumeId, resumeTtl, arguments);
- string tag = destination;
- state.consume(MessageDelivery::getMessageDeliveryToken(destination, acceptMode, acquireMode),
- tag, queue, false, //TODO get rid of no-local
- acceptMode == 0, acquireMode == 0, exclusive, &arguments);
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventSubscribe(getConnection().getUrl(), getConnection().getUserId(),
+ queueName, destination, exclusive, arguments));
}
void
SessionAdapter::MessageHandlerImpl::cancel(const string& destination )
{
state.cancel(destination);
+
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventUnsubscribe(getConnection().getUrl(), getConnection().getUserId(), destination));
}
void
@@ -510,8 +557,7 @@ void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination)
void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands)
{
-
- commands.for_each(acceptOp);
+ state.accepted(commands);
}
framing::MessageAcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers)
@@ -595,7 +641,7 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid,
if (suspend) {
throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set."));
} else {
- return XaResult(XA_RBROLLBACK);
+ return XaResult(XA_STATUS_XA_RBROLLBACK);
}
} else {
if (suspend) {
@@ -603,10 +649,10 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid,
} else {
state.endDtx(convert(xid), false);
}
- return XaResult(XA_OK);
+ return XaResult(XA_STATUS_XA_OK);
}
- } catch (const DtxTimeoutException& e) {
- return XaResult(XA_RBTIMEOUT);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
}
}
@@ -623,9 +669,9 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid,
} else {
state.startDtx(convert(xid), getBroker().getDtxManager(), join);
}
- return XaResult(XA_OK);
- } catch (const DtxTimeoutException& e) {
- return XaResult(XA_RBTIMEOUT);
+ return XaResult(XA_STATUS_XA_OK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
}
}
@@ -633,9 +679,9 @@ XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid)
{
try {
bool ok = getBroker().getDtxManager().prepare(convert(xid));
- return XaResult(ok ? XA_OK : XA_RBROLLBACK);
- } catch (const DtxTimeoutException& e) {
- return XaResult(XA_RBTIMEOUT);
+ return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
}
}
@@ -644,9 +690,9 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid,
{
try {
bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase);
- return XaResult(ok ? XA_OK : XA_RBROLLBACK);
- } catch (const DtxTimeoutException& e) {
- return XaResult(XA_RBTIMEOUT);
+ return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
}
}
@@ -655,9 +701,9 @@ XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid)
{
try {
getBroker().getDtxManager().rollback(convert(xid));
- return XaResult(XA_OK);
- } catch (const DtxTimeoutException& e) {
- return XaResult(XA_RBTIMEOUT);
+ return XaResult(XA_STATUS_XA_OK);
+ } catch (const DtxTimeoutException& /*e*/) {
+ return XaResult(XA_STATUS_XA_RBTIMEOUT);
}
}
@@ -699,11 +745,11 @@ void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid,
Queue::shared_ptr SessionAdapter::HandlerHelper::getQueue(const string& name) const {
Queue::shared_ptr queue;
if (name.empty()) {
- throw amqp_0_10::IllegalArgumentException(QPID_MSG("No queue name specified."));
+ throw framing::IllegalArgumentException(QPID_MSG("No queue name specified."));
} else {
queue = session.getBroker().getQueues().find(name);
if (!queue)
- throw amqp_0_10::NotFoundException(QPID_MSG("Queue not found: "<<name));
+ throw framing::NotFoundException(QPID_MSG("Queue not found: "<<name));
}
return queue;
}
diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h
index 4eaaf13f8d..b69f258037 100644
--- a/cpp/src/qpid/broker/SessionAdapter.h
+++ b/cpp/src/qpid/broker/SessionAdapter.h
@@ -19,15 +19,16 @@
*
*/
-#include "HandlerImpl.h"
+#include "qpid/broker/HandlerImpl.h"
-#include "ConnectionToken.h"
-#include "OwnershipToken.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>
@@ -68,6 +69,12 @@ class Queue;
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
@@ -102,14 +109,14 @@ class Queue;
const std::string& routingKey,
const framing::FieldTable& arguments);
private:
- void checkType(shared_ptr<Exchange> exchange, const std::string& type);
+ void checkType(boost::shared_ptr<Exchange> exchange, const std::string& type);
- void checkAlternate(shared_ptr<Exchange> exchange,
- shared_ptr<Exchange> alternate);
+ void checkAlternate(boost::shared_ptr<Exchange> exchange,
+ boost::shared_ptr<Exchange> alternate);
};
class QueueHandlerImpl : public QueueHandler,
- public HandlerHelper, public OwnershipToken
+ public HandlerHelper
{
Broker& broker;
std::vector< boost::shared_ptr<Queue> > exclusiveQueues;
@@ -130,6 +137,10 @@ class Queue;
bool isLocal(const ConnectionToken* t) const;
void destroyExclusiveQueues();
+ template <class F> void eachExclusiveQueue(F f)
+ {
+ std::for_each(exclusiveQueues.begin(), exclusiveQueues.end(), f);
+ }
};
class MessageHandlerImpl :
diff --git a/cpp/src/qpid/broker/SessionContext.h b/cpp/src/qpid/broker/SessionContext.h
index 7a277964ab..afbbb2cc22 100644
--- a/cpp/src/qpid/broker/SessionContext.h
+++ b/cpp/src/qpid/broker/SessionContext.h
@@ -26,9 +26,9 @@
#include "qpid/framing/AMQP_ClientProxy.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/sys/OutputControl.h"
-#include "ConnectionState.h"
-#include "OwnershipToken.h"
-
+#include "qpid/broker/ConnectionState.h"
+#include "qpid/broker/OwnershipToken.h"
+#include "qpid/SessionId.h"
#include <boost/noncopyable.hpp>
@@ -40,9 +40,12 @@ 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;
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp
index c752f6315b..7106f85807 100644
--- a/cpp/src/qpid/broker/SessionHandler.cpp
+++ b/cpp/src/qpid/broker/SessionHandler.cpp
@@ -18,9 +18,9 @@
*
*/
-#include "SessionHandler.h"
-#include "SessionState.h"
-#include "Connection.h"
+#include "qpid/broker/SessionHandler.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/Connection.h"
#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
@@ -34,7 +34,8 @@ using namespace qpid::sys;
SessionHandler::SessionHandler(Connection& c, ChannelId ch)
: amqp_0_10::SessionHandler(&c.getOutput(), ch),
connection(c),
- proxy(out)
+ proxy(out),
+ clusterOrderProxy(c.getClusterOrderOutput() ? new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0)
{}
SessionHandler::~SessionHandler() {}
@@ -44,12 +45,18 @@ ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; }
MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; }
} // namespace
-void SessionHandler::channelException(uint16_t, const std::string&) {
- handleDetach();
+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::connectionException(uint16_t code, const std::string& msg) {
- connection.close(code, msg, 0, 0);
+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; }
@@ -71,6 +78,12 @@ void SessionHandler::setState(const std::string& name, bool force) {
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(); }
@@ -78,18 +91,31 @@ void SessionHandler::readyToSend() {
if (session.get()) session->readyToSend();
}
-// TODO aconway 2008-05-12: hacky - handle attached for bridge clients.
-// We need to integrate the client code so we can run a real client
-// in the bridge.
-//
-void SessionHandler::attached(const std::string& name) {
- if (session.get())
- checkName(name);
- else {
+/**
+ * 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();
+ session.reset(new SessionState(connection.getBroker(), *this, id, config));
+ 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()) {
+ 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/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h
index 1aa3137fdf..ca6d6bb193 100644
--- a/cpp/src/qpid/broker/SessionHandler.h
+++ b/cpp/src/qpid/broker/SessionHandler.h
@@ -54,23 +54,42 @@ class SessionHandler : public amqp_0_10::SessionHandler {
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();
-
- // Overrides
- void attached(const std::string& name);
+ 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 channelException(uint16_t code, const std::string& msg);
- virtual void connectionException(uint16_t code, const std::string& msg);
+ 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
diff --git a/cpp/src/qpid/broker/SessionManager.cpp b/cpp/src/qpid/broker/SessionManager.cpp
index e7190fdae6..996a02f4c6 100644
--- a/cpp/src/qpid/broker/SessionManager.cpp
+++ b/cpp/src/qpid/broker/SessionManager.cpp
@@ -19,8 +19,8 @@
*
*/
-#include "SessionManager.h"
-#include "SessionState.h"
+#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"
@@ -86,9 +86,14 @@ void SessionManager::forget(const SessionId& id) {
void SessionManager::eraseExpired() {
// Called with lock held.
if (!detached.empty()) {
- Detached::iterator keep = std::lower_bound(
- detached.begin(), detached.end(), now(),
- boost::bind(std::less<AbsTime>(), boost::bind(&SessionState::expiry, _1), _2));
+ // 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);
diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp
index aa6f6b7520..4c5aaf7fc4 100644
--- a/cpp/src/qpid/broker/SessionState.cpp
+++ b/cpp/src/qpid/broker/SessionState.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,18 +18,22 @@
* under the License.
*
*/
-#include "SessionState.h"
-#include "Broker.h"
-#include "ConnectionState.h"
-#include "MessageDelivery.h"
-#include "SessionManager.h"
-#include "SessionHandler.h"
+#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>
@@ -44,36 +48,51 @@ 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)
+ Broker& b, SessionHandler& h, const SessionId& id, const SessionState::Configuration& config)
: qpid::SessionState(id, config),
broker(b), handler(&h),
- ignoring(false),
semanticState(*this, *this),
adapter(semanticState),
msgBuilder(&broker.getStore(), broker.getStagingThreshold()),
enqueuedOp(boost::bind(&SessionState::enqueued, this, _1)),
- mgmtObject(0)
+ mgmtObject(0),
+ rateFlowcontrol(0)
{
+ 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");
+ }
+ }
Manageable* parent = broker.GetVhostObject ();
if (parent != 0) {
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = getBroker().getManagementAgent();
if (agent != 0) {
- mgmtObject = new management::Session (agent, this, parent, getId().getName());
+ mgmtObject = new _qmf::Session
+ (agent, this, parent, getId().getName());
mgmtObject->set_attached (0);
mgmtObject->set_detachedLifespan (0);
- agent->addObject (mgmtObject);
+ mgmtObject->clr_expireTime();
+ if (rateFlowcontrol) mgmtObject->set_maxClientRate(maxRate);
+ agent->addObject (mgmtObject, agent->allocateId(this));
}
}
attach(h);
}
SessionState::~SessionState() {
- // Remove ID from active session list.
- broker.getSessionManager().forget(getId());
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
+
+ if (flowControlTimer)
+ flowControlTimer->cancel();
}
AMQP_ClientProxy& SessionState::getProxy() {
@@ -81,6 +100,11 @@ AMQP_ClientProxy& SessionState::getProxy() {
return handler->getProxy();
}
+uint16_t SessionState::getChannel() const {
+ assert(isAttached());
+ return handler->getChannel();
+}
+
ConnectionState& SessionState::getConnection() {
assert(isAttached());
return handler->getConnection();
@@ -92,18 +116,19 @@ bool SessionState::isLocal(const ConnectionToken* t) const
}
void SessionState::detach() {
- // activateOutput can be called in a different thread, lock to protect attached status
- Mutex::ScopedLock l(lock);
QPID_LOG(debug, getId() << ": detached on broker.");
- getConnection().outputTasks.removeOutputTask(&semanticState);
+ 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) {
- // activateOutput can be called in a different thread, lock to protect attached status
- Mutex::ScopedLock l(lock);
QPID_LOG(debug, getId() << ": attached on broker.");
handler = &h;
if (mgmtObject != 0)
@@ -114,34 +139,42 @@ void SessionState::attach(SessionHandler& h) {
}
}
+void SessionState::abort() {
+ if (isAttached())
+ getConnection().outputTasks.abort();
+}
+
void SessionState::activateOutput() {
- // activateOutput can be called in a different thread, lock to protect attached status
- Mutex::ScopedLock l(lock);
- if (isAttached())
+ 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*/)
+ Args& /*args*/,
+ string& /*text*/)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
switch (methodId)
{
- case management::Session::METHOD_DETACH :
- if (handler != 0)
- {
+ case _qmf::Session::METHOD_DETACH :
+ if (handler != 0) {
handler->sendDetach();
}
status = Manageable::STATUS_OK;
break;
- case management::Session::METHOD_CLOSE :
+ case _qmf::Session::METHOD_CLOSE :
/*
if (handler != 0)
{
@@ -151,8 +184,8 @@ Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
break;
*/
- case management::Session::METHOD_SOLICITACK :
- case management::Session::METHOD_RESETLIFESPAN :
+ case _qmf::Session::METHOD_SOLICITACK :
+ case _qmf::Session::METHOD_RESETLIFESPAN :
status = Manageable::STATUS_NOT_IMPLEMENTED;
break;
}
@@ -162,18 +195,42 @@ Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
void SessionState::handleCommand(framing::AMQMethodBody* method, const SequenceNumber& id) {
Invoker::Result invocation = invoke(adapter, *method);
- receiverCompleted(id);
+ 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()) {
+ if (method->isSync()) {
incomplete.process(enqueuedOp, true);
- sendCompletion();
+ sendAcceptAndCompletion();
}
}
+struct ScheduledCreditTask : public sys::TimerTask {
+ sys::Timer& timer;
+ SessionState& sessionState;
+ ScheduledCreditTask(const qpid::sys::Duration& d, sys::Timer& t,
+ SessionState& s) :
+ TimerTask(d),
+ 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
@@ -183,14 +240,13 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id)
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;
- header.setBody(AMQHeaderBody());
+ AMQFrame header((AMQHeaderBody()));
header.setBof(false);
header.setEof(false);
- msg->getFrames().append(header);
+ msg->getFrames().append(header);
}
msg->setPublisher(&getConnection());
- semanticState.handle(msg);
+ semanticState.handle(msg);
msgBuilder.end();
if (msg->isEnqueueComplete()) {
@@ -199,51 +255,80 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id)
incomplete.add(msg);
}
- //hold up execution until async enqueue is complete
- if (msg->getFrames().getMethod()->isSync()) {
+ //hold up execution until async enqueue is complete
+ if (msg->getFrames().getMethod()->isSync()) {
incomplete.process(enqueuedOp, true);
- sendCompletion();
+ sendAcceptAndCompletion();
} else {
incomplete.process(enqueuedOp, false);
}
}
+
+ // 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();
}
void SessionState::enqueued(boost::intrusive_ptr<Message> msg)
{
receiverCompleted(msg->getCommandId());
- if (msg->requiresAccept())
- getProxy().getMessage().accept(SequenceSet(msg->getCommandId()));
+ if (msg->requiresAccept())
+ accepted.add(msg->getCommandId());
}
void SessionState::handleIn(AMQFrame& frame) {
SequenceNumber commandId = receiverGetCurrent();
- try {
- //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");
- }
- } catch(const SessionException& e) {
- //TODO: better implementation of new exception handling mechanism
-
- //0-10 final changes the types of exceptions, 'model layer'
- //exceptions will all be session exceptions regardless of
- //current channel/connection classification
-
- AMQMethodBody* m = frame.getMethod();
- if (m) {
- getProxy().getExecution().exception(e.code, commandId, m->amqpClassId(), m->amqpMethodId(), 0, e.what(), FieldTable());
- } else {
- getProxy().getExecution().exception(e.code, commandId, 0, 0, 0, e.what(), FieldTable());
- }
- ignoring = true;
- handler->sendDetach();
+ //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");
}
}
@@ -252,32 +337,50 @@ void SessionState::handleOut(AMQFrame& frame) {
handler->out(frame);
}
-DeliveryId SessionState::deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token)
+void SessionState::deliver(DeliveryRecord& msg, bool sync)
{
uint32_t maxFrameSize = getConnection().getFrameMax();
assert(senderGetCommandPoint().offset == 0);
SequenceNumber commandId = senderGetCommandPoint().command;
- MessageDelivery::deliver(msg, getProxy().getHandler(), commandId, token, maxFrameSize);
+ msg.deliver(getProxy().getHandler(), commandId, maxFrameSize);
assert(senderGetCommandPoint() == SessionPoint(commandId+1, 0)); // Delivery has moved sendPoint.
- return commandId;
+ if (sync) {
+ AMQP_ClientProxy::Execution& p(getProxy().getExecution());
+ Proxy::ScopedSync s(p);
+ p.sync();
+ }
}
-void SessionState::sendCompletion() { handler->sendCompletion(); }
+void SessionState::sendCompletion() {
+ handler->sendCompletion();
+}
void SessionState::senderCompleted(const SequenceSet& commands) {
qpid::SessionState::senderCompleted(commands);
- for (SequenceSet::RangeIterator i = commands.rangesBegin(); i != commands.rangesEnd(); i++)
- semanticState.completed(i->first(), i->last());
+ semanticState.completed(commands);
}
void SessionState::readyToSend() {
QPID_LOG(debug, getId() << ": ready to send, activating output.");
assert(handler);
- sys::AggregateOutput& tasks = handler->getConnection().outputTasks;
- tasks.addOutputTask(&semanticState);
- tasks.activateOutput();
+ 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; }
+framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() {
+ return handler->getClusterOrderProxy();
+}
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h
index 96f2e8f512..eade93ddaa 100644
--- a/cpp/src/qpid/broker/SessionState.h
+++ b/cpp/src/qpid/broker/SessionState.h
@@ -25,16 +25,15 @@
#include "qpid/SessionState.h"
#include "qpid/framing/FrameHandler.h"
#include "qpid/framing/SequenceSet.h"
-#include "qpid/sys/Mutex.h"
#include "qpid/sys/Time.h"
#include "qpid/management/Manageable.h"
-#include "qpid/management/Session.h"
-#include "SessionAdapter.h"
-#include "DeliveryAdapter.h"
-#include "IncompleteMessageList.h"
-#include "MessageBuilder.h"
-#include "SessionContext.h"
-#include "SemanticState.h"
+#include "qmf/org/apache/qpid/broker/Session.h"
+#include "qpid/broker/SessionAdapter.h"
+#include "qpid/broker/DeliveryAdapter.h"
+#include "qpid/broker/IncompleteMessageList.h"
+#include "qpid/broker/MessageBuilder.h"
+#include "qpid/broker/SessionContext.h"
+#include "qpid/broker/SemanticState.h"
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
@@ -49,6 +48,10 @@ namespace framing {
class AMQP_ClientProxy;
}
+namespace sys {
+class TimerTask;
+}
+
namespace broker {
class Broker;
@@ -56,12 +59,13 @@ 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,
+class SessionState : public qpid::SessionState,
public SessionContext,
public DeliveryAdapter,
public management::Manageable,
@@ -74,10 +78,14 @@ class SessionState : public qpid::SessionState,
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;
@@ -85,22 +93,33 @@ class SessionState : public qpid::SessionState,
Broker& getBroker();
/** OutputControl **/
+ void abort();
void activateOutput();
+ void giveReadCredit(int32_t);
void senderCompleted(const framing::SequenceSet& ranges);
-
+
void sendCompletion();
//delivery adapter methods:
- DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token);
+ void deliver(DeliveryRecord&, bool sync);
// Manageable entry points
management::ManagementObject* GetManagementObject (void) const;
management::Manageable::status_t
- ManagementMethod (uint32_t methodId, management::Args& args);
+ 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(); }
+
private:
void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id);
@@ -114,20 +133,34 @@ class SessionState : public qpid::SessionState,
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;
+ SessionHandler* handler;
sys::AbsTime expiry; // Used by SessionManager.
- sys::Mutex lock;
- bool ignoring;
- std::string name;
SemanticState semanticState;
SessionAdapter adapter;
MessageBuilder msgBuilder;
IncompleteMessageList incomplete;
IncompleteMessageList::CompletionListener enqueuedOp;
- management::Session* mgmtObject;
+ 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;
- friend class SessionManager;
+ friend class SessionManager;
};
diff --git a/cpp/src/qpid/broker/SignalHandler.cpp b/cpp/src/qpid/broker/SignalHandler.cpp
index fee54cfdfc..b565cfd419 100644
--- a/cpp/src/qpid/broker/SignalHandler.cpp
+++ b/cpp/src/qpid/broker/SignalHandler.cpp
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "SignalHandler.h"
-#include "Broker.h"
+#include "qpid/broker/SignalHandler.h"
+#include "qpid/broker/Broker.h"
#include <signal.h>
namespace qpid {
@@ -36,11 +36,10 @@ void SignalHandler::setBroker(const boost::intrusive_ptr<Broker>& b) {
signal(SIGHUP,SIG_IGN); // TODO aconway 2007-07-18: reload config.
signal(SIGCHLD,SIG_IGN);
- signal(SIGTSTP,SIG_IGN);
- signal(SIGTTOU,SIG_IGN);
- signal(SIGTTIN,SIG_IGN);
}
+void SignalHandler::shutdown() { shutdownHandler(0); }
+
void SignalHandler::shutdownHandler(int) {
if (broker.get()) {
broker->shutdown();
diff --git a/cpp/src/qpid/broker/SignalHandler.h b/cpp/src/qpid/broker/SignalHandler.h
index d2cdfae07c..bbe831b61d 100644
--- a/cpp/src/qpid/broker/SignalHandler.h
+++ b/cpp/src/qpid/broker/SignalHandler.h
@@ -38,6 +38,9 @@ class SignalHandler
/** Set the broker to be shutdown on signals */
static void setBroker(const boost::intrusive_ptr<Broker>& broker);
+ /** Initiate shut-down of broker */
+ static void shutdown();
+
private:
static void shutdownHandler(int);
static boost::intrusive_ptr<Broker> broker;
diff --git a/cpp/src/qpid/broker/System.cpp b/cpp/src/qpid/broker/System.cpp
index 6c58339432..455ad11cf2 100644
--- a/cpp/src/qpid/broker/System.cpp
+++ b/cpp/src/qpid/broker/System.cpp
@@ -17,20 +17,22 @@
// under the License.
//
-#include "System.h"
-#include "qpid/agent/ManagementAgent.h"
+#include "qpid/broker/System.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/framing/Uuid.h"
-#include <sys/utsname.h>
+#include "qpid/sys/SystemInfo.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) : mgmtObject(0)
+System::System (string _dataDir, Broker* broker) : mgmtObject(0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker ? broker->getManagementAgent() : 0;
if (agent != 0)
{
@@ -62,18 +64,20 @@ System::System (string _dataDir) : mgmtObject(0)
}
}
- mgmtObject = new management::System (agent, this, systemId);
- struct utsname _uname;
- if (uname (&_uname) == 0)
- {
- mgmtObject->set_osName (std::string (_uname.sysname));
- mgmtObject->set_nodeName (std::string (_uname.nodename));
- mgmtObject->set_release (std::string (_uname.release));
- mgmtObject->set_version (std::string (_uname.version));
- mgmtObject->set_machine (std::string (_uname.machine));
- }
+ mgmtObject = new _qmf::System (agent, this, systemId);
+ 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, 1, 1);
+ agent->addObject (mgmtObject, 0x1000000000000001LL);
}
}
diff --git a/cpp/src/qpid/broker/System.h b/cpp/src/qpid/broker/System.h
index ef7c6ba73b..0fc2c2bd88 100644
--- a/cpp/src/qpid/broker/System.h
+++ b/cpp/src/qpid/broker/System.h
@@ -21,24 +21,26 @@
//
#include "qpid/management/Manageable.h"
-#include "qpid/management/System.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:
- management::System* mgmtObject;
+ qmf::org::apache::qpid::broker::System* mgmtObject;
public:
typedef boost::shared_ptr<System> shared_ptr;
- System (std::string _dataDir);
+ System (std::string _dataDir, Broker* broker = 0);
management::ManagementObject* GetManagementObject (void) const
{ return mgmtObject; }
diff --git a/cpp/src/qpid/broker/Timer.cpp b/cpp/src/qpid/broker/Timer.cpp
deleted file mode 100644
index 0b0d3ba63d..0000000000
--- a/cpp/src/qpid/broker/Timer.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "Timer.h"
-#include <iostream>
-
-using boost::intrusive_ptr;
-using qpid::sys::AbsTime;
-using qpid::sys::Duration;
-using qpid::sys::Monitor;
-using qpid::sys::Thread;
-using namespace qpid::broker;
-
-TimerTask::TimerTask(Duration timeout) :
- duration(timeout), time(AbsTime::now(), timeout), cancelled(false) {}
-
-TimerTask::TimerTask(AbsTime _time) :
- duration(0), time(_time), cancelled(false) {}
-
-TimerTask::~TimerTask(){}
-
-void TimerTask::reset() { time = AbsTime(AbsTime::now(), duration); }
-
-Timer::Timer() : active(false)
-{
- start();
-}
-
-Timer::~Timer()
-{
- stop();
-}
-
-void Timer::run()
-{
- Monitor::ScopedLock l(monitor);
- while(active){
- if (tasks.empty()) {
- monitor.wait();
- } else {
- intrusive_ptr<TimerTask> t = tasks.top();
- if (t->cancelled) {
- tasks.pop();
- } else if(t->time < AbsTime::now()) {
- tasks.pop();
- Monitor::ScopedUnlock u(monitor);
- t->fire();
- } else {
- monitor.wait(t->time);
- }
- }
- }
-}
-
-void Timer::add(intrusive_ptr<TimerTask> task)
-{
- Monitor::ScopedLock l(monitor);
- 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();
-}
-
-bool Later::operator()(const intrusive_ptr<TimerTask>& a,
- const intrusive_ptr<TimerTask>& b) const
-{
- return a.get() && b.get() && a->time > b->time;
-}
-
diff --git a/cpp/src/qpid/broker/Timer.h b/cpp/src/qpid/broker/Timer.h
deleted file mode 100644
index f702f0f32d..0000000000
--- a/cpp/src/qpid/broker/Timer.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _Timer_
-#define _Timer_
-
-#include "qpid/sys/Monitor.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/RefCounted.h"
-
-#include <memory>
-#include <queue>
-
-#include <boost/intrusive_ptr.hpp>
-
-namespace qpid {
-namespace broker {
-
-struct TimerTask : public RefCounted {
- const qpid::sys::Duration duration;
- qpid::sys::AbsTime time;
- volatile bool cancelled;
-
- TimerTask(qpid::sys::Duration timeout);
- TimerTask(qpid::sys::AbsTime time);
- virtual ~TimerTask();
- void reset();
- virtual void fire() = 0;
-};
-
-struct Later {
- bool operator()(const boost::intrusive_ptr<TimerTask>& a,
- const boost::intrusive_ptr<TimerTask>& b) const;
-};
-
-class Timer : private qpid::sys::Runnable {
- protected:
- qpid::sys::Monitor monitor;
- std::priority_queue<boost::intrusive_ptr<TimerTask>,
- std::vector<boost::intrusive_ptr<TimerTask> >,
- Later> tasks;
- qpid::sys::Thread runner;
- bool active;
-
- virtual void run();
-
- public:
- Timer();
- virtual ~Timer();
-
- void add(boost::intrusive_ptr<TimerTask> task);
- void start();
- void stop();
-
-};
-
-
-}}
-
-
-#endif
diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp
index 48d6e88503..dd57549b5d 100644
--- a/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/cpp/src/qpid/broker/TopicExchange.cpp
@@ -18,138 +18,249 @@
* under the License.
*
*/
-#include "TopicExchange.h"
+#include "qpid/broker/TopicExchange.h"
#include <algorithm>
-using namespace qpid::broker;
+
+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.
-Tokens& Tokens::operator=(const std::string& s) {
- clear();
- if (s.empty()) return *this;
- std::string::const_iterator i = s.begin();
- while (true) {
- // Invariant: i is at the beginning of the next untokenized word.
- std::string::const_iterator j = std::find(i, s.end(), '.');
- push_back(std::string(i, j));
- if (j == s.end()) return *this;
- i = j + 1;
- }
- return *this;
-}
+namespace
+{
+const std::string qpidFedOp("qpid.fed.op");
+const std::string qpidFedTags("qpid.fed.tags");
+const std::string qpidFedOrigin("qpid.fed.origin");
-TopicPattern& TopicPattern::operator=(const Tokens& tokens) {
- Tokens::operator=(tokens);
- normalize();
- return *this;
+const std::string fedOpBind("B");
+const std::string fedOpUnbind("U");
+const std::string fedOpReorigin("R");
+const std::string fedOpHello("H");
}
+
namespace {
-const std::string hashmark("#");
-const std::string star("*");
-}
+// Iterate over a string of '.'-separated tokens.
+struct TokenIterator {
+ typedef pair<const char*,const char*> Token;
+
+ TokenIterator(const char* b, const char* e) : token(make_pair(b, find(b,e,'.'))), end(e) {}
+
+ 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, '.'));
+ }
+ }
+
+ bool match1(char c) const {
+ return token.second==token.first+1 && *token.first == c;
+ }
+
+ bool match(const Token& token2) {
+ ptrdiff_t l=len();
+ return l == token2.second-token2.first &&
+ strncmp(token.first, token2.first, l) == 0;
+ }
+
+ ptrdiff_t len() const { return token.second - token.first; }
-void TopicPattern::normalize() {
- std::string word;
- Tokens::iterator i = begin();
- while (i != end()) {
- if (*i == hashmark) {
- ++i;
- while (i != end()) {
- // Invariant: *(i-1)==#, [begin()..i-1] is normalized.
- if (*i == star) { // Move * before #.
- std::swap(*i, *(i-1));
- ++i;
- } else if (*i == hashmark) {
- erase(i); // Remove extra #
- } else {
- break;
+ Token token;
+ const char* end;
+};
+
+class Normalizer : public 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 {
- i ++;
+ else
+ next();
}
}
-}
+ string& pattern;
+};
-namespace {
-// TODO aconway 2006-09-20: Ineficient to convert every routingKey to a string.
-// Need StringRef class that operates on a string in place witout copy.
-// Should be applied everywhere strings are extracted from frames.
-//
-bool do_match(Tokens::const_iterator pattern_begin, Tokens::const_iterator pattern_end, Tokens::const_iterator target_begin, Tokens::const_iterator target_end)
-{
- // Invariant: [pattern_begin..p) matches [target_begin..t)
- Tokens::const_iterator p = pattern_begin;
- Tokens::const_iterator t = target_begin;
- while (p != pattern_end && t != target_end)
- {
- if (*p == star || *p == *t) {
- ++p, ++t;
- } else if (*p == hashmark) {
- ++p;
- if (do_match(p, pattern_end, t, target_end)) return true;
- while (t != target_end) {
- ++t;
- if (do_match(p, pattern_end, t, target_end)) return true;
+class Matcher {
+ public:
+ Matcher(const string& p, const string& k)
+ : matched(), pattern(&p[0], &p[0]+p.size()), key(&k[0], &k[0]+k.size())
+ { matched = match(); }
+
+ operator bool() const { return matched; }
+
+ private:
+ Matcher(const char* bp, const char* ep, const char* bk, const char* ek)
+ : matched(), pattern(bp,ep), key(bk,ek) { matched = match(); }
+
+ bool match() {
+ // Invariant: pattern and key match up to but excluding
+ // pattern.token and key.token
+ while (!pattern.finished() && !key.finished()) {
+ if (pattern.match1('*') && !key.finished()) {
+ pattern.next();
+ key.next();
}
- return false;
- } else {
- return false;
+ else if (pattern.match1('#')) {
+ pattern.next();
+ if (pattern.finished()) return true; // Trailing # matches anything.
+ while (!key.finished()) {
+ if (Matcher(pattern.token.first, pattern.end,
+ key.token.first, key.end))
+ return true;
+ key.next();
+ }
+ return false;
+ }
+ else if (pattern.len() == key.len() &&
+ equal(pattern.token.first,pattern.token.second,key.token.first)) {
+ pattern.next();
+ key.next();
+ }
+ else
+ return false;
}
+ if (!pattern.finished() && pattern.match1('#'))
+ pattern.next(); // Trailing # matches empty.
+ return pattern.finished() && key.finished();
}
- while (p != pattern_end && *p == hashmark) ++p; // Ignore trailing #
- return t == target_end && p == pattern_end;
+
+ bool matched;
+ TokenIterator pattern, key;
+};
}
+
+// 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;
}
-bool TopicPattern::match(const Tokens& target) const
+bool TopicExchange::match(const string& pattern, const string& key)
{
- return do_match(begin(), end(), target.begin(), target.end());
+ return Matcher(pattern, key);
}
-TopicExchange::TopicExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent)
+TopicExchange::TopicExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
{
if (mgmtExchange != 0)
mgmtExchange->set_type (typeName);
}
TopicExchange::TopicExchange(const std::string& _name, bool _durable,
- const FieldTable& _args, Manageable* _parent) :
- Exchange(_name, _durable, _args, _parent)
+ const FieldTable& _args, Manageable* _parent, Broker* b) :
+ Exchange(_name, _durable, _args, _parent, b)
{
if (mgmtExchange != 0)
mgmtExchange->set_type (typeName);
}
-bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){
- RWlock::ScopedWlock l(lock);
- TopicPattern routingPattern(routingKey);
- if (isBound(queue, routingPattern)) {
- return false;
- } else {
- Binding::shared_ptr binding (new Binding (routingKey, queue, this));
- bindings[routingPattern].push_back(binding);
- if (mgmtExchange != 0) {
- mgmtExchange->inc_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->inc_bindingCount();
+bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, 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;
+ bool reallyUnbind;
+ string routingPattern = normalize(routingKey);
+
+ if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
+ RWlock::ScopedWlock l(lock);
+ if (isBound(queue, routingPattern)) {
+ return false;
+ } else {
+ Binding::shared_ptr binding (new Binding (routingPattern, queue, this, FieldTable(), fedOrigin));
+ BoundKey& bk = bindings[routingPattern];
+ bk.bindingVector.push_back(binding);
+ propagate = bk.fedBinding.addOrigin(fedOrigin);
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ }
+ }
+ } else if (fedOp == fedOpUnbind) {
+ {
+ RWlock::ScopedWlock l(lock);
+ BoundKey& bk = bindings[routingPattern];
+ propagate = bk.fedBinding.delOrigin(fedOrigin);
+ reallyUnbind = bk.fedBinding.count() == 0;
+ }
+ if (reallyUnbind)
+ unbind(queue, routingPattern, 0);
+ } 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;
+ {
+ RWlock::ScopedRlock l(lock);
+ for (BindingMap::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());
}
- return true;
}
+
+ routeIVE();
+ if (propagate)
+ propagateFedOp(routingKey, fedTags, fedOp, fedOrigin);
+ return true;
}
-bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){
+bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* /*args*/){
RWlock::ScopedWlock l(lock);
- BindingMap::iterator bi = bindings.find(TopicPattern(routingKey));
- Binding::vector& qv(bi->second);
+ string routingKey = normalize(constRoutingKey);
+
+ BindingMap::iterator bi = bindings.find(routingKey);
if (bi == bindings.end()) return false;
+ BoundKey& bk = bi->second;
+ Binding::vector& qv(bk.bindingVector);
+ bool propagate = false;
Binding::vector::iterator q;
for (q = qv.begin(); q != qv.end(); q++)
@@ -157,19 +268,22 @@ bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, co
break;
if(q == qv.end()) return false;
qv.erase(q);
+ propagate = bk.fedBinding.delOrigin();
if(qv.empty()) bindings.erase(bi);
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->dec_bindingCount();
}
+
+ if (propagate)
+ propagateFedOp(routingKey, string(), fedOpUnbind, string());
return true;
}
-bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern)
+bool TopicExchange::isBound(Queue::shared_ptr queue, const string& pattern)
{
BindingMap::iterator bi = bindings.find(pattern);
if (bi == bindings.end()) return false;
- Binding::vector& qv(bi->second);
+ Binding::vector& qv(bi->second.bindingVector);
Binding::vector::iterator q;
for (q = qv.begin(); q != qv.end(); q++)
if ((*q)->queue == queue)
@@ -177,55 +291,41 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern)
return q != qv.end();
}
-void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){
- RWlock::ScopedRlock l(lock);
- uint32_t count(0);
- Tokens tokens(routingKey);
-
- for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) {
- if (i->first.match(tokens)) {
- Binding::vector& qv(i->second);
- for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++, count++){
- msg.deliverTo((*j)->queue);
- if ((*j)->mgmtBinding != 0)
- (*j)->mgmtBinding->inc_msgMatched ();
- }
- }
- }
-
- if (mgmtExchange != 0)
+void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/)
+{
+ Binding::vector mb;
+ BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ PreRoute pr(msg, this);
{
- mgmtExchange->inc_msgReceives ();
- mgmtExchange->inc_byteReceives (msg.contentSize ());
- if (count == 0)
- {
- mgmtExchange->inc_msgDrops ();
- mgmtExchange->inc_byteDrops (msg.contentSize ());
- }
- else
- {
- mgmtExchange->inc_msgRoutes (count);
- mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
+ RWlock::ScopedRlock l(lock);
+ for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) {
+ if (match(i->first, routingKey)) {
+ Binding::vector& qv(i->second.bindingVector);
+ for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++){
+ b->push_back(*j);
+ }
+ }
}
}
+ doRoute(msg, b);
}
bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const)
{
+ RWlock::ScopedRlock l(lock);
if (routingKey && queue) {
- TopicPattern key(*routingKey);
+ string key(normalize(*routingKey));
return isBound(queue, key);
} else if (!routingKey && !queue) {
return bindings.size() > 0;
} else if (routingKey) {
for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) {
- if (i->first.match(*routingKey)) {
+ if (match(i->first, *routingKey))
return true;
}
- }
} else {
for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) {
- Binding::vector& qv(i->second);
+ Binding::vector& qv(i->second.bindingVector);
Binding::vector::iterator q;
for (q = qv.begin(); q != qv.end(); q++)
if ((*q)->queue == queue)
@@ -233,10 +333,11 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routing
}
}
return false;
+ return queue && routingKey;
}
TopicExchange::~TopicExchange() {}
const std::string TopicExchange::typeName("topic");
-
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h
index 2e107142b7..3bbf143889 100644
--- a/cpp/src/qpid/broker/TopicExchange.h
+++ b/cpp/src/qpid/broker/TopicExchange.h
@@ -23,77 +23,57 @@
#include <map>
#include <vector>
-#include "Exchange.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/sys/Monitor.h"
-#include "Queue.h"
+#include "qpid/broker/Queue.h"
namespace qpid {
namespace broker {
-/** A vector of string tokens */
-class Tokens : public std::vector<std::string> {
- public:
- Tokens() {};
- // Default copy, assign, dtor are sufficient.
-
- /** Tokenize s, provides automatic conversion of string to Tokens */
- Tokens(const std::string& s) { operator=(s); }
- /** Tokenizing assignment operator s */
- Tokens & operator=(const std::string& s);
-
- private:
- size_t hash;
-};
-
-
-/**
- * Tokens that have been normalized as a pattern and can be matched
- * with topic Tokens. Normalized meands all sequences of mixed * and
- * # are reduced to a series of * followed by at most one #.
- */
-class TopicPattern : public Tokens
-{
- public:
- TopicPattern() {}
- // Default copy, assign, dtor are sufficient.
- TopicPattern(const Tokens& tokens) { operator=(tokens); }
- TopicPattern(const std::string& str) { operator=(str); }
- TopicPattern& operator=(const Tokens&);
- TopicPattern& operator=(const std::string& str) { return operator=(Tokens(str)); }
-
- /** Match a topic */
- bool match(const std::string& topic) { return match(Tokens(topic)); }
- bool match(const Tokens& topic) const;
-
- private:
- void normalize();
-};
-
-class TopicExchange : public virtual Exchange{
- typedef std::map<TopicPattern, Binding::vector> BindingMap;
+class TopicExchange : public virtual Exchange {
+ struct BoundKey {
+ Binding::vector bindingVector;
+ FedBinding fedBinding;
+ };
+ typedef std::map<std::string, BoundKey> BindingMap;
BindingMap bindings;
qpid::sys::RWlock lock;
- bool isBound(Queue::shared_ptr queue, TopicPattern& pattern);
+ bool isBound(Queue::shared_ptr queue, const string& pattern);
+
public:
static const std::string typeName;
- TopicExchange(const string& name, management::Manageable* parent = 0);
- TopicExchange(const string& _name, bool _durable,
- const qpid::framing::FieldTable& _args, management::Manageable* parent = 0);
+ static QPID_BROKER_EXTERN bool match(const std::string& pattern, const std::string& topic);
+ static QPID_BROKER_EXTERN std::string normalize(const std::string& pattern);
+
+ QPID_BROKER_EXTERN TopicExchange(const string& name,
+ management::Manageable* parent = 0, Broker* broker = 0);
+ QPID_BROKER_EXTERN TopicExchange(const 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 string& routingKey, const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue,
+ const string& routingKey,
+ const qpid::framing::FieldTable* args);
virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args);
- virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args);
+ QPID_BROKER_EXTERN virtual void route(Deliverable& msg,
+ const string& routingKey,
+ const qpid::framing::FieldTable* args);
- virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args);
+ QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue,
+ const string* const routingKey,
+ const qpid::framing::FieldTable* const args);
- virtual ~TopicExchange();
+ QPID_BROKER_EXTERN virtual ~TopicExchange();
+ virtual bool supportsDynamicBinding() { return true; }
};
diff --git a/cpp/src/qpid/broker/TxAccept.cpp b/cpp/src/qpid/broker/TxAccept.cpp
index 82acf61cd1..928ac12c10 100644
--- a/cpp/src/qpid/broker/TxAccept.cpp
+++ b/cpp/src/qpid/broker/TxAccept.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "TxAccept.h"
+#include "qpid/broker/TxAccept.h"
#include "qpid/log/Statement.h"
using std::bind1st;
@@ -26,19 +26,56 @@ using std::bind2nd;
using std::mem_fun_ref;
using namespace qpid::broker;
using qpid::framing::SequenceSet;
+using qpid::framing::SequenceNumber;
-TxAccept::TxAccept(SequenceSet& _acked, std::list<DeliveryRecord>& _unacked) :
- acked(_acked), unacked(_unacked) {}
+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{
- //dequeue messages from their respective queues:
- for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) {
- if (i->coveredBy(&acked)) {
- i->dequeue(ctxt);
- }
- }
+ ops.prepare(ctxt);
return true;
}catch(const std::exception& e){
QPID_LOG(error, "Failed to prepare: " << e.what());
@@ -51,11 +88,13 @@ bool TxAccept::prepare(TransactionContext* ctxt) throw()
void TxAccept::commit() throw()
{
- for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) {
- if (i->coveredBy(&acked)) i->setEnded();
+ 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)");
}
-
- unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant));
}
void TxAccept::rollback() throw() {}
diff --git a/cpp/src/qpid/broker/TxAccept.h b/cpp/src/qpid/broker/TxAccept.h
index 9548c50c2a..314a150176 100644
--- a/cpp/src/qpid/broker/TxAccept.h
+++ b/cpp/src/qpid/broker/TxAccept.h
@@ -25,8 +25,8 @@
#include <functional>
#include <list>
#include "qpid/framing/SequenceSet.h"
-#include "DeliveryRecord.h"
-#include "TxOp.h"
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/TxOp.h"
namespace qpid {
namespace broker {
@@ -34,9 +34,31 @@ namespace qpid {
* Defines the transactional behaviour for accepts received by
* a transactional channel.
*/
- class TxAccept : public TxOp{
- framing::SequenceSet& acked;
- std::list<DeliveryRecord>& unacked;
+ 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:
/**
@@ -44,11 +66,15 @@ namespace qpid {
* acks received
* @param unacked the record of delivered messages
*/
- TxAccept(framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked);
+ 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; }
};
}
}
diff --git a/cpp/src/qpid/broker/TxBuffer.cpp b/cpp/src/qpid/broker/TxBuffer.cpp
index 8fe2c17bf0..b509778e89 100644
--- a/cpp/src/qpid/broker/TxBuffer.cpp
+++ b/cpp/src/qpid/broker/TxBuffer.cpp
@@ -18,10 +18,11 @@
* under the License.
*
*/
-#include "TxBuffer.h"
+#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;
@@ -73,3 +74,7 @@ bool TxBuffer::commitLocal(TransactionalStore* const store)
}
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/cpp/src/qpid/broker/TxBuffer.h b/cpp/src/qpid/broker/TxBuffer.h
index 361c47e92c..d49c8ba16a 100644
--- a/cpp/src/qpid/broker/TxBuffer.h
+++ b/cpp/src/qpid/broker/TxBuffer.h
@@ -24,8 +24,9 @@
#include <algorithm>
#include <functional>
#include <vector>
-#include "TransactionalStore.h"
-#include "TxOp.h"
+#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
@@ -68,7 +69,7 @@ namespace qpid {
/**
* Adds an operation to the transaction.
*/
- void enlist(TxOp::shared_ptr op);
+ QPID_BROKER_EXTERN void enlist(TxOp::shared_ptr op);
/**
* Requests that all ops are prepared. This should
@@ -81,7 +82,7 @@ namespace qpid {
* @returns true if all the operations prepared
* successfully, false if not.
*/
- bool prepare(TransactionContext* const ctxt);
+ QPID_BROKER_EXTERN bool prepare(TransactionContext* const ctxt);
/**
* Signals that the ops all prepared successfully and can
@@ -91,7 +92,7 @@ namespace qpid {
* Should only be called after a call to prepare() returns
* true.
*/
- void commit();
+ QPID_BROKER_EXTERN void commit();
/**
* Signals that all ops can be rolled back.
@@ -100,13 +101,16 @@ namespace qpid {
* returns true (2pc) or instead of a prepare call
* ('server-local')
*/
- void rollback();
+ QPID_BROKER_EXTERN void rollback();
/**
* Helper method for managing the process of server local
* commit
*/
- bool commitLocal(TransactionalStore* const store);
+ QPID_BROKER_EXTERN bool commitLocal(TransactionalStore* const store);
+
+ // Used by cluster to replicate transaction status.
+ void accept(TxOpConstVisitor& v) const;
};
}
}
diff --git a/cpp/src/qpid/broker/TxOp.h b/cpp/src/qpid/broker/TxOp.h
index e687c437cc..a8fa1c2621 100644
--- a/cpp/src/qpid/broker/TxOp.h
+++ b/cpp/src/qpid/broker/TxOp.h
@@ -21,11 +21,13 @@
#ifndef _TxOp_
#define _TxOp_
-#include "TransactionalStore.h"
+#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;
@@ -34,9 +36,11 @@ namespace qpid {
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/cpp/src/qpid/broker/TxOpVisitor.h b/cpp/src/qpid/broker/TxOpVisitor.h
new file mode 100644
index 0000000000..ceb894896e
--- /dev/null
+++ b/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/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp
index dcee00e803..4b083033ea 100644
--- a/cpp/src/qpid/broker/TxPublish.cpp
+++ b/cpp/src/qpid/broker/TxPublish.cpp
@@ -19,16 +19,21 @@
*
*/
#include "qpid/log/Statement.h"
-#include "TxPublish.h"
+#include "qpid/broker/TxPublish.h"
using boost::intrusive_ptr;
using namespace qpid::broker;
TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {}
-bool TxPublish::prepare(TransactionContext* ctxt) throw(){
+bool TxPublish::prepare(TransactionContext* ctxt) throw()
+{
try{
- for_each(queues.begin(), queues.end(), Prepare(ctxt, msg));
+ 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());
@@ -38,14 +43,33 @@ bool TxPublish::prepare(TransactionContext* ctxt) throw(){
return false;
}
-void TxPublish::commit() throw(){
- for_each(queues.begin(), queues.end(), Commit(msg));
+void TxPublish::commit() throw()
+{
+ try {
+ for_each(prepared.begin(), prepared.end(), Commit(msg));
+ if (msg->checkContentReleasable()) {
+ msg->releaseContent();
+ }
+ } 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(){
+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(Queue::shared_ptr& queue){
+void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){
if (!queue->isLocal(msg)) {
queues.push_back(queue);
delivered = true;
@@ -54,26 +78,30 @@ void TxPublish::deliverTo(Queue::shared_ptr& queue){
}
}
-TxPublish::Prepare::Prepare(TransactionContext* _ctxt, intrusive_ptr<Message>& _msg)
- : ctxt(_ctxt), msg(_msg){}
-
-void TxPublish::Prepare::operator()(Queue::shared_ptr& queue){
+void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr<Queue> queue)
+{
if (!queue->enqueue(ctxt, msg)){
/**
- * if not store then mark message for ack and deleivery once
- * commit happens, as async IO will never set it when no store
- * exists
- */
+ * if not store then mark message for ack and deleivery once
+ * commit happens, as async IO will never set it when no store
+ * exists
+ */
msg->enqueueComplete();
}
}
TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){}
-void TxPublish::Commit::operator()(Queue::shared_ptr& queue){
+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/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h
index d2590debfb..b6ab9767ab 100644
--- a/cpp/src/qpid/broker/TxPublish.h
+++ b/cpp/src/qpid/broker/TxPublish.h
@@ -21,11 +21,12 @@
#ifndef _TxPublish_
#define _TxPublish_
-#include "Queue.h"
-#include "Deliverable.h"
-#include "Message.h"
-#include "MessageStore.h"
-#include "TxOp.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/Queue.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>
@@ -46,37 +47,43 @@ namespace qpid {
* dispatch or to be added to the in-memory queue.
*/
class TxPublish : public TxOp, public Deliverable{
- class Prepare{
- TransactionContext* ctxt;
- boost::intrusive_ptr<Message>& msg;
- public:
- Prepare(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg);
- void operator()(Queue::shared_ptr& queue);
- };
class Commit{
boost::intrusive_ptr<Message>& msg;
public:
Commit(boost::intrusive_ptr<Message>& msg);
- void operator()(Queue::shared_ptr& queue);
+ 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<Queue::shared_ptr> queues;
+ std::list<Queue::shared_ptr> prepared;
+
+ void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>);
public:
- TxPublish(boost::intrusive_ptr<Message> msg);
- virtual bool prepare(TransactionContext* ctxt) throw();
- virtual void commit() throw();
- virtual void rollback() throw();
+ 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; };
- virtual void deliverTo(Queue::shared_ptr& queue);
+ 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();
- uint64_t contentSize();
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+ const std::list<Queue::shared_ptr> getQueues() const { return queues; }
};
}
}
diff --git a/cpp/src/qpid/broker/Vhost.cpp b/cpp/src/qpid/broker/Vhost.cpp
index 23203ec13e..df37cba255 100644
--- a/cpp/src/qpid/broker/Vhost.cpp
+++ b/cpp/src/qpid/broker/Vhost.cpp
@@ -17,23 +17,33 @@
// under the License.
//
-#include "Vhost.h"
-#include "qpid/agent/ManagementAgent.h"
+#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;
-Vhost::Vhost (management::Manageable* parentBroker) : mgmtObject(0)
+namespace qpid { namespace management {
+class Manageable;
+}}
+
+Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmtObject(0)
{
- if (parentBroker != 0)
+ if (parentBroker != 0 && broker != 0)
{
- ManagementAgent* agent = ManagementAgent::Singleton::getInstance();
+ ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtObject = new management::Vhost (agent, this, parentBroker, "/");
- agent->addObject (mgmtObject, 3, 1);
+ mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/");
+ agent->addObject (mgmtObject, 0x1000000000000003LL);
}
}
}
+void Vhost::setFederationTag(const std::string& tag)
+{
+ mgmtObject->set_federationTag(tag);
+}
diff --git a/cpp/src/qpid/broker/Vhost.h b/cpp/src/qpid/broker/Vhost.h
index e56cc61272..9554d641c2 100644
--- a/cpp/src/qpid/broker/Vhost.h
+++ b/cpp/src/qpid/broker/Vhost.h
@@ -21,29 +21,28 @@
//
#include "qpid/management/Manageable.h"
-#include "qpid/management/Vhost.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:
- management::Vhost* mgmtObject;
+ qmf::org::apache::qpid::broker::Vhost* mgmtObject;
public:
typedef boost::shared_ptr<Vhost> shared_ptr;
- Vhost (management::Manageable* parentBroker);
+ Vhost (management::Manageable* parentBroker, Broker* broker = 0);
management::ManagementObject* GetManagementObject (void) const
{ return mgmtObject; }
-
- management::Manageable::status_t ManagementMethod (uint32_t, management::Args&)
- { return management::Manageable::STATUS_OK; }
+ void setFederationTag(const std::string& tag);
};
}}
diff --git a/cpp/src/qpid/broker/XmlExchange.cpp b/cpp/src/qpid/broker/XmlExchange.cpp
deleted file mode 100644
index cb0f9a9606..0000000000
--- a/cpp/src/qpid/broker/XmlExchange.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "config.h"
-#include "XmlExchange.h"
-
-#include "DeliverableMessage.h"
-
-#include "qpid/log/Statement.h"
-#include "qpid/framing/FieldTable.h"
-#include "qpid/framing/FieldValue.h"
-#include "qpid/framing/reply_exceptions.h"
-
-#include <xercesc/framework/MemBufInputSource.hpp>
-
-#include <xqilla/context/ItemFactory.hpp>
-#include <xqilla/xqilla-simple.hpp>
-
-#include <iostream>
-#include <sstream>
-
-using namespace qpid::framing;
-using namespace qpid::sys;
-using qpid::management::Manageable;
-
-namespace qpid {
-namespace broker {
-
-XmlExchange::XmlExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent)
-{
- if (mgmtExchange != 0)
- mgmtExchange->set_type (typeName);
-}
-
-XmlExchange::XmlExchange(const std::string& _name, bool _durable,
- const FieldTable& _args, Manageable* _parent) :
- Exchange(_name, _durable, _args, _parent)
-{
- if (mgmtExchange != 0)
- mgmtExchange->set_type (typeName);
-}
-
-/*
- * Use the name of the query as the binding key.
- *
- * The first time a given name is used in a binding, the query body
- * must be provided.After that, no query body should be present.
- *
- * To modify an installed query, the user must first unbind the
- * existing query, then replace it by binding again with the same
- * name.
- *
- */
-
- // #### TODO: The Binding should take the query text
- // #### only. Consider encapsulating the entire block, including
- // #### the if condition.
-
-
-bool XmlExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* bindingArguments)
-{
- string queryText = bindingArguments->getString("xquery");
-
- try {
- RWlock::ScopedWlock l(lock);
- XmlBinding::vector& bindings(bindingsMap[routingKey]);
- XmlBinding::vector::iterator i;
-
- for (i = bindings.begin(); i != bindings.end(); i++)
- if ((*i)->queue == queue)
- break;
-
- if (i == bindings.end()) {
-
- Query query(xqilla.parse(X(queryText.c_str())));
- XmlBinding::shared_ptr binding(new XmlBinding (routingKey, queue, this, query));
- XmlBinding::vector bindings(1, binding);
- bindingsMap[routingKey] = bindings;
- QPID_LOG(trace, "Bound successfully with query: " << queryText );
-
- if (mgmtExchange != 0) {
- mgmtExchange->inc_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->inc_bindingCount();
- }
- return true;
- } else{
- return false;
- }
- }
- catch (XQException& e) {
- throw InternalErrorException(QPID_MSG("Could not parse xquery:"+ queryText));
- }
- catch (...) {
- throw InternalErrorException(QPID_MSG("Unexpected error - Could not parse xquery:"+ queryText));
- }
-}
-
-bool XmlExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/)
-{
- RWlock::ScopedWlock l(lock);
- XmlBinding::vector& bindings(bindingsMap[routingKey]);
- XmlBinding::vector::iterator i;
-
- for (i = bindings.begin(); i != bindings.end(); i++)
- if ((*i)->queue == queue)
- break;
-
- if (i < bindings.end()) {
- bindings.erase(i);
- if (bindings.empty()) {
- bindingsMap.erase(routingKey);
- }
- if (mgmtExchange != 0) {
- mgmtExchange->dec_bindingCount();
- ((management::Queue*) queue->GetManagementObject())->dec_bindingCount();
- }
- return true;
- } else {
- return false;
- }
-}
-
-bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args)
-{
- // ### TODO: Need istream for frameset
- // Hack alert - the following code does not work for really large messages
-
- string msgContent;
-
- try {
- msg.getMessage().getFrames().getContent(msgContent);
-
- QPID_LOG(trace, "matches: query is [" << UTF8(query->getQueryText()) << "]");
- QPID_LOG(trace, "matches: message content is [" << msgContent << "]");
-
- boost::scoped_ptr<DynamicContext> context(query->createDynamicContext());
- if (!context.get()) {
- throw InternalErrorException(QPID_MSG("Query context looks munged ..."));
- }
-
- XERCES_CPP_NAMESPACE::MemBufInputSource xml((XMLByte*) msgContent.c_str(), msgContent.length(), "input" );
- Sequence seq(context->parseDocument(xml));
-
- if (args) {
- FieldTable::ValueMap::const_iterator v = args->begin();
- for(; v != args->end(); ++v) {
- // ### TODO: Do types properly
- if (v->second->convertsTo<std::string>()) {
- QPID_LOG(trace, "XmlExchange, external variable: " << v->first << " = " << v->second->getData().getString().c_str());
- Item::Ptr value = context->getItemFactory()->createString(X(v->second->getData().getString().c_str()), context.get());
- context->setExternalVariable(X(v->first.c_str()), value);
- }
- }
- }
-
- if(!seq.isEmpty() && seq.first()->isNode()) {
- context->setContextItem(seq.first());
- context->setContextPosition(1);
- context->setContextSize(1);
- }
- Result result = query->execute(context.get());
- return result->getEffectiveBooleanValue(context.get(), 0);
- }
- catch (XQException& e) {
- QPID_LOG(warning, "Could not parse XML content (or message headers):" << msgContent);
- return 0;
- }
- catch (...) {
- QPID_LOG(warning, "Unexpected error routing message: " << msgContent);
- return 0;
- }
- return 0;
-}
-
-void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args)
-{
- try {
- RWlock::ScopedRlock l(lock);
- XmlBinding::vector& bindings(bindingsMap[routingKey]);
- XmlBinding::vector::iterator i;
- int count(0);
-
- for (i = bindings.begin(); i != bindings.end(); i++) {
-
- if ((*i)->xquery && matches((*i)->xquery, msg, args)) { // Overly defensive? There should always be a query ...
- msg.deliverTo((*i)->queue);
- count++;
- QPID_LOG(trace, "Delivered to queue" );
-
- if ((*i)->mgmtBinding != 0)
- (*i)->mgmtBinding->inc_msgMatched ();
- }
-
- if(!count){
- QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey);
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgDrops ();
- mgmtExchange->inc_byteDrops (msg.contentSize ());
- }
- }
- else {
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgRoutes (count);
- mgmtExchange->inc_byteRoutes (count * msg.contentSize ());
- }
- }
-
- if (mgmtExchange != 0) {
- mgmtExchange->inc_msgReceives ();
- mgmtExchange->inc_byteReceives (msg.contentSize ());
- }
- }
- }
- catch (...) {
- QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey);
- }
-
-
-}
-
-
-bool XmlExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const)
-{
- XmlBinding::vector::iterator j;
-
- if (routingKey) {
- XmlBindingsMap::iterator i = bindingsMap.find(*routingKey);
-
- if (i == bindingsMap.end())
- return false;
- if (!queue)
- return true;
- for (j = i->second.begin(); j != i->second.end(); j++)
- if ((*j)->queue == queue)
- return true;
- } 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++)
- for (j = i->second.begin(); j != i->second.end(); j++)
- if ((*j)->queue == queue)
- return true;
- return false;
- }
-
- return false;
-}
-
-
-XmlExchange::~XmlExchange()
-{
- bindingsMap.clear();
-}
-
-const std::string XmlExchange::typeName("xml");
-
-}
-}
diff --git a/cpp/src/qpid/broker/Prefetch.h b/cpp/src/qpid/broker/posix/BrokerDefaults.cpp
index 8eb27a3e21..9e463fa32d 100644
--- a/cpp/src/qpid/broker/Prefetch.h
+++ b/cpp/src/qpid/broker/posix/BrokerDefaults.cpp
@@ -18,25 +18,23 @@
* under the License.
*
*/
-#ifndef _Prefetch_
-#define _Prefetch_
-#include "qpid/framing/amqp_types.h"
+#include "qpid/broker/Broker.h"
+#include <stdlib.h>
namespace qpid {
- namespace broker {
- /**
- * Count and total size of asynchronously delivered
- * (i.e. pushed) messages that have acks outstanding.
- */
- struct Prefetch{
- uint32_t size;
- uint16_t count;
+namespace broker {
- void reset() { size = 0; count = 0; }
- };
- }
-}
+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;
+}
-#endif
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/DeliveryToken.h b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
index 8bdf5e6359..b6862f0418 100644
--- a/cpp/src/qpid/broker/DeliveryToken.h
+++ b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
@@ -18,28 +18,24 @@
* under the License.
*
*/
-#ifndef _DeliveryToken_
-#define _DeliveryToken_
-#include <boost/shared_ptr.hpp>
+#include "qpid/broker/Broker.h"
+#include <stdlib.h>
namespace qpid {
namespace broker {
- /**
- * A DeliveryToken allows the delivery of a message to be
- * associated with whatever mechanism caused it to be
- * delivered. (i.e. its a form of Memento).
- */
- class DeliveryToken
- {
- public:
- typedef boost::shared_ptr<DeliveryToken> shared_ptr;
+const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("\\TEMP");
+const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("\\QPIDD.DATA");
- virtual ~DeliveryToken(){}
- };
+std::string
+Broker::Options::getHome() {
+ std::string home;
+ char home_c[MAX_PATH+1];
+ size_t unused;
+ if (0 == getenv_s (&unused, home_c, sizeof(home_c), "HOME"))
+ home += home_c;
+ return home;
+}
-}}
-
-
-#endif
+}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
new file mode 100644
index 0000000000..212d7c4db4
--- /dev/null
+++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.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.
+ *
+ */
+
+// 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*/)
+{
+ return;
+}
+
+void SaslAuthenticator::fini(void)
+{
+ return;
+}
+
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c)
+{
+ 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);
+ int error = 0;
+ if (!LogonUser(uid.c_str(), ".", 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/cpp/src/qpid/client/Bounds.cpp b/cpp/src/qpid/client/Bounds.cpp
index aac18022bc..abb983a62e 100644
--- a/cpp/src/qpid/client/Bounds.cpp
+++ b/cpp/src/qpid/client/Bounds.cpp
@@ -1,4 +1,24 @@
-#include "Bounds.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/Bounds.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/Waitable.h"
@@ -28,8 +48,7 @@ void Bounds::reduce(size_t size) {
if (current == 0) return;
current -= std::min(size, current);
if (current < max && lock.hasWaiters()) {
- assert(lock.hasWaiters() == 1);
- lock.notify();
+ lock.notifyAll();
}
}
diff --git a/cpp/src/qpid/client/AckPolicy.cpp b/cpp/src/qpid/client/Completion.cpp
index 7956ebad0f..a97c8c3534 100644
--- a/cpp/src/qpid/client/AckPolicy.cpp
+++ b/cpp/src/qpid/client/Completion.cpp
@@ -18,33 +18,23 @@
* under the License.
*
*/
-#include "AckPolicy.h"
+
+#include "qpid/client/Completion.h"
+#include "qpid/client/CompletionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
namespace qpid {
namespace client {
-AckPolicy::AckPolicy(size_t n) : interval(n), count(n) {}
+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 AckPolicy::ack(const Message& msg, AsyncSession session)
-{
- accepted.add(msg.getId());
- if (interval && --count==0) {
- session.markCompleted(msg.getId(), false, true);
- session.messageAccept(accepted);
- accepted.clear();
- count = interval;
- } else {
- session.markCompleted(msg.getId(), false, false);
- }
-}
-void AckPolicy::ackOutstanding(AsyncSession session)
-{
- if (!accepted.empty()) {
- session.messageAccept(accepted);
- accepted.clear();
- session.sendCompletion();
- }
-}
+void Completion::wait() { impl->wait(); }
+bool Completion::isComplete() { return impl->isComplete(); }
+std::string Completion::getResult() { return impl->getResult(); }
}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/Completion.h b/cpp/src/qpid/client/Completion.h
deleted file mode 100644
index c4979d7934..0000000000
--- a/cpp/src/qpid/client/Completion.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#ifndef _Completion_
-#define _Completion_
-
-#include <boost/shared_ptr.hpp>
-#include "Future.h"
-#include "SessionImpl.h"
-
-namespace qpid {
-namespace client {
-
-/**
- * Asynchronous commands that do not return a result will return a
- * Completion. You can use the completion to wait for that specific
- * command to complete.
- *
- *@see TypedResult
- *
- *\ingroup clientapi
- */
-class Completion
-{
-protected:
- Future future;
- shared_ptr<SessionImpl> session;
-
-public:
- ///@internal
- Completion() {}
-
- ///@internal
- Completion(Future f, shared_ptr<SessionImpl> s) : future(f), session(s) {}
-
- /** Wait for the asynchronous command that returned this
- *Completion to complete.
- *
- *@exception If the command returns an error, get() throws an exception.
- */
- void wait()
- {
- future.wait(*session);
- }
-
- bool isComplete() {
- return future.isComplete(*session);
- }
-};
-
-}}
-
-#endif
diff --git a/cpp/src/qpid/client/CompletionImpl.h b/cpp/src/qpid/client/CompletionImpl.h
new file mode 100644
index 0000000000..f180708316
--- /dev/null
+++ b/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/cpp/src/qpid/client/Connection.cpp b/cpp/src/qpid/client/Connection.cpp
index 6572794516..32a01b2c40 100644
--- a/cpp/src/qpid/client/Connection.cpp
+++ b/cpp/src/qpid/client/Connection.cpp
@@ -18,15 +18,16 @@
* under the License.
*
*/
-#include "Connection.h"
-#include "ConnectionSettings.h"
-#include "Message.h"
-#include "SessionImpl.h"
-#include "SessionBase_0_10Access.h"
+#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/shared_ptr.h"
#include "qpid/framing/AMQP_HighestVersion.h"
#include <algorithm>
@@ -35,6 +36,7 @@
#include <functional>
#include <boost/format.hpp>
#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
using namespace qpid::framing;
using namespace qpid::sys;
@@ -43,9 +45,45 @@ using namespace qpid::sys;
namespace qpid {
namespace client {
-Connection::Connection() : channelIdCounter(0), version(framing::highestProtocolVersion) {}
+Connection::Connection() : version(framing::highestProtocolVersion) {}
-Connection::~Connection(){ }
+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 TcpAddress* tcp = i->get<TcpAddress>();
+ i++;
+ if (tcp) {
+ try {
+ ConnectionSettings cs(settings);
+ cs.host = tcp->host;
+ cs.port = tcp->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,
@@ -67,39 +105,55 @@ 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 = shared_ptr<ConnectionImpl>(new ConnectionImpl(version, settings));
- impl->open(settings.host, settings.port);
- max_frame_size = impl->getNegotiatedSettings().maxFrameSize;
+ impl = boost::shared_ptr<ConnectionImpl>(new ConnectionImpl(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) {
+Session Connection::newSession(const std::string& name, uint32_t timeout) {
if (!isOpen())
throw Exception(QPID_MSG("Connection has not yet been opened"));
- shared_ptr<SessionImpl> simpl(
- new SessionImpl(name, impl, ++channelIdCounter, max_frame_size));
- impl->addSession(simpl);
- simpl->open(0);
Session s;
- SessionBase_0_10Access(s).set(simpl);
+ 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."));
-
- session.impl->setChannel(++channelIdCounter);
impl->addSession(session.impl);
session.impl->resume(impl);
}
void Connection::close() {
- impl->close();
+ if ( impl )
+ impl->close();
+}
+
+std::vector<Url> Connection::getInitialBrokers() {
+ return impl ? impl->getInitialBrokers() : std::vector<Url>();
}
}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/Connection.h b/cpp/src/qpid/client/Connection.h
deleted file mode 100644
index ee543e20d2..0000000000
--- a/cpp/src/qpid/client/Connection.h
+++ /dev/null
@@ -1,149 +0,0 @@
-#ifndef _client_Connection_
-#define _client_Connection_
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES 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 <string>
-#include "qpid/client/Session.h"
-
-namespace qpid {
-namespace client {
-
-class ConnectionSettings;
-
-/**
- * Represents a connection to an AMQP broker. All communication is
- * initiated by establishing a connection, then creating one or more
- * Session objecst using the connection. @see newSession()
- *
- * \ingroup clientapi
- */
-class Connection
-{
- framing::ChannelId channelIdCounter;
- framing::ProtocolVersion version;
- uint16_t max_frame_size;
-
- protected:
- boost::shared_ptr<ConnectionImpl> impl;
-
- public:
- /**
- * Creates a connection object, but does not open the connection.
- * @see open()
- */
- Connection();
- ~Connection();
-
- /**
- * Opens a connection to a broker.
- *
- * @param host the host on which the broker is running.
- *
- * @param port the port on the which the broker is listening.
- *
- * @param uid the userid to connect with.
- *
- * @param pwd the password to connect with (currently SASL
- * PLAIN is the only authentication method supported so this
- * is sent in clear text).
- *
- * @param virtualhost the AMQP virtual host to use (virtual
- * hosts, where implemented(!), provide namespace partitioning
- * within a single broker).
- */
- void open(const std::string& host, int port = 5672,
- const std::string& uid = "guest",
- const std::string& pwd = "guest",
- const std::string& virtualhost = "/", uint16_t maxFrameSize=65535);
-
- /**
- * Opens a connection to a broker.
- *
- * @param the settings to use (host, port etc) @see ConnectionSettings
- */
- void open(const ConnectionSettings& settings);
-
- /**
- * Close the connection.
- *
- * Any further use of this connection (without reopening it) will
- * not succeed.
- */
- void close();
-
- /**
- * Create a new session on this connection. Sessions allow
- * multiple streams of work to be multiplexed over the same
- * connection. The returned Session provides functions to send
- * commands to the broker.
- *
- * Session functions are synchronous. In other words, a Session
- * function will send a command to the broker and does not return
- * until it receives the broker's response confirming the command
- * was executed.
- *
- * AsyncSession provides asynchronous versions of the same
- * functions. These functions send a command to the broker but do
- * not wait for a response.
- *
- * You can convert a Session s into an AsyncSession as follows:
- * @code
- * #include <qpid/client/AsyncSession.h>
- * AsyncSession as = async(s);
- * @endcode
- *
- * You can execute a single command asynchronously will a Session s
- * like ths:
- * @code
- * async(s).messageTransfer(...);
- * @endcode
- *
- * Using an AsyncSession is faster for sending large numbers of
- * commands, since each command is sent as soon as possible
- * without waiting for the previous command to be confirmed.
- *
- * However with AsyncSession you cannot assume that a command has
- * completed until you explicitly synchronize. The simplest way to
- * do this is to call Session::sync() or AsyncSession::sync().
- * Both of these functions wait for the broker to confirm all
- * commands issued so far on the session.
- *
- *@param name: A name to identify the session. @see qpid::SessionId
- * If the name is empty (the default) then a unique name will be
- * chosen using a Universally-unique identifier (UUID) algorithm.
- */
- Session newSession(const std::string& name=std::string());
-
- /**
- * Resume a suspended session. A session may be resumed
- * on a different connection to the one that created it.
- */
- void resume(Session& session);
-
- bool isOpen() const;
-};
-
-}} // namespace qpid::client
-
-
-#endif
diff --git a/cpp/src/qpid/client/FutureCompletion.h b/cpp/src/qpid/client/ConnectionAccess.h
index 4248ddeab8..b662fd5d8b 100644
--- a/cpp/src/qpid/client/FutureCompletion.h
+++ b/cpp/src/qpid/client/ConnectionAccess.h
@@ -1,3 +1,6 @@
+#ifndef QPID_CLIENT_CONNECTIONACCESS_H
+#define QPID_CLIENT_CONNECTIONACCESS_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,31 +22,20 @@
*
*/
-#ifndef _FutureCompletion_
-#define _FutureCompletion_
+#include "qpid/client/Connection.h"
-#include "qpid/framing/amqp_framing.h"
-#include "qpid/sys/Monitor.h"
+/**@file @internal Internal use only */
namespace qpid {
namespace client {
-///@internal
-class FutureCompletion
-{
-protected:
- mutable sys::Monitor lock;
- bool complete;
-
-public:
- FutureCompletion();
- virtual ~FutureCompletion(){}
- bool isComplete() const;
- void waitForCompletion() const;
- void completed();
-};
-}}
+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; }
+};
+
+}} // namespace qpid::client
-#endif
+#endif /*!QPID_CLIENT_CONNECTIONACCESS_H*/
diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp
index 22ebec76bf..8f1cc7b03f 100644
--- a/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,17 +19,28 @@
*
*/
-#include "ConnectionHandler.h"
+#include "qpid/client/ConnectionHandler.h"
-#include "qpid/log/Statement.h"
+#include "qpid/client/SaslFactory.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 boost;
+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");
@@ -40,24 +51,54 @@ 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;
}
-ConnectionHandler::ConnectionHandler(const ConnectionSettings& s, framing::ProtocolVersion& v)
- : StateManager(NOT_STARTED), ConnectionSettings(s), outHandler(*this), proxy(outHandler), errorCode(200), version(v)
-{
+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::ConnectionHandler(const ConnectionSettings& s, ProtocolVersion& v)
+ : StateManager(NOT_STARTED), ConnectionSettings(s), outHandler(*this), 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");
+ throw Exception("Received frame on closed connection");
}
+ if (rcvTimeoutTask) {
+ // Received frame on connection so delay timeout
+ rcvTimeoutTask->restart();
+ }
AMQBody* body = frame.getBody();
try {
@@ -67,26 +108,27 @@ void ConnectionHandler::incoming(AMQFrame& frame)
in(frame);
break;
case CLOSING:
- QPID_LOG(warning, "Ignoring frame while closing connection: " << frame);
+ 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());
+ QPID_LOG(warning, "Closing connection due to " << e.what());
setState(CLOSING);
- proxy.close(501, e.what());
- if (onError) onError(501, e.what());
+ errorCode = CLOSE_CODE_FRAMING_ERROR;
+ errorText = e.what();
+ proxy.close(501, e.what());
}
}
void ConnectionHandler::outgoing(AMQFrame& frame)
{
- if (getState() == OPEN)
+ if (getState() == OPEN)
out(frame);
else
- throw Exception(errorText.empty() ? "Connection is not open." : errorText);
+ throw TransportFailure(errorText.empty() ? "Connection is not open." : errorText);
}
void ConnectionHandler::waitForOpen()
@@ -105,14 +147,29 @@ void ConnectionHandler::close()
fail("Connection closed before it was established");
break;
case OPEN:
- setState(CLOSING);
- proxy.close(200, OK);
- waitFor(CLOSED);
+ if (setState(CLOSING, OPEN)) {
+ proxy.close(200, OK);
+ 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 for CLOSING, CLOSED, FAILED or NOT_STARTED
+
+ // 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) {
@@ -122,45 +179,92 @@ void ConnectionHandler::checkState(STATES s, const std::string& msg)
void ConnectionHandler::fail(const std::string& message)
{
- errorCode = 502;
+ errorCode = CLOSE_CODE_FRAMING_ERROR;
errorText = message;
QPID_LOG(warning, message);
setState(FAILED);
}
-void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& /*mechanisms*/, const Array& /*locales*/)
+namespace {
+std::string SPACE(" ");
+}
+
+void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& mechanisms, const Array& /*locales*/)
{
checkState(NOT_STARTED, INVALID_STATE_START);
setState(NEGOTIATING);
- //TODO: verify that desired mechanism and locale are supported
- string response = ((char)0) + username + ((char)0) + password;
- proxy.startOk(properties, mechanism, response, locale);
+ sasl = SaslFactory::getInstance().create(*this);
+
+ std::string mechlist;
+ bool chosenMechanismSupported = mechanism.empty();
+ for (Array::const_iterator i = mechanisms.begin(); i != mechanisms.end(); ++i) {
+ if (!mechanism.empty() && mechanism == (*i)->get<std::string>()) {
+ chosenMechanismSupported = true;
+ mechlist = (*i)->get<std::string>() + SPACE + mechlist;
+ } else {
+ if (i != mechanisms.begin()) mechlist += SPACE;
+ mechlist += (*i)->get<std::string>();
+ }
+ }
+
+ if (!chosenMechanismSupported) {
+ fail("Selected mechanism not supported: " + mechanism);
+ }
+
+ if (sasl.get()) {
+ string response = sasl->start(mechanism.empty() ? mechlist : mechanism,
+ getSSF ? getSSF() : 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*/)
+void ConnectionHandler::secure(const std::string& challenge)
{
- throw NotImplementedException("Challenge-response cycle not yet implemented in client");
+ 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*/)
+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);
- //TODO: implement heartbeats and check desired value is in valid range
+ // 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 framing::Array& /*knownHosts*/)
+void ConnectionHandler::openOk ( const Array& knownBrokers )
{
checkState(OPENING, INVALID_STATE_OPEN_OK);
- //TODO: store knownHosts for reconnection etc
+ 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");
@@ -169,7 +273,7 @@ void ConnectionHandler::redirect(const std::string& /*host*/, const Array& /*kno
void ConnectionHandler::close(uint16_t replyCode, const std::string& replyText)
{
proxy.closeOk();
- errorCode = replyCode;
+ errorCode = convert(replyCode);
errorText = replyText;
setState(CLOSED);
QPID_LOG(warning, "Broker closed connection: " << replyCode << ", " << replyText);
@@ -181,7 +285,9 @@ void ConnectionHandler::close(uint16_t replyCode, const std::string& replyText)
void ConnectionHandler::closeOk()
{
checkState(CLOSING, INVALID_STATE_CLOSE_OK);
- if (onClose) {
+ if (onError && errorCode != CLOSE_CODE_NORMAL) {
+ onError(errorCode, errorText);
+ } else if (onClose) {
onClose();
}
setState(CLOSED);
@@ -195,5 +301,17 @@ bool ConnectionHandler::isOpen() const
bool ConnectionHandler::isClosed() const
{
int s = getState();
- return s == CLOSING || s == CLOSED || s == FAILED;
+ 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/cpp/src/qpid/client/ConnectionHandler.h b/cpp/src/qpid/client/ConnectionHandler.h
index f8bd5e5d49..ed1e385dcf 100644
--- a/cpp/src/qpid/client/ConnectionHandler.h
+++ b/cpp/src/qpid/client/ConnectionHandler.h
@@ -21,17 +21,23 @@
#ifndef _ConnectionHandler_
#define _ConnectionHandler_
-#include "ChainableFrameHandler.h"
-#include "ConnectionSettings.h"
-#include "StateManager.h"
+#include "qpid/client/ChainableFrameHandler.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/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 client {
@@ -44,7 +50,7 @@ class ConnectionHandler : private StateManager,
{
typedef framing::AMQP_ClientOperations::ConnectionHandler ConnectionOperations;
enum STATES {NOT_STARTED, NEGOTIATING, OPENING, OPEN, CLOSING, CLOSED, FAILED};
- std::set<int> ESTABLISHED;
+ std::set<int> ESTABLISHED, FINISHED;
class Adapter : public framing::FrameHandler
{
@@ -56,12 +62,16 @@ class ConnectionHandler : private StateManager,
Adapter outHandler;
framing::AMQP_ServerProxy::Connection proxy;
- uint16_t errorCode;
+ 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);
@@ -79,11 +89,13 @@ class ConnectionHandler : private StateManager,
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<void(uint16_t, const std::string&)> ErrorListener;
+ typedef boost::function<unsigned int()> GetConnSSF;
ConnectionHandler(const ConnectionSettings&, framing::ProtocolVersion&);
@@ -99,9 +111,19 @@ public:
// 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; }
+ GetConnSSF getSSF; /** query the connection for its security strength factor */
};
}}
diff --git a/cpp/src/qpid/client/ConnectionImpl.cpp b/cpp/src/qpid/client/ConnectionImpl.cpp
index 5e8596cacb..cede7f7310 100644
--- a/cpp/src/qpid/client/ConnectionImpl.cpp
+++ b/cpp/src/qpid/client/ConnectionImpl.cpp
@@ -18,55 +18,97 @@
* under the License.
*
*/
-#include "ConnectionImpl.h"
-#include "Connector.h"
-#include "ConnectionSettings.h"
-#include "SessionImpl.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Connector.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/SessionImpl.h"
#include "qpid/log/Statement.h"
-#include "qpid/framing/constants.h"
+#include "qpid/Url.h"
+#include "qpid/framing/enum.h"
#include "qpid/framing/reply_exceptions.h"
#include <boost/bind.hpp>
#include <boost/format.hpp>
-using namespace qpid::client;
+#include <limits>
+
+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
+// Get timer singleton
+Timer& theTimer() {
+ static Mutex timerInitLock;
+ ScopedLock<Mutex> l(timerInitLock);
+
+ static qpid::sys::Timer t;
+ return t;
+}
+
+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),
+ timeout(t)
+ {}
+};
+
ConnectionImpl::ConnectionImpl(framing::ProtocolVersion v, const ConnectionSettings& settings)
: Bounds(settings.maxFrameSize * settings.bounds),
handler(settings, v),
- connector(new Connector(v, settings, this)),
- version(v)
+ version(v),
+ nextChannel(1)
{
- QPID_LOG(debug, "ConnectionImpl created for " << version);
+ QPID_LOG(debug, "ConnectionImpl created for " << version.toString());
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,
- NORMAL, std::string());
- connector->setInputHandler(&handler);
- connector->setShutdownHandler(this);
-
+ CLOSE_CODE_NORMAL, std::string());
//only set error handler once open
handler.onError = boost::bind(&ConnectionImpl::closed, this, _1, _2);
+ handler.getSSF = boost::bind(&Connector::getSSF, boost::ref(connector));
}
+const uint16_t ConnectionImpl::NEXT_CHANNEL = std::numeric_limits<uint16_t>::max();
+
ConnectionImpl::~ConnectionImpl() {
// Important to close the connector first, to ensure the
// connector thread does not call on us while the destructor
// is running.
- connector->close();
+ if (connector) connector->close();
}
-void ConnectionImpl::addSession(const boost::shared_ptr<SessionImpl>& session)
+void ConnectionImpl::addSession(const boost::shared_ptr<SessionImpl>& session, uint16_t channel)
{
Mutex::ScopedLock l(lock);
- boost::weak_ptr<SessionImpl>& s = sessions[session->getChannel()];
- if (s.lock()) throw SessionBusyException();
- s = session;
+ 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
+ }
+
}
void ConnectionImpl::handle(framing::AMQFrame& frame)
@@ -81,9 +123,11 @@ void ConnectionImpl::incoming(framing::AMQFrame& frame)
Mutex::ScopedLock l(lock);
s = sessions[frame.getChannel()].lock();
}
- if (!s)
- throw NotAttachedException(QPID_MSG("Invalid channel: " << frame.getChannel()));
- s->in(frame);
+ if (!s) {
+ QPID_LOG(info, "Dropping frame received on invalid channel: " << frame);
+ } else {
+ s->in(frame);
+ }
}
bool ConnectionImpl::isOpen() const
@@ -92,57 +136,121 @@ bool ConnectionImpl::isOpen() const
}
-void ConnectionImpl::open(const std::string& host, int port)
+void ConnectionImpl::open()
{
- QPID_LOG(info, "Connecting to " << host << ":" << port);
+ const std::string& protocol = handler.protocol;
+ const std::string& host = handler.host;
+ int port = handler.port;
+ QPID_LOG(info, "Connecting to " << protocol << ":" << host << ":" << port);
+
+ connector.reset(Connector::create(protocol, version, handler, this));
+ connector->setInputHandler(&handler);
+ connector->setShutdownHandler(this);
connector->connect(host, port);
connector->init();
- handler.waitForOpen();
+
+ // 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);
+ }
+
+ try {
+ handler.waitForOpen();
+ } catch (...) {
+ // Make sure the connector thread is joined.
+ connector->close();
+ throw;
+ }
+
+ // 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, "Activating security layer");
+ connector->activateSecurityLayer(securityLayer);
+ } else {
+ QPID_LOG(debug, "No security layer in place");
+ }
}
void ConnectionImpl::idleIn()
{
- close();
+ connector->abort();
}
void ConnectionImpl::idleOut()
{
- AMQFrame frame(in_place<AMQHeartbeatBody>());
+ AMQFrame frame((AMQHeartbeatBody()));
connector->send(frame);
}
void ConnectionImpl::close()
{
- if (!handler.isOpen()) return;
- handler.close();
- closed(NORMAL, "Closed by client");
+ 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) {
- connector->close();
- for (SessionMap::iterator i = sessions.begin(); i != sessions.end(); ++i) {
+ 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);
}
- sessions.clear();
}
void ConnectionImpl::closed(uint16_t code, const std::string& text) {
Mutex::ScopedLock l(lock);
- setException(new ConnectionException(code, text));
+ setException(new ConnectionException(ConnectionHandler::convert(code), text));
closeInternal(boost::bind(&SessionImpl::connectionClosed, _1, code, text));
}
-static const std::string CONN_CLOSED("Connection closed by broker");
+static const std::string CONN_CLOSED("Connection closed");
void ConnectionImpl::shutdown() {
- Mutex::ScopedLock l(lock);
- // FIXME aconway 2008-06-06: exception use, connection-forced is incorrect here.
- setException(new ConnectionException(CONNECTION_FORCED, CONN_CLOSED));
+ if ( failureCallback )
+ failureCallback();
+
if (handler.isClosed()) return;
- handler.fail(CONN_CLOSED);
- closeInternal(boost::bind(&SessionImpl::connectionBroke, _1, CONNECTION_FORCED, CONN_CLOSED));
+
+ // FIXME aconway 2008-06-06: exception use, amqp0-10 does not seem to have
+ // an appropriate close-code. connection-forced is not right.
+ bool isClosing = handler.isClosing();
+ handler.fail(CONN_CLOSED);//ensure connection is marked as failed before notifying sessions
+ Mutex::ScopedLock l(lock);
+ if (!isClosing)
+ closeInternal(boost::bind(&SessionImpl::connectionBroke, _1, CONN_CLOSED));
+ setException(new TransportFailure(CONN_CLOSED));
}
void ConnectionImpl::erase(uint16_t ch) {
@@ -154,4 +262,16 @@ 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;
+}
+
+}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/ConnectionImpl.h b/cpp/src/qpid/client/ConnectionImpl.h
index cd0788d68b..2b32e1ccf0 100644
--- a/cpp/src/qpid/client/ConnectionImpl.h
+++ b/cpp/src/qpid/client/ConnectionImpl.h
@@ -22,8 +22,9 @@
#ifndef _ConnectionImpl_
#define _ConnectionImpl_
-#include "Bounds.h"
-#include "ConnectionHandler.h"
+#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"
@@ -39,7 +40,7 @@ namespace qpid {
namespace client {
class Connector;
-class ConnectionSettings;
+struct ConnectionSettings;
class SessionImpl;
class ConnectionImpl : public Bounds,
@@ -51,12 +52,17 @@ class ConnectionImpl : public Bounds,
{
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;
+ boost::intrusive_ptr<qpid::sys::TimerTask> heartbeatTask;
+
template <class F> void closeInternal(const F&);
void incoming(framing::AMQFrame& frame);
@@ -65,20 +71,27 @@ class ConnectionImpl : public Bounds,
void idleIn();
void shutdown();
+ boost::function<void ()> failureCallback;
+
public:
ConnectionImpl(framing::ProtocolVersion version, const ConnectionSettings& settings);
~ConnectionImpl();
- void open(const std::string& host, int port);
+ void open();
bool isOpen() const;
- void addSession(const boost::shared_ptr<SessionImpl>&);
+ 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; }
};
}}
diff --git a/cpp/src/qpid/client/ConnectionSettings.cpp b/cpp/src/qpid/client/ConnectionSettings.cpp
index 6bc220cd41..3ae4ee010a 100644
--- a/cpp/src/qpid/client/ConnectionSettings.cpp
+++ b/cpp/src/qpid/client/ConnectionSettings.cpp
@@ -18,27 +18,28 @@
* under the License.
*
*/
-#include "ConnectionSettings.h"
+#include "qpid/client/ConnectionSettings.h"
#include "qpid/log/Logger.h"
#include "qpid/sys/Socket.h"
-#include <sys/socket.h>
+#include "qpid/Version.h"
namespace qpid {
namespace client {
ConnectionSettings::ConnectionSettings() :
+ protocol("tcp"),
host("localhost"),
port(TcpAddress::DEFAULT_PORT),
- username("guest"),
- password("guest"),
- mechanism("PLAIN"),
locale("en_US"),
heartbeat(0),
maxChannels(32767),
maxFrameSize(65535),
bounds(2),
- tcpNoDelay(false)
+ tcpNoDelay(false),
+ service(qpid::saslName),
+ minSsf(0),
+ maxSsf(256)
{}
ConnectionSettings::~ConnectionSettings() {}
@@ -46,7 +47,7 @@ ConnectionSettings::~ConnectionSettings() {}
void ConnectionSettings::configureSocket(qpid::sys::Socket& socket) const
{
if (tcpNoDelay) {
- socket.setTcpNoDelay(tcpNoDelay);
+ socket.setTcpNoDelay();
QPID_LOG(info, "Set TCP_NODELAY");
}
}
diff --git a/cpp/src/qpid/client/ConnectionSettings.h b/cpp/src/qpid/client/ConnectionSettings.h
deleted file mode 100644
index 5e93b3103e..0000000000
--- a/cpp/src/qpid/client/ConnectionSettings.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef QPID_CLIENT_CONNECTIONSETTINGS_H
-#define QPID_CLIENT_CONNECTIONSETTINGS_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 "qpid/log/Options.h"
-#include "qpid/Url.h"
-
-#include <iostream>
-#include <exception>
-
-namespace qpid {
-
-namespace sys {
-class Socket;
-}
-
-namespace client {
-
-/**
- * Settings for a Connection.
- */
-struct ConnectionSettings {
-
- ConnectionSettings();
- virtual ~ConnectionSettings();
-
- /**
- * Allows socket to be configured; default only sets tcp-nodelay
- * based on the flag set. Can be overridden.
- */
- virtual void configureSocket(qpid::sys::Socket&) const;
-
- /**
- * The host (or ip address) to connect to (defaults to 'localhost').
- */
- std::string host;
- /**
- * The port to connect to (defaults to 5672).
- */
- uint16_t port;
- /**
- * Allows an AMQP 'virtual host' to be specified for the
- * connection.
- */
- std::string virtualhost;
-
- /**
- * The username to use when authenticating the connection.
- */
- std::string username;
- /**
- * The password to use when authenticating the connection.
- */
- std::string password;
- /**
- * The SASL mechanism to use when authenticating the connection;
- * the options are currently PLAIN or ANONYMOUS.
- */
- std::string mechanism;
- /**
- * Allows a locale to be specified for the connection.
- */
- std::string locale;
- /**
- * Allows a heartbeat frequency to be specified (this feature is
- * not yet implemented).
- */
- uint16_t heartbeat;
- /**
- * The maximum number of channels that the client will request for
- * use on this connection.
- */
- uint16_t maxChannels;
- /**
- * The maximum frame size that the client will request for this
- * connection.
- */
- uint16_t maxFrameSize;
- /**
- * Allows the size of outgoing frames to be limited. The value
- * should be a mutliple of the maximum buffer size in use (which
- * is in turn set through the maxFrameSize setting above).
- */
- uint bounds;
- /**
- * If true, TCP_NODELAY will be set for the connection.
- */
- bool tcpNoDelay;
-};
-
-}} // namespace qpid::client
-
-#endif /*!QPID_CLIENT_CONNECTIONSETTINGS_H*/
diff --git a/cpp/src/qpid/client/Connector.cpp b/cpp/src/qpid/client/Connector.cpp
index f4f414bc63..2c4feffdcf 100644
--- a/cpp/src/qpid/client/Connector.cpp
+++ b/cpp/src/qpid/client/Connector.cpp
@@ -18,20 +18,23 @@
* under the License.
*
*/
-#include "Connector.h"
-#include "Bounds.h"
-#include "ConnectionImpl.h"
-#include "ConnectionSettings.h"
+#include "qpid/client/Connector.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/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 <map>
#include <boost/bind.hpp>
#include <boost/format.hpp>
@@ -43,227 +46,37 @@ using namespace qpid::framing;
using boost::format;
using boost::str;
-Connector::Connector(ProtocolVersion ver,
- const ConnectionSettings& settings,
- ConnectionImpl* cimpl)
- : maxFrameSize(settings.maxFrameSize),
- version(ver),
- initiated(false),
- closed(true),
- joined(true),
- timeout(0),
- idleIn(0), idleOut(0),
- timeoutHandler(0),
- shutdownHandler(0),
- writer(maxFrameSize, cimpl),
- aio(0),
- impl(cimpl)
-{
- QPID_LOG(debug, "Connector created for " << version);
- settings.configureSocket(socket);
-}
-
-Connector::~Connector() {
- close();
-}
-
-void Connector::connect(const std::string& host, int port){
- Mutex::ScopedLock l(closedLock);
- assert(closed);
- socket.connect(host, port);
- identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
- closed = false;
- poller = Poller::shared_ptr(new Poller);
- aio = new AsynchIO(socket,
- boost::bind(&Connector::readbuff, this, _1, _2),
- boost::bind(&Connector::eof, this, _1),
- boost::bind(&Connector::eof, this, _1),
- 0, // closed
- 0, // nobuffs
- boost::bind(&Connector::writebuff, this, _1));
- writer.init(identifier, aio);
-}
-
-void Connector::init(){
- Mutex::ScopedLock l(closedLock);
- assert(joined);
- ProtocolInitiation init(version);
- writeDataBlock(init);
- joined = false;
- receiver = Thread(this);
-}
-
-bool Connector::closeInternal() {
- Mutex::ScopedLock l(closedLock);
- bool ret = !closed;
- if (!closed) {
- closed = true;
- poller->shutdown();
- }
- if (!joined && receiver.id() != Thread::current().id()) {
- joined = true;
- Mutex::ScopedUnlock u(closedLock);
- receiver.join();
- }
- return ret;
-}
-
-void Connector::close() {
- closeInternal();
-}
-
-void Connector::setInputHandler(InputHandler* handler){
- input = handler;
-}
-
-void Connector::setShutdownHandler(ShutdownHandler* handler){
- shutdownHandler = handler;
-}
-
-OutputHandler* Connector::getOutputHandler(){
- return this;
-}
+// Stuff for the registry of protocol connectors (maybe should be moved to its own file)
+namespace {
+ typedef std::map<std::string, Connector::Factory*> ProtocolRegistry;
-void Connector::send(AMQFrame& frame) {
- writer.handle(frame);
-}
+ ProtocolRegistry& theProtocolRegistry() {
+ static ProtocolRegistry protocolRegistry;
-void Connector::handleClosed() {
- if (closeInternal() && shutdownHandler)
- shutdownHandler->shutdown();
+ return protocolRegistry;
+ }
}
-struct Connector::Buff : public AsynchIO::BufferBase {
- Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {}
- ~Buff() { delete [] bytes;}
-};
-
-Connector::Writer::Writer(uint16_t s, Bounds* b) : maxFrameSize(s), aio(0), buffer(0), lastEof(0), bounds(b)
+Connector* Connector::create(const std::string& proto, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c)
{
-}
-
-Connector::Writer::~Writer() { delete buffer; }
-
-void Connector::Writer::init(std::string id, sys::AsynchIO* a) {
- Mutex::ScopedLock l(lock);
- identifier = id;
- aio = a;
- newBuffer(l);
-}
-void Connector::Writer::handle(framing::AMQFrame& frame) {
- Mutex::ScopedLock l(lock);
- frames.push_back(frame);
- if (frame.getEof()) {//or if we already have a buffers worth
- lastEof = frames.size();
- aio->notifyPendingWrite();
- }
- QPID_LOG(trace, "SENT " << identifier << ": " << frame);
-}
-
-void Connector::Writer::writeOne(const Mutex::ScopedLock& l) {
- assert(buffer);
- framesEncoded = 0;
-
- buffer->dataStart = 0;
- buffer->dataCount = encode.getPosition();
- aio->queueWrite(buffer);
- newBuffer(l);
-}
-
-void Connector::Writer::newBuffer(const Mutex::ScopedLock&) {
- buffer = aio->getQueuedBuffer();
- if (!buffer) buffer = new Buff(maxFrameSize);
- encode = framing::Buffer(buffer->bytes, buffer->byteCount);
- framesEncoded = 0;
-}
-
-// Called in IO thread.
-void Connector::Writer::write(sys::AsynchIO&) {
- 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.size();
- if (size > encode.available()) writeOne(l);
- assert(size <= encode.available());
- frame.encode(encode);
- ++framesEncoded;
- bytesWritten += size;
+ ProtocolRegistry::const_iterator i = theProtocolRegistry().find(proto);
+ if (i==theProtocolRegistry().end()) {
+ throw Exception(QPID_MSG("Unknown protocol: " << proto));
}
- frames.erase(frames.begin(), frames.begin()+lastEof);
- lastEof = 0;
- if (bounds) bounds->reduce(bytesWritten);
- if (encode.getPosition() > 0) writeOne(l);
+ return (i->second)(v, s, c);
}
-void Connector::readbuff(AsynchIO& aio, AsynchIO::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 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;
}
-void Connector::writebuff(AsynchIO& aio_) {
- writer.write(aio_);
-}
-
-void Connector::writeDataBlock(const AMQDataBlock& data) {
- AsynchIO::BufferBase* buff = new Buff(maxFrameSize);
- framing::Buffer out(buff->bytes, buff->byteCount);
- data.encode(out);
- buff->dataCount = data.size();
- aio->queueWrite(buff);
-}
-
-void Connector::eof(AsynchIO&) {
- handleClosed();
-}
-
-// TODO: astitcher 20070908 This version of the code can never time out, so the idle processing
-// will never be called
-void Connector::run(){
- // Keep the connection impl in memory until run() completes.
- boost::shared_ptr<ConnectionImpl> protect = impl->shared_from_this();
- assert(protect);
- try {
- Dispatcher d(poller);
-
- for (int i = 0; i < 32; i++) {
- aio->queueReadBuffer(new Buff(maxFrameSize));
- }
-
- aio->start(poller);
- d.run();
- aio->queueForDeletion();
- socket.close();
- } catch (const std::exception& e) {
- QPID_LOG(error, e.what());
- handleClosed();
- }
+void Connector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>)
+{
}
-
}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/Connector.h b/cpp/src/qpid/client/Connector.h
index cde12f7b5b..3a49ae9012 100644
--- a/cpp/src/qpid/client/Connector.h
+++ b/cpp/src/qpid/client/Connector.h
@@ -42,107 +42,40 @@
namespace qpid {
namespace sys {
-class Poller;
-class AsynchIO;
-class AsynchIOBufferBase;
+class SecurityLayer;
}
-
+
namespace client {
-class Bounds;
-class ConnectionSettings;
+struct ConnectionSettings;
class ConnectionImpl;
///@internal
-class Connector : public framing::OutputHandler,
- private sys::Runnable
-{
- struct Buff;
-
- /** Batch up frames for writing to aio. */
- class Writer : public framing::FrameHandler {
- typedef sys::AsynchIOBufferBase BufferBase;
- typedef std::vector<framing::AMQFrame> Frames;
-
- const uint16_t maxFrameSize;
- sys::Mutex lock;
- sys::AsynchIO* 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(const sys::Mutex::ScopedLock&);
- void newBuffer(const sys::Mutex::ScopedLock&);
-
- public:
-
- Writer(uint16_t maxFrameSize, Bounds*);
- ~Writer();
- void init(std::string id, sys::AsynchIO*);
- void handle(framing::AMQFrame&);
- void write(sys::AsynchIO&);
- };
-
- const uint16_t maxFrameSize;
- framing::ProtocolVersion version;
- bool initiated;
-
- sys::Mutex closedLock;
- bool closed;
- bool joined;
-
- sys::AbsTime lastIn;
- sys::AbsTime lastOut;
- sys::Duration timeout;
- sys::Duration idleIn;
- sys::Duration idleOut;
-
- sys::TimeoutHandler* timeoutHandler;
- sys::ShutdownHandler* shutdownHandler;
- framing::InputHandler* input;
- framing::InitiationHandler* initialiser;
- framing::OutputHandler* output;
-
- Writer writer;
-
- sys::Thread receiver;
-
- sys::Socket socket;
-
- sys::AsynchIO* aio;
- boost::shared_ptr<sys::Poller> poller;
-
- void run();
- void handleClosed();
- bool closeInternal();
-
- void readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
- void writebuff(qpid::sys::AsynchIO&);
- void writeDataBlock(const framing::AMQDataBlock& data);
- void eof(qpid::sys::AsynchIO&);
-
- std::string identifier;
-
- ConnectionImpl* impl;
-
+class Connector : public framing::OutputHandler
+{
public:
- Connector(framing::ProtocolVersion pVersion,
- const ConnectionSettings&,
- ConnectionImpl*);
- virtual ~Connector();
- virtual void connect(const std::string& host, int port);
- virtual void init();
- virtual void close();
- virtual void setInputHandler(framing::InputHandler* handler);
- virtual void setShutdownHandler(sys::ShutdownHandler* handler);
- virtual sys::ShutdownHandler* getShutdownHandler() { return shutdownHandler; }
- virtual framing::OutputHandler* getOutputHandler();
- virtual void send(framing::AMQFrame& frame);
- const std::string& getIdentifier() const { return identifier; }
+ // Protocol connector factory related stuff (it might be better to separate this code from the TCP Connector in the future)
+ typedef Connector* Factory(framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*);
+ static Connector* create(const std::string& proto, framing::ProtocolVersion, const ConnectionSettings&, ConnectionImpl*);
+ static void registerFactory(const std::string& proto, Factory* connectorFactory);
+
+ virtual ~Connector() {};
+ virtual void connect(const std::string& host, int 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 unsigned int getSSF() = 0;
+
};
}}
diff --git a/cpp/src/qpid/client/Demux.cpp b/cpp/src/qpid/client/Demux.cpp
index b774214ae4..abc23c75df 100644
--- a/cpp/src/qpid/client/Demux.cpp
+++ b/cpp/src/qpid/client/Demux.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "Demux.h"
+#include "qpid/client/Demux.h"
#include "qpid/Exception.h"
#include "qpid/framing/MessageTransferBody.h"
diff --git a/cpp/src/qpid/client/Demux.h b/cpp/src/qpid/client/Demux.h
index 7304e799bb..31dc3f9c06 100644
--- a/cpp/src/qpid/client/Demux.h
+++ b/cpp/src/qpid/client/Demux.h
@@ -25,6 +25,7 @@
#include "qpid/framing/FrameSet.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/ClientImportExport.h"
#ifndef _Demux_
#define _Demux_
@@ -49,17 +50,17 @@ public:
typedef sys::BlockingQueue<framing::FrameSet::shared_ptr> Queue;
typedef boost::shared_ptr<Queue> QueuePtr;
- Demux();
- ~Demux();
+ QPID_CLIENT_EXTERN Demux();
+ QPID_CLIENT_EXTERN ~Demux();
- void handle(framing::FrameSet::shared_ptr);
- void close(const sys::ExceptionHolder& ex);
- void open();
-
- QueuePtr add(const std::string& name, Condition);
- void remove(const std::string& name);
- QueuePtr get(const std::string& name);
- QueuePtr getDefault();
+ 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
diff --git a/cpp/src/qpid/client/Dispatcher.cpp b/cpp/src/qpid/client/Dispatcher.cpp
index 5028d68405..a715c623bf 100644
--- a/cpp/src/qpid/client/Dispatcher.cpp
+++ b/cpp/src/qpid/client/Dispatcher.cpp
@@ -18,15 +18,25 @@
* under the License.
*
*/
-#include "Dispatcher.h"
+#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 "Message.h"
-
-#include <boost/state_saver.hpp>
+#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;
@@ -37,44 +47,40 @@ using qpid::sys::Thread;
namespace qpid {
namespace client {
-Subscriber::Subscriber(const Session& s, MessageListener* l, AckPolicy a)
- : session(s), listener(l), autoAck(a) {}
-
-void Subscriber::received(Message& msg)
-{
- if (listener) {
- listener->received(msg);
- autoAck.ack(msg, session);
- }
-}
-
Dispatcher::Dispatcher(const Session& s, const std::string& q)
- : session(s), running(false), autoStop(true)
+ : session(s),
+ running(false),
+ autoStop(true),
+ failoverHandler(0)
{
- queue = q.empty() ?
- session.getExecution().getDemux().getDefault() :
- session.getExecution().getDemux().get(q);
-}
+ 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.");
- boost::state_saver<bool> reset(running); // Reset to false on exit.
+ 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(*content);
- Subscriber::shared_ptr listener = find(msg.getDestination());
+ 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 {
@@ -91,9 +97,21 @@ void Dispatcher::run()
}
session.sync(); // Make sure all our acks are received before returning.
}
- catch (const ClosedException&) {} //ignore it and return
+ 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) {
- QPID_LOG(error, "Exception in client dispatch thread: " << e.what());
+ if ( failoverHandler ) {
+ QPID_LOG(debug, QPID_MSG(session.getId() << " failover: " << e.what()));
+ failoverHandler();
+ } else {
+ QPID_LOG(error, session.getId() << " error: " << e.what());
+ throw;
+ }
}
}
@@ -109,7 +127,7 @@ void Dispatcher::setAutoStop(bool b)
autoStop = b;
}
-Subscriber::shared_ptr Dispatcher::find(const std::string& name)
+boost::intrusive_ptr<SubscriptionImpl> Dispatcher::find(const std::string& name)
{
ScopedLock<Mutex> l(lock);
Listeners::iterator i = listeners.find(name);
@@ -119,27 +137,14 @@ Subscriber::shared_ptr Dispatcher::find(const std::string& name)
return i->second;
}
-void Dispatcher::listen(
- MessageListener* listener, AckPolicy autoAck
-)
-{
+void Dispatcher::listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription) {
ScopedLock<Mutex> l(lock);
- defaultListener = Subscriber::shared_ptr(
- new Subscriber(session, listener, autoAck));
+ listeners[subscription->getName()] = subscription;
}
-void Dispatcher::listen(const std::string& destination, MessageListener* listener, AckPolicy autoAck)
-{
- ScopedLock<Mutex> l(lock);
- listeners[destination] = Subscriber::shared_ptr(
- new Subscriber(session, listener, autoAck));
-}
-
-void Dispatcher::cancel(const std::string& destination)
-{
+void Dispatcher::cancel(const std::string& destination) {
ScopedLock<Mutex> l(lock);
- listeners.erase(destination);
- if (autoStop && listeners.empty())
+ if (listeners.erase(destination) && running && autoStop && listeners.empty())
queue->close();
}
diff --git a/cpp/src/qpid/client/Dispatcher.h b/cpp/src/qpid/client/Dispatcher.h
index 7d42bf8793..74fdb90103 100644
--- a/cpp/src/qpid/client/Dispatcher.h
+++ b/cpp/src/qpid/client/Dispatcher.h
@@ -26,28 +26,18 @@
#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 "MessageListener.h"
-#include "AckPolicy.h"
+#include "qpid/client/ClientImportExport.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/SubscriptionImpl.h"
namespace qpid {
namespace client {
-///@internal
-class Subscriber : public MessageListener
-{
- AsyncSession session;
- MessageListener* const listener;
- AckPolicy autoAck;
-
-public:
- typedef boost::shared_ptr<Subscriber> shared_ptr;
- Subscriber(const Session& session, MessageListener* listener, AckPolicy);
- void received(Message& msg);
-
-};
+class SubscriptionImpl;
///@internal
typedef framing::Handler<framing::FrameSet> FrameSetHandler;
@@ -55,7 +45,7 @@ typedef framing::Handler<framing::FrameSet> FrameSetHandler;
///@internal
class Dispatcher : public sys::Runnable
{
- typedef std::map<std::string, Subscriber::shared_ptr> Listeners;
+ typedef std::map<std::string, boost::intrusive_ptr<SubscriptionImpl> >Listeners;
sys::Mutex lock;
sys::Thread worker;
Session session;
@@ -63,22 +53,32 @@ class Dispatcher : public sys::Runnable
bool running;
bool autoStop;
Listeners listeners;
- Subscriber::shared_ptr defaultListener;
+ boost::intrusive_ptr<SubscriptionImpl> defaultListener;
std::auto_ptr<FrameSetHandler> handler;
- Subscriber::shared_ptr find(const std::string& name);
+ 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 run();
+ 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 listen(MessageListener* listener, AckPolicy autoAck=AckPolicy());
- void listen(const std::string& destination, MessageListener* listener, AckPolicy autoAck=AckPolicy());
+ void registerFailoverHandler ( boost::function<void ()> fh )
+ {
+ failoverHandler = fh;
+ }
+
+ void listen(const boost::intrusive_ptr<SubscriptionImpl>& subscription);
void cancel(const std::string& destination);
};
diff --git a/cpp/src/qpid/client/Execution.h b/cpp/src/qpid/client/Execution.h
index 10674afde0..ad622af9c1 100644
--- a/cpp/src/qpid/client/Execution.h
+++ b/cpp/src/qpid/client/Execution.h
@@ -22,7 +22,7 @@
#define _Execution_
#include "qpid/framing/SequenceNumber.h"
-#include "Demux.h"
+#include "qpid/client/Demux.h"
namespace qpid {
namespace client {
diff --git a/cpp/src/qpid/client/FailoverListener.cpp b/cpp/src/qpid/client/FailoverListener.cpp
new file mode 100644
index 0000000000..3396f5598c
--- /dev/null
+++ b/cpp/src/qpid/client/FailoverListener.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 "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)
+{
+ knownBrokers = c.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/cpp/src/qpid/client/FailoverManager.cpp b/cpp/src/qpid/client/FailoverManager.cpp
new file mode 100644
index 0000000000..81f71eb7df
--- /dev/null
+++ b/cpp/src/qpid/client/FailoverManager.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/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 TcpAddress* tcp = j->get<TcpAddress>();
+ if (tcp) {
+ s.host = tcp->host;
+ s.port = tcp->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/cpp/src/qpid/client/FlowControl.h b/cpp/src/qpid/client/FlowControl.h
deleted file mode 100644
index a4ed9879f4..0000000000
--- a/cpp/src/qpid/client/FlowControl.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#ifndef QPID_CLIENT_FLOWCONTROL_H
-#define QPID_CLIENT_FLOWCONTROL_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 client {
-
-/**
- * Flow control works by associating a finite amount of "credit"
- * associated with a subscription.
- *
- * Credit includes a message count and a byte count. Each message
- * received decreases the message count by one, and the byte count by
- * the size of the message. Either count can have the special value
- * UNLIMITED which is never decreased.
- *
- * A subscription's credit is exhausted when the message count is 0 or
- * the byte count is too small for the next available message. The
- * subscription will not receive any further messages until is credit
- * is renewed.
- *
- * In "window mode" credit is automatically renewed when a message is
- * acknowledged (@see AckPolicy) In non-window mode credit is not
- * automatically renewed, it must be explicitly re-set (@see
- * SubscriptionManager)
- */
-struct FlowControl {
- static const uint32_t UNLIMITED=0xFFFFFFFF;
- FlowControl(uint32_t messages_=0, uint32_t bytes_=0, bool window_=false)
- : messages(messages_), bytes(bytes_), window(window_) {}
-
- static FlowControl messageCredit(uint32_t messages_) { return FlowControl(messages_,UNLIMITED,false); }
- static FlowControl messageWindow(uint32_t messages_) { return FlowControl(messages_,UNLIMITED,true); }
- static FlowControl byteCredit(uint32_t bytes_) { return FlowControl(UNLIMITED,bytes_,false); }
- static FlowControl byteWindow(uint32_t bytes_) { return FlowControl(UNLIMITED,bytes_,true); }
- static FlowControl unlimited() { return FlowControl(UNLIMITED, UNLIMITED, false); }
- static FlowControl zero() { return FlowControl(0, 0, false); }
-
- /** Message credit: subscription can accept up to this many messages. */
- uint32_t messages;
- /** Byte credit: subscription can accept up to this many bytes of message content. */
- uint32_t bytes;
- /** Window mode. If true credit is automatically renewed as messages are acknowledged. */
- bool window;
-
- bool operator==(const FlowControl& x) {
- return messages == x.messages && bytes == x.bytes && window == x.window;
- };
-};
-
-}} // namespace qpid::client
-
-#endif /*!QPID_CLIENT_FLOWCONTROL_H*/
diff --git a/cpp/src/qpid/client/Future.cpp b/cpp/src/qpid/client/Future.cpp
index 6a0c78ae4b..740cd3df59 100644
--- a/cpp/src/qpid/client/Future.cpp
+++ b/cpp/src/qpid/client/Future.cpp
@@ -19,7 +19,8 @@
*
*/
-#include "Future.h"
+#include "qpid/client/Future.h"
+#include "qpid/client/SessionImpl.h"
namespace qpid {
namespace client {
diff --git a/cpp/src/qpid/client/Future.h b/cpp/src/qpid/client/Future.h
deleted file mode 100644
index 67f39cdf3f..0000000000
--- a/cpp/src/qpid/client/Future.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#ifndef _Future_
-#define _Future_
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-#include "qpid/Exception.h"
-#include "qpid/framing/SequenceNumber.h"
-#include "qpid/framing/StructHelper.h"
-#include "FutureCompletion.h"
-#include "FutureResult.h"
-#include "SessionImpl.h"
-
-namespace qpid {
-namespace client {
-
-/**@internal */
-class Future : private framing::StructHelper
-{
- framing::SequenceNumber command;
- boost::shared_ptr<FutureResult> result;
- bool complete;
-
-public:
- Future() : complete(false) {}
- Future(const framing::SequenceNumber& id) : command(id), complete(false) {}
-
- template <class T> void decodeResult(T& value, SessionImpl& session)
- {
- if (result) {
- decode(value, result->getResult(session));
- } else {
- throw Exception("Result not expected");
- }
- }
-
- void wait(SessionImpl& session);
- bool isComplete(SessionImpl& session);
- void setFutureResult(boost::shared_ptr<FutureResult> r);
-};
-
-}}
-
-#endif
diff --git a/cpp/src/qpid/client/FutureCompletion.cpp b/cpp/src/qpid/client/FutureCompletion.cpp
index 130c65d6aa..ccfb073855 100644
--- a/cpp/src/qpid/client/FutureCompletion.cpp
+++ b/cpp/src/qpid/client/FutureCompletion.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "FutureCompletion.h"
+#include "qpid/client/FutureCompletion.h"
using namespace qpid::client;
using namespace qpid::sys;
diff --git a/cpp/src/qpid/client/FutureResult.cpp b/cpp/src/qpid/client/FutureResult.cpp
index 007f278051..0237eb1464 100644
--- a/cpp/src/qpid/client/FutureResult.cpp
+++ b/cpp/src/qpid/client/FutureResult.cpp
@@ -19,9 +19,9 @@
*
*/
-#include "FutureResult.h"
+#include "qpid/client/FutureResult.h"
-#include "SessionImpl.h"
+#include "qpid/client/SessionImpl.h"
using namespace qpid::client;
using namespace qpid::framing;
diff --git a/cpp/src/qpid/client/LoadPlugins.cpp b/cpp/src/qpid/client/LoadPlugins.cpp
new file mode 100644
index 0000000000..82cdedc7fe
--- /dev/null
+++ b/cpp/src/qpid/client/LoadPlugins.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.
+ *
+ */
+
+#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 {
+
+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);
+ }
+ }
+} init;
+
+} // namespace
diff --git a/cpp/src/qpid/client/LocalQueue.cpp b/cpp/src/qpid/client/LocalQueue.cpp
index 99ab6f0133..0019adabaf 100644
--- a/cpp/src/qpid/client/LocalQueue.cpp
+++ b/cpp/src/qpid/client/LocalQueue.cpp
@@ -18,59 +18,35 @@
* under the License.
*
*/
-#include "LocalQueue.h"
+#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;
-LocalQueue::LocalQueue(AckPolicy a) : autoAck(a) {}
-LocalQueue::~LocalQueue() {}
+typedef PrivateImplRef<LocalQueue> PI;
-Message LocalQueue::pop() { return get(); }
+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::get() {
- Message result;
- bool ok = get(result, sys::TIME_INFINITE);
- assert(ok); (void) ok;
- return result;
-}
+Message LocalQueue::pop(sys::Duration timeout) { return impl->pop(timeout); }
-bool LocalQueue::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>()) {
- result = Message(*content);
- autoAck.ack(result, session);
- return true;
- }
- else
- throw CommandInvalidException(
- QPID_MSG("Unexpected method: " << content->getMethod()));
-}
+Message LocalQueue::get(sys::Duration timeout) { return impl->get(timeout); }
-void LocalQueue::setAckPolicy(AckPolicy a) { autoAck=a; }
-AckPolicy& LocalQueue::getAckPolicy() { return autoAck; }
+bool LocalQueue::get(Message& result, sys::Duration timeout) { return impl->get(result, timeout); }
-bool LocalQueue::empty() const
-{
- if (!queue)
- throw ClosedException();
- return queue->empty();
-}
-
-size_t LocalQueue::size() const
-{
- if (!queue)
- throw ClosedException();
- return queue->size();
-}
+bool LocalQueue::empty() const { return impl->empty(); }
+size_t LocalQueue::size() const { return impl->size(); }
}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/LocalQueue.h b/cpp/src/qpid/client/LocalQueue.h
deleted file mode 100644
index f81065ef3c..0000000000
--- a/cpp/src/qpid/client/LocalQueue.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef QPID_CLIENT_LOCALQUEUE_H
-#define QPID_CLIENT_LOCALQUEUE_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/Demux.h"
-#include "qpid/client/AckPolicy.h"
-#include "qpid/sys/Time.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
- */
-class LocalQueue
-{
- public:
- /** Create a local queue. Subscribe the local queue to a remote broker
- * queue with a SubscriptionManager.
- *
- * LocalQueue is an alternative to implementing a MessageListener.
- *
- *@param ackPolicy Policy for acknowledging messages. @see AckPolicy.
- */
- LocalQueue(AckPolicy ackPolicy=AckPolicy());
-
- ~LocalQueue();
-
- /** 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 for a
- * message from the broker queue.
- *@exception ClosedException if subscription has been closed.
- */
- Message get();
-
- /** Synonym for get(). */
- Message pop();
-
- /** Return true if local queue is empty. */
- bool empty() const;
-
- /** Number of messages on the local queue */
- size_t size() const;
-
- /** Set the message acknowledgement policy. @see AckPolicy. */
- void setAckPolicy(AckPolicy);
-
- /** Get the message acknowledgement policy. @see AckPolicy. */
- AckPolicy& getAckPolicy();
-
- private:
- Session session;
- Demux::QueuePtr queue;
- AckPolicy autoAck;
-
- friend class SubscriptionManager;
-};
-
-}} // namespace qpid::client
-
-#endif /*!QPID_CLIENT_LOCALQUEUE_H*/
diff --git a/cpp/src/qpid/client/LocalQueueImpl.cpp b/cpp/src/qpid/client/LocalQueueImpl.cpp
new file mode 100644
index 0000000000..8b191728f4
--- /dev/null
+++ b/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/cpp/src/qpid/client/LocalQueueImpl.h b/cpp/src/qpid/client/LocalQueueImpl.h
new file mode 100644
index 0000000000..75b62cf203
--- /dev/null
+++ b/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/cpp/src/qpid/client/Message.cpp b/cpp/src/qpid/client/Message.cpp
index d5464594ee..00f911c57e 100644
--- a/cpp/src/qpid/client/Message.cpp
+++ b/cpp/src/qpid/client/Message.cpp
@@ -19,55 +19,44 @@
*
*/
-#include "Message.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/MessageImpl.h"
namespace qpid {
namespace client {
-Message::Message(const std::string& data_,
- const std::string& routingKey,
- const std::string& exchange) : TransferContent(data_, routingKey, exchange) {}
+Message::Message(MessageImpl* mi) : impl(mi) {}
-std::string Message::getDestination() const
-{
- return method.getDestination();
-}
+Message::Message(const std::string& data, const std::string& routingKey)
+ : impl(new MessageImpl(data, routingKey)) {}
-bool Message::isRedelivered() const
-{
- return hasDeliveryProperties() && getDeliveryProperties().getRedelivered();
-}
+Message::Message(const Message& m) : impl(new MessageImpl(*m.impl)) {}
-void Message::setRedelivered(bool redelivered)
-{
- getDeliveryProperties().setRedelivered(redelivered);
-}
+Message::~Message() { delete impl; }
-framing::FieldTable& Message::getHeaders()
-{
- return getMessageProperties().getApplicationHeaders();
-}
+Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; }
-const framing::FieldTable& Message::getHeaders() const
-{
- return getMessageProperties().getApplicationHeaders();
-}
+void Message::swap(Message& m) { std::swap(impl, m.impl); }
-const framing::MessageTransferBody& Message::getMethod() const
-{
- return method;
-}
+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(); }
-const framing::SequenceNumber& Message::getId() const
-{
- return id;
-}
+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(); }
-/**@internal for incoming messages */
-Message::Message(const framing::FrameSet& frameset) :
- method(*frameset.as<framing::MessageTransferBody>()), id(frameset.getId())
-{
- populate(frameset);
-}
+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/cpp/src/qpid/client/MessageImpl.cpp b/cpp/src/qpid/client/MessageImpl.cpp
new file mode 100644
index 0000000000..865c462b15
--- /dev/null
+++ b/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/cpp/src/qpid/client/Message.h b/cpp/src/qpid/client/MessageImpl.h
index 4e6ed49bb4..a64ddd20d8 100644
--- a/cpp/src/qpid/client/Message.h
+++ b/cpp/src/qpid/client/MessageImpl.h
@@ -1,5 +1,5 @@
-#ifndef _client_Message_h
-#define _client_Message_h
+#ifndef QPID_CLIENT_MESSAGEIMPL_H
+#define QPID_CLIENT_MESSAGEIMPL_H
/*
*
@@ -21,30 +21,24 @@
* under the License.
*
*/
-#include <string>
+#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 {
-/**
- * A message sent to or received from the broker.
- *
- * \ingroup clientapi
- */
-class Message : public framing::TransferContent
+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.
- *@param exchange Name of the exchange that should route the message.
*/
- Message(const std::string& data=std::string(),
- const std::string& routingKey=std::string(),
- const std::string& exchange=std::string());
+ 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
@@ -70,7 +64,10 @@ public:
const framing::SequenceNumber& getId() const;
/**@internal for incoming messages */
- Message(const framing::FrameSet& frameset);
+ 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:
@@ -80,4 +77,4 @@ private:
}}
-#endif /*!_client_Message_h*/
+#endif /*!QPID_CLIENT_MESSAGEIMPL_H*/
diff --git a/cpp/src/qpid/client/MessageListener.cpp b/cpp/src/qpid/client/MessageListener.cpp
index 68ebedeb0d..0f2a71287c 100644
--- a/cpp/src/qpid/client/MessageListener.cpp
+++ b/cpp/src/qpid/client/MessageListener.cpp
@@ -19,6 +19,6 @@
*
*/
-#include "MessageListener.h"
+#include "qpid/client/MessageListener.h"
qpid::client::MessageListener::~MessageListener() {}
diff --git a/cpp/src/qpid/client/MessageReplayTracker.cpp b/cpp/src/qpid/client/MessageReplayTracker.cpp
new file mode 100644
index 0000000000..079fb1167a
--- /dev/null
+++ b/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) {
+ 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/cpp/src/qpid/client/PrivateImplRef.h b/cpp/src/qpid/client/PrivateImplRef.h
new file mode 100644
index 0000000000..503a383c31
--- /dev/null
+++ b/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/cpp/src/qpid/client/QueueOptions.cpp b/cpp/src/qpid/client/QueueOptions.cpp
new file mode 100644
index 0000000000..f4c1483859
--- /dev/null
+++ b/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/cpp/src/qpid/client/RdmaConnector.cpp b/cpp/src/qpid/client/RdmaConnector.cpp
new file mode 100644
index 0000000000..77169db3a6
--- /dev/null
+++ b/cpp/src/qpid/client/RdmaConnector.cpp
@@ -0,0 +1,387 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/sys/rdma/RdmaIO.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, private sys::Runnable
+{
+ struct Buff;
+
+ typedef Rdma::Buffer BufferBase;
+ 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 pollingLock;
+ bool polling;
+ bool joined;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ sys::Thread receiver;
+
+ Rdma::AsynchIO* aio;
+ sys::Poller::shared_ptr poller;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+
+ ~RdmaConnector();
+
+ void run();
+ void handleClosed();
+ bool closeInternal();
+
+ // 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(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&);
+ 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 eof(Rdma::AsynchIO&);
+
+ std::string identifier;
+
+ ConnectionImpl* impl;
+
+ void connect(const std::string& host, int 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>);
+
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+
+public:
+ RdmaConnector(framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+ unsigned int getSSF() { return 0; }
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new RdmaConnector(v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ Connector::registerFactory("rdma", &create);
+ Connector::registerFactory("ib", &create);
+ };
+ } init;
+}
+
+
+RdmaConnector::RdmaConnector(ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ lastEof(0),
+ currentSize(0),
+ bounds(cimpl),
+ version(ver),
+ initiated(false),
+ polling(false),
+ joined(true),
+ shutdownHandler(0),
+ aio(0),
+ impl(cimpl)
+{
+ QPID_LOG(debug, "RdmaConnector created for " << version);
+}
+
+RdmaConnector::~RdmaConnector() {
+ close();
+}
+
+void RdmaConnector::connect(const std::string& host, int port){
+ Mutex::ScopedLock l(pollingLock);
+ assert(!polling);
+ assert(joined);
+ poller = Poller::shared_ptr(new Poller);
+
+ SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ Rdma::Connector* c = new Rdma::Connector(
+ sa,
+ 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, poller, _1),
+ boost::bind(&RdmaConnector::rejected, this, poller, _1, _2));
+ c->start(poller);
+
+ polling = true;
+ joined = false;
+ receiver = Thread(this);
+}
+
+// The following only gets run when connected
+void RdmaConnector::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) {
+ Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair();
+
+ aio = new Rdma::AsynchIO(ci->getQueuePair(),
+ 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::eof, this, _1)); // data error - just close connection
+ aio->start(poller);
+
+ identifier = str(format("[%1% %2%]") % ci->getLocalName() % ci->getPeerName());
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+}
+
+void RdmaConnector::connectionError(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, Rdma::ErrorType) {
+ QPID_LOG(trace, "Connection Error " << identifier);
+ eof(*aio);
+}
+
+void RdmaConnector::disconnected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&) {
+ eof(*aio);
+}
+
+void RdmaConnector::rejected(sys::Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams& cp) {
+ QPID_LOG(trace, "Connection Rejected " << identifier << ": " << cp.maxRecvBufferSize);
+ eof(*aio);
+}
+
+bool RdmaConnector::closeInternal() {
+ bool ret;
+ {
+ Mutex::ScopedLock l(pollingLock);
+ ret = polling;
+ if (polling) {
+ polling = false;
+ poller->shutdown();
+ }
+ if (joined || receiver.id() == Thread::current().id()) {
+ return ret;
+ }
+ joined = true;
+ }
+
+ receiver.join();
+ return ret;
+}
+
+void RdmaConnector::close() {
+ closeInternal();
+}
+
+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) {
+ 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();
+}
+
+void RdmaConnector::handleClosed() {
+ if (closeInternal() && shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+// Called in IO thread. (write idle routine)
+// This is NOT only called in response to previously calling notifyPendingWrite
+void RdmaConnector::writebuff(Rdma::AsynchIO&) {
+ Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
+ if (codec->canEncode()) {
+ std::auto_ptr<BufferBase> buffer = std::auto_ptr<BufferBase>(aio->getBuffer());
+ size_t encoded = codec->encode(buffer->bytes, buffer->byteCount);
+
+ buffer->dataStart = 0;
+ buffer->dataCount = encoded;
+ aio->queueWrite(buffer.release());
+ }
+}
+
+bool RdmaConnector::canEncode()
+{
+ Mutex::ScopedLock l(lock);
+ //have at least one full frameset or a whole buffers worth of data
+ return aio->writable() && aio->bufferAvailable() && (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->dataStart, 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->getBuffer();
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void RdmaConnector::eof(Rdma::AsynchIO&) {
+ handleClosed();
+}
+
+void RdmaConnector::run(){
+ // Keep the connection impl in memory until run() completes.
+ //GRS: currently the ConnectionImpls destructor is where the Io thread is joined
+ //boost::shared_ptr<ConnectionImpl> protect = impl->shared_from_this();
+ //assert(protect);
+ try {
+ Dispatcher d(poller);
+
+ //aio->start(poller);
+ d.run();
+ //aio->queueForDeletion();
+ } catch (const std::exception& e) {
+ {
+ // We're no longer polling
+ Mutex::ScopedLock l(pollingLock);
+ polling = false;
+ }
+ QPID_LOG(error, e.what());
+ handleClosed();
+ }
+}
+
+void RdmaConnector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer> sl)
+{
+ securityLayer = sl;
+ securityLayer->init(this);
+}
+
+}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/Results.cpp b/cpp/src/qpid/client/Results.cpp
index c605af2878..0de3e8bd04 100644
--- a/cpp/src/qpid/client/Results.cpp
+++ b/cpp/src/qpid/client/Results.cpp
@@ -19,18 +19,21 @@
*
*/
-#include "Results.h"
-#include "FutureResult.h"
+#include "qpid/client/Results.h"
+#include "qpid/client/FutureResult.h"
#include "qpid/framing/SequenceSet.h"
using namespace qpid::framing;
-using namespace boost;
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++) {
diff --git a/cpp/src/qpid/client/Results.h b/cpp/src/qpid/client/Results.h
index 667f35089c..4c49f6b05b 100644
--- a/cpp/src/qpid/client/Results.h
+++ b/cpp/src/qpid/client/Results.h
@@ -38,6 +38,7 @@ 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);
diff --git a/cpp/src/qpid/client/Sasl.h b/cpp/src/qpid/client/Sasl.h
new file mode 100644
index 0000000000..63da37fcb1
--- /dev/null
+++ b/cpp/src/qpid/client/Sasl.h
@@ -0,0 +1,70 @@
+#ifndef QPID_CLIENT_SASL_H
+#define QPID_CLIENT_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;
+}
+
+namespace client {
+
+struct ConnectionSettings;
+
+/**
+ * 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 ssf Security Strength Factor (SSF). SSF is used to negotiate
+ * a SASL security layer on top of the connection should both
+ * parties require and support it. The value indicates the
+ * required level of security for communication. 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.
+ */
+ virtual std::string start(const std::string& mechanisms, unsigned int ssf) = 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::client
+
+#endif /*!QPID_CLIENT_SASL_H*/
diff --git a/cpp/src/qpid/client/SaslFactory.cpp b/cpp/src/qpid/client/SaslFactory.cpp
new file mode 100644
index 0000000000..5012b75c94
--- /dev/null
+++ b/cpp/src/qpid/client/SaslFactory.cpp
@@ -0,0 +1,379 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/SaslFactory.h"
+#include "qpid/client/ConnectionSettings.h"
+#include <map>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef HAVE_SASL
+
+namespace qpid {
+namespace client {
+
+//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 ConnectionSettings&)
+{
+ return std::auto_ptr<Sasl>();
+}
+
+qpid::sys::Mutex SaslFactory::lock;
+std::auto_ptr<SaslFactory> SaslFactory::instance;
+
+}} // namespace qpid::client
+
+#else
+
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
+#include "qpid/log/Statement.h"
+#include <sasl/sasl.h>
+#include <strings.h>
+
+namespace qpid {
+namespace client {
+
+using qpid::sys::SecurityLayer;
+using qpid::sys::cyrus::CyrusSecurityLayer;
+using qpid::framing::InternalErrorException;
+
+const size_t MAX_LOGIN_LENGTH = 50;
+
+class CyrusSasl : public Sasl
+{
+ public:
+ CyrusSasl(const ConnectionSettings&);
+ ~CyrusSasl();
+ std::string start(const std::string& mechanisms, unsigned int ssf);
+ 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
+ ConnectionSettings settings;
+ std::string input;
+ std::string mechanism;
+ char login[MAX_LOGIN_LENGTH];
+
+ 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 ConnectionSettings& settings)
+{
+ std::auto_ptr<Sasl> sasl(new CyrusSasl(settings));
+ return sasl;
+}
+
+CyrusSasl::CyrusSasl(const ConnectionSettings& s) : conn(0), settings(s)
+{
+ 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_USER;
+ callbacks[i].proc = (CallbackProc*) &getUserFromSettings;
+ callbacks[i++].context = &settings;
+
+ 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, unsigned int ssf)
+{
+ 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 (ssf) {
+ sasl_ssf_t external_ssf = (sasl_ssf_t) 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 " << ssf);
+ }
+ }
+
+ 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)
+{
+
+ 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 = ((ConnectionSettings*) context)->username.c_str();
+ QPID_LOG(debug, "getUserFromSettings(): " << (*result));
+ return SASL_OK;
+ } else {
+ return SASL_FAIL;
+ }
+}
+
+namespace {
+// Global map of secrest 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 = ((ConnectionSettings*) 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, ((ConnectionSettings*) context)->password.data(), length);
+ *psecret = secret;
+ return SASL_OK;
+ } else {
+ return SASL_FAIL;
+ }
+}
+
+}} // namespace qpid::client
+
+#endif
diff --git a/cpp/src/qpid/client/SaslFactory.h b/cpp/src/qpid/client/SaslFactory.h
new file mode 100644
index 0000000000..d012af06f7
--- /dev/null
+++ b/cpp/src/qpid/client/SaslFactory.h
@@ -0,0 +1,48 @@
+#ifndef QPID_CLIENT_SASLFACTORY_H
+#define QPID_CLIENT_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/client/Sasl.h"
+#include "qpid/sys/Mutex.h"
+#include <memory>
+
+namespace qpid {
+namespace client {
+
+/**
+ * Factory for instances of the Sasl interface through which Sasl
+ * support is provided to a ConnectionHandler.
+ */
+class SaslFactory
+{
+ public:
+ std::auto_ptr<Sasl> create(const ConnectionSettings&);
+ static SaslFactory& getInstance();
+ ~SaslFactory();
+ private:
+ SaslFactory();
+ static qpid::sys::Mutex lock;
+ static std::auto_ptr<SaslFactory> instance;
+};
+}} // namespace qpid::client
+
+#endif /*!QPID_CLIENT_SASLFACTORY_H*/
diff --git a/cpp/src/qpid/client/SessionBase_0_10.cpp b/cpp/src/qpid/client/SessionBase_0_10.cpp
index 974acbfcf6..1a345d534e 100644
--- a/cpp/src/qpid/client/SessionBase_0_10.cpp
+++ b/cpp/src/qpid/client/SessionBase_0_10.cpp
@@ -18,21 +18,23 @@
* under the License.
*
*/
-#include "SessionBase_0_10.h"
+#include "qpid/client/SessionBase_0_10.h"
+#include "qpid/client/Connection.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() { impl->close(); }
-
-Execution& SessionBase_0_10::getExecution()
-{
- return *impl;
+void SessionBase_0_10::close()
+{
+ if (impl) impl->close();
}
void SessionBase_0_10::flush()
@@ -47,6 +49,11 @@ void SessionBase_0_10::sync()
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);
@@ -57,8 +64,14 @@ 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(); }
-framing::FrameSet::shared_ptr SessionBase_0_10::get() { return impl->get(); }
+bool SessionBase_0_10::isValid() const { return impl; }
}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/SessionBase_0_10.h b/cpp/src/qpid/client/SessionBase_0_10.h
deleted file mode 100644
index 8634164dd1..0000000000
--- a/cpp/src/qpid/client/SessionBase_0_10.h
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef QPID_CLIENT_SESSIONBASE_H
-#define QPID_CLIENT_SESSIONBASE_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/amqp_structs.h"
-#include "qpid/framing/ProtocolVersion.h"
-#include "qpid/framing/MethodContent.h"
-#include "qpid/framing/TransferContent.h"
-#include "qpid/client/Completion.h"
-#include "qpid/client/ConnectionImpl.h"
-#include "qpid/client/Execution.h"
-#include "qpid/client/SessionImpl.h"
-#include "qpid/client/TypedResult.h"
-#include "qpid/shared_ptr.h"
-#include <string>
-
-namespace qpid {
-namespace client {
-
-using std::string;
-using framing::Content;
-using framing::FieldTable;
-using framing::MethodContent;
-using framing::SequenceNumber;
-using framing::SequenceSet;
-using framing::SequenceNumberSet;
-using qpid::SessionId;
-using framing::Xid;
-
-/** Unit of message credit: messages or bytes */
-enum CreditUnit { MESSAGE_CREDIT=0, BYTE_CREDIT=1, UNLIMITED_CREDIT=0xFFFFFFFF };
-
-/**
- * Base class for handles to an AMQP session.
- *
- * Subclasses provide the AMQP commands for a given
- * version of the protocol.
- */
-class SessionBase_0_10 {
- public:
-
- typedef framing::TransferContent DefaultContent;
-
- ///@internal
- SessionBase_0_10();
- ~SessionBase_0_10();
-
- /** Get the next message frame-set from the session. */
- framing::FrameSet::shared_ptr get();
-
- /** Get the session ID */
- SessionId getId() const;
-
- /** Close the session.
- * A session is automatically closed when all handles to it are destroyed.
- */
- void close();
-
- /**
- * Synchronize the session: sync() waits until all commands issued
- * on this session so far have been completed by the broker.
- *
- * Note sync() is always synchronous, even on an AsyncSession object
- * because that's almost always what you want. You can call
- * AsyncSession::executionSync() directly in the unusual event
- * that you want to do an asynchronous sync.
- */
- void sync();
-
- /** Set the timeout for this session. */
- uint32_t timeout(uint32_t seconds);
-
- Execution& getExecution();
- void flush();
- void markCompleted(const framing::SequenceNumber& id, bool cumulative, bool notifyPeer);
- void sendCompletion();
-
- protected:
- boost::shared_ptr<SessionImpl> impl;
- friend class SessionBase_0_10Access;
-};
-
-}} // namespace qpid::client
-
-#endif /*!QPID_CLIENT_SESSIONBASE_H*/
diff --git a/cpp/src/qpid/client/SessionBase_0_10Access.h b/cpp/src/qpid/client/SessionBase_0_10Access.h
index e2189a53dd..4d08a7ceaf 100644
--- a/cpp/src/qpid/client/SessionBase_0_10Access.h
+++ b/cpp/src/qpid/client/SessionBase_0_10Access.h
@@ -33,7 +33,7 @@ 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() { return sb.impl; }
+ boost::shared_ptr<SessionImpl> get() const { return sb.impl; }
private:
SessionBase_0_10& sb;
};
diff --git a/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp
index 2075ed04f3..0f767c9f2e 100644
--- a/cpp/src/qpid/client/SessionImpl.cpp
+++ b/cpp/src/qpid/client/SessionImpl.cpp
@@ -19,21 +19,25 @@
*
*/
-#include "SessionImpl.h"
+#include "qpid/client/SessionImpl.h"
-#include "ConnectionImpl.h"
-#include "Future.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/constants.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; }
@@ -41,29 +45,26 @@ namespace qpid {
namespace client {
using namespace qpid::framing;
-using namespace qpid::framing::session;//for detach codes
+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,
- shared_ptr<ConnectionImpl> conn,
- uint16_t ch, uint64_t _maxFrameSize)
- : error(OK),
- code(NORMAL),
- text(EMPTY),
- state(INACTIVE),
+SessionImpl::SessionImpl(const std::string& name, boost::shared_ptr<ConnectionImpl> conn)
+ : state(INACTIVE),
detachedLifetime(0),
- maxFrameSize(_maxFrameSize),
+ maxFrameSize(conn->getNegotiatedSettings().maxFrameSize),
id(conn->getNegotiatedSettings().username, name.empty() ? Uuid(true).str() : name),
connection(conn),
ioHandler(*this),
- channel(ch),
proxy(ioHandler),
nextIn(0),
- nextOut(0)
+ nextOut(0),
+ sendMsgCredit(0),
+ doClearDeliveryPropertiesExchange(true),
+ autoDetach(true)
{
channel.next = connection.get();
}
@@ -71,12 +72,18 @@ SessionImpl::SessionImpl(const std::string& name,
SessionImpl::~SessionImpl() {
{
Lock l(state);
- if (state != DETACHED) {
- QPID_LOG(error, "Session was not closed cleanly");
+ if (state != DETACHED && state != DETACHING) {
+ QPID_LOG(warning, "Session was not closed cleanly: " << id);
+ try {
+ // Inform broker but don't wait for detached as that deadlocks.
+ // The detached will be ignored as the channel will be invalid.
+ if (autoDetach) detach();
+ } catch (...) {} // ignore errors.
setState(DETACHED);
handleClosed();
state.waitWaiters();
}
+ delete sendMsgCredit;
}
connection->erase(channel);
}
@@ -102,7 +109,7 @@ void SessionImpl::open(uint32_t timeout) // user thread
waitFor(ATTACHED);
//TODO: timeout will not be set locally until get response to
//confirm, should we wait for that?
- proxy.requestTimeout(timeout);
+ setTimeout(timeout);
proxy.commandPoint(nextOut, 0);
} else {
throw Exception("Open already called for this session");
@@ -112,16 +119,18 @@ void SessionImpl::open(uint32_t timeout) // user thread
void SessionImpl::close() //user thread
{
Lock l(state);
- if (detachedLifetime) {
- proxy.requestTimeout(0);
- //should we wait for the timeout response?
- detachedLifetime = 0;
+ // 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);
}
- detach();
- waitFor(DETACHED);
}
-void SessionImpl::resume(shared_ptr<ConnectionImpl>) // user thread
+void SessionImpl::resume(boost::shared_ptr<ConnectionImpl>) // user thread
{
throw NotImplementedException("Resume not yet implemented by client!");
}
@@ -135,8 +144,8 @@ void SessionImpl::suspend() //user thread
void SessionImpl::detach() //call with lock held
{
if (state == ATTACHED) {
- proxy.detach(id.getName());
setState(DETACHING);
+ proxy.detach(id.getName());
}
}
@@ -201,6 +210,16 @@ bool SessionImpl::isCompleteUpTo(const SequenceNumber& id)
return f.result;
}
+framing::SequenceNumber SessionImpl::getCompleteUpTo()
+{
+ SequenceNumber firstIncomplete;
+ {
+ Lock l(state);
+ firstIncomplete = incompleteIn.front();
+ }
+ return --firstIncomplete;
+}
+
struct MarkCompleted
{
const SequenceNumber& id;
@@ -219,6 +238,16 @@ struct MarkCompleted
};
+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);
@@ -239,17 +268,22 @@ void SessionImpl::markCompleted(const SequenceNumber& id, bool cumulative, bool
}
}
+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)
-{
- Lock l(state);
- error = CONNECTION_CLOSE;
- code = _code;
- text = _text;
- setState(DETACHED);
+void SessionImpl::connectionClosed(uint16_t code, const std::string& text) {
+ setException(createConnectionException(code, text));
handleClosed();
}
@@ -257,9 +291,9 @@ void SessionImpl::connectionClosed(uint16_t _code, const std::string& _text)
* Called by ConnectionImpl to notify active sessions when connection
* is disconnected
*/
-void SessionImpl::connectionBroke(uint16_t _code, const std::string& _text)
-{
- connectionClosed(_code, _text);
+void SessionImpl::connectionBroke(const std::string& _text) {
+ setException(sys::ExceptionHolder(new TransportFailure(_text)));
+ handleClosed();
}
Future SessionImpl::send(const AMQBody& command)
@@ -272,8 +306,76 @@ 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++;
{
@@ -297,9 +399,21 @@ Future SessionImpl::sendCommand(const AMQBody& command, const MethodContent* con
}
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){
@@ -309,7 +423,7 @@ void SessionImpl::sendContent(const MethodContent& content)
const uint32_t frag_size = maxFrameSize - AMQFrame::frameOverhead();
if(data_length < frag_size){
- AMQFrame frame(in_place<AMQContentBody>(content.getData()));
+ AMQFrame frame((AMQContentBody(content.getData())));
frame.setFirstSegment(false);
handleOut(frame);
}else{
@@ -318,7 +432,7 @@ void SessionImpl::sendContent(const MethodContent& content)
while (remaining > 0) {
uint32_t length = remaining > frag_size ? frag_size : remaining;
string frag(content.getData().substr(offset, length));
- AMQFrame frame(in_place<AMQContentBody>(frag));
+ AMQFrame frame((AMQContentBody(frag)));
frame.setFirstSegment(false);
frame.setLastSegment(true);
if (offset > 0) {
@@ -359,37 +473,45 @@ bool isContentFrame(AMQFrame& frame)
void SessionImpl::handleIn(AMQFrame& frame) // network thread
{
try {
- if (!invoke(static_cast<SessionHandler&>(*this), *frame.getBody())) {
- 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 not handled by this class, its for the application:
- deliver(frame);
- }
+ 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) {
- //TODO: proper 0-10 exception handling
- QPID_LOG(error, "Session exception:" << e.what());
- Lock l(state);
- error = EXCEPTION;
- code = e.code;
- text = e.what();
+ }
+ 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
{
- connection->expand(frame.size(), true);
- channel.handle(frame);
+ sendFrame(frame, true);
}
void SessionImpl::proxyOut(AMQFrame& frame) // network thread
{
- connection->expand(frame.size(), false);
+ //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)
+{
channel.handle(frame);
+ connection->expand(frame.encodedSize(), canBlock);
}
void SessionImpl::deliver(AMQFrame& frame) // network thread
@@ -435,24 +557,22 @@ void SessionImpl::detach(const std::string& _name)
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);
}
-void SessionImpl::detached(const std::string& _name, uint8_t _code)
-{
+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
- QPID_LOG(error, "Session detached by peer: " << id << " " << code);
- error = SESSION_DETACH;
- code = _code;
- text = "Session detached by peer";
+ setExceptionLH(createChannelException(_code, "Session detached by peer"));
+ QPID_LOG(error, exceptionHolder.what());
}
if (detachedLifetime == 0) {
handleClosed();
- }
+}
}
void SessionImpl::requestTimeout(uint32_t t)
@@ -478,7 +598,7 @@ void SessionImpl::commandPoint(const framing::SequenceNumber& id, uint64_t offse
void SessionImpl::expected(const framing::SequenceSet& commands, const framing::Array& fragments)
{
- if (!commands.empty() || fragments.size()) {
+ if (!commands.empty() || fragments.encodedSize()) {
throw NotImplementedException("Session resumption not yet supported");
}
}
@@ -492,7 +612,7 @@ void SessionImpl::completed(const framing::SequenceSet& commands, bool timelyRep
{
Lock l(state);
incompleteOut.remove(commands);
- state.notify();//notify any waiters of completion
+ state.notifyAll();//notify any waiters of completion
completedOut.add(commands);
//notify any waiting results of completion
results.completed(commands);
@@ -561,20 +681,78 @@ void SessionImpl::exception(uint16_t errorCode,
const std::string& description,
const framing::FieldTable& /*errorInfo*/)
{
- QPID_LOG(warning, "Exception received from peer: " << errorCode << ":" << description
+ 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);
- error = EXCEPTION;
- code = errorCode;
- text = description;
- if (detachedLifetime) {
- proxy.requestTimeout(0);
- //should we wait for the timeout response?
- detachedLifetime = 0;
+ 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:
@@ -586,25 +764,21 @@ inline void SessionImpl::setState(State s) //call with lock held
inline void SessionImpl::waitFor(State s) //call with lock held
{
// We can be DETACHED at any time
- state.waitFor(States(s, DETACHED));
+ if (s == DETACHED) state.waitFor(DETACHED);
+ else state.waitFor(States(s, DETACHED));
check();
}
void SessionImpl::check() const //call with lock held.
{
- switch (error) {
- case OK: break;
- case CONNECTION_CLOSE: throw ConnectionException(code, text);
- case SESSION_DETACH: throw ChannelException(code, text);
- case EXCEPTION: throwExecutionException(code, text);
- }
+ exceptionHolder.raise();
}
void SessionImpl::checkOpen() const //call with lock held.
{
check();
if (state != ATTACHED) {
- throw NotAttachedException("Session isn't attached");
+ throw NotAttachedException(QPID_MSG("Session " << getId() << " isn't attached"));
}
}
@@ -616,10 +790,28 @@ void SessionImpl::assertOpen() const
void SessionImpl::handleClosed()
{
- // FIXME aconway 2008-06-12: needs to be set to the correct exception type.
- //
- demux.close(sys::ExceptionHolder(text.empty() ? new ClosedException() : new Exception(text)));
+ 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/cpp/src/qpid/client/SessionImpl.h b/cpp/src/qpid/client/SessionImpl.h
index 55031a94ae..2f35032c4e 100644
--- a/cpp/src/qpid/client/SessionImpl.h
+++ b/cpp/src/qpid/client/SessionImpl.h
@@ -22,12 +22,13 @@
#ifndef _SessionImpl_
#define _SessionImpl_
-#include "Demux.h"
-#include "Execution.h"
-#include "Results.h"
+#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/shared_ptr.h"
+#include "qpid/SessionState.h"
#include "qpid/framing/FrameHandler.h"
#include "qpid/framing/ChannelHandler.h"
#include "qpid/framing/SequenceNumber.h"
@@ -35,7 +36,10 @@
#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 {
@@ -52,15 +56,17 @@ 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::ExecutionHandler,
+ private framing::AMQP_ClientOperations::MessageHandler
{
public:
- SessionImpl(const std::string& name, shared_ptr<ConnectionImpl>, uint16_t channel, uint64_t maxFrameSize);
+ SessionImpl(const std::string& name, boost::shared_ptr<ConnectionImpl>);
~SessionImpl();
@@ -74,33 +80,57 @@ public:
void open(uint32_t detachedLifetime);
void close();
- void resume(shared_ptr<ConnectionImpl>);
+ void resume(boost::shared_ptr<ConnectionImpl>);
void suspend();
void assertOpen() 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(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 ErrorType {
- OK,
- CONNECTION_CLOSE,
- SESSION_DETACH,
- EXCEPTION
- };
enum State {
INACTIVE,
ATTACHING,
@@ -110,12 +140,14 @@ private:
};
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;
@@ -124,13 +156,19 @@ private:
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 requestTimeout(uint32_t timeout);
void sendCompletionImpl();
@@ -139,7 +177,8 @@ private:
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 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);
@@ -160,17 +199,28 @@ private:
uint8_t fieldIndex,
const std::string& description,
const framing::FieldTable& errorInfo);
-
- ErrorType error;
- int code; // Error code
- std::string text; // Error text
+
+ // 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;
- shared_ptr<ConnectionImpl> connection;
+ boost::shared_ptr<ConnectionImpl> connection;
+
framing::FrameHandler::MemFunRef<SessionImpl, &SessionImpl::proxyOut> ioHandler;
framing::ChannelHandler channel;
framing::AMQP_ServerProxy::Session proxy;
@@ -186,6 +236,16 @@ private:
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
diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp
new file mode 100644
index 0000000000..5cdaaa4615
--- /dev/null
+++ b/cpp/src/qpid/client/SslConnector.cpp
@@ -0,0 +1,400 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#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/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/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, private sys::Runnable
+{
+ 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;
+
+ sys::Mutex closedLock;
+ bool closed;
+ bool joined;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ Writer writer;
+
+ sys::Thread receiver;
+
+ sys::ssl::SslSocket socket;
+
+ sys::ssl::SslIO* aio;
+ boost::shared_ptr<sys::Poller> poller;
+
+ ~SslConnector();
+
+ void run();
+ void handleClosed();
+ bool closeInternal();
+
+ 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&);
+
+ std::string identifier;
+
+ ConnectionImpl* impl;
+
+ void connect(const std::string& host, int 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;
+
+public:
+ SslConnector(framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+ unsigned int getSSF() { return socket.getKeyLen(); }
+};
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new SslConnector(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(ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ version(ver),
+ initiated(false),
+ closed(true),
+ joined(true),
+ shutdownHandler(0),
+ writer(maxFrameSize, cimpl),
+ aio(0),
+ impl(cimpl)
+{
+ QPID_LOG(debug, "SslConnector created for " << version.toString());
+ //TODO: how do we want to handle socket configuration with ssl?
+ //settings.configureSocket(socket);
+}
+
+SslConnector::~SslConnector() {
+ close();
+}
+
+void SslConnector::connect(const std::string& host, int port){
+ Mutex::ScopedLock l(closedLock);
+ assert(closed);
+ try {
+ socket.connect(host, port);
+ } catch (const std::exception& e) {
+ socket.close();
+ throw;
+ }
+
+ identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+ closed = false;
+ poller = Poller::shared_ptr(new Poller);
+ aio = new SslIO(socket,
+ boost::bind(&SslConnector::readbuff, this, _1, _2),
+ boost::bind(&SslConnector::eof, this, _1),
+ boost::bind(&SslConnector::eof, this, _1),
+ 0, // closed
+ 0, // nobuffs
+ boost::bind(&SslConnector::writebuff, this, _1));
+ writer.init(identifier, aio);
+}
+
+void SslConnector::init(){
+ Mutex::ScopedLock l(closedLock);
+ assert(joined);
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+ joined = false;
+ receiver = Thread(this);
+}
+
+bool SslConnector::closeInternal() {
+ Mutex::ScopedLock l(closedLock);
+ bool ret = !closed;
+ if (!closed) {
+ closed = true;
+ aio->queueForDeletion();
+ poller->shutdown();
+ }
+ if (!joined && receiver.id() != Thread::current().id()) {
+ joined = true;
+ Mutex::ScopedUnlock u(closedLock);
+ receiver.join();
+ }
+ return ret;
+}
+
+void SslConnector::close() {
+ closeInternal();
+}
+
+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);
+}
+
+void SslConnector::handleClosed() {
+ if (closeInternal() && shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+struct SslConnector::Buff : public SslIO::BufferBase {
+ Buff(size_t size) : SslIO::BufferBase(new char[size], size) {}
+ ~Buff() { delete [] bytes;}
+};
+
+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&) {
+ handleClosed();
+}
+
+void SslConnector::run(){
+ // Keep the connection impl in memory until run() completes.
+ boost::shared_ptr<ConnectionImpl> protect = impl->shared_from_this();
+ assert(protect);
+ try {
+ Dispatcher d(poller);
+
+ for (int i = 0; i < 32; i++) {
+ aio->queueReadBuffer(new Buff(maxFrameSize));
+ }
+
+ aio->start(poller);
+ d.run();
+ socket.close();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, e.what());
+ handleClosed();
+ }
+}
+
+
+}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/StateManager.cpp b/cpp/src/qpid/client/StateManager.cpp
index 0cb3c6b9d4..5462e0fed4 100644
--- a/cpp/src/qpid/client/StateManager.cpp
+++ b/cpp/src/qpid/client/StateManager.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "StateManager.h"
+#include "qpid/client/StateManager.h"
#include "qpid/framing/amqp_framing.h"
using namespace qpid::client;
@@ -60,6 +60,18 @@ void StateManager::setState(int 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);
diff --git a/cpp/src/qpid/client/StateManager.h b/cpp/src/qpid/client/StateManager.h
index b01664a0c1..3c8412dfa7 100644
--- a/cpp/src/qpid/client/StateManager.h
+++ b/cpp/src/qpid/client/StateManager.h
@@ -36,6 +36,7 @@ class StateManager
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);
diff --git a/cpp/src/qpid/client/Subscription.cpp b/cpp/src/qpid/client/Subscription.cpp
new file mode 100644
index 0000000000..988f372604
--- /dev/null
+++ b/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/cpp/src/qpid/client/SubscriptionImpl.cpp b/cpp/src/qpid/client/SubscriptionImpl.cpp
new file mode 100644
index 0000000000..a8a0b47d94
--- /dev/null
+++ b/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/cpp/src/qpid/client/SubscriptionImpl.h b/cpp/src/qpid/client/SubscriptionImpl.h
new file mode 100644
index 0000000000..da77213423
--- /dev/null
+++ b/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/cpp/src/qpid/client/SubscriptionManager.cpp b/cpp/src/qpid/client/SubscriptionManager.cpp
index b4c48f7365..7eac3c541b 100644
--- a/cpp/src/qpid/client/SubscriptionManager.cpp
+++ b/cpp/src/qpid/client/SubscriptionManager.cpp
@@ -18,129 +18,85 @@
* under the License.
*
*/
-#ifndef _Subscription_
-#define _Subscription_
-#include "SubscriptionManager.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>
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/SubscriptionManagerImpl.h"
+#include "qpid/client/PrivateImplRef.h"
namespace qpid {
namespace client {
-SubscriptionManager::SubscriptionManager(const Session& s)
- : dispatcher(s), session(s),
- flowControl(UNLIMITED, UNLIMITED, false),
- acceptMode(0), acquireMode(0),
- autoStop(true)
-{}
-
-void SubscriptionManager::subscribeInternal(
- const std::string& q, const std::string& dest, const FlowControl& fc)
-{
- session.messageSubscribe(
- arg::queue=q, arg::destination=dest,
- arg::acceptMode=acceptMode, arg::acquireMode=acquireMode);
- if (fc.messages || fc.bytes) // No need to set if all 0.
- setFlowControl(dest, fc);
-}
+typedef PrivateImplRef<SubscriptionManager> PI;
-void SubscriptionManager::subscribe(
- MessageListener& listener, const std::string& q, const std::string& d)
-{
- subscribe(listener, q, getFlowControl(), d);
-}
+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); }
-void SubscriptionManager::subscribe(
- MessageListener& listener, const std::string& q, const FlowControl& fc, const std::string& d)
-{
- std::string dest=d.empty() ? q:d;
- dispatcher.listen(dest, &listener, autoAck);
- return subscribeInternal(q, dest, fc);
-}
+Subscription SubscriptionManager::subscribe(
+ MessageListener& listener, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{ return impl->subscribe(listener, q, ss, n); }
-void SubscriptionManager::subscribe(
- LocalQueue& lq, const std::string& q, const std::string& d)
-{
- subscribe(lq, q, getFlowControl(), d);
-}
+Subscription SubscriptionManager::subscribe(
+ LocalQueue& lq, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
+{ return impl->subscribe(lq, q, ss, n); }
-void SubscriptionManager::subscribe(
- LocalQueue& lq, const std::string& q, const FlowControl& fc, const std::string& d)
-{
- std::string dest=d.empty() ? q:d;
- lq.session=session;
- lq.queue=session.getExecution().getDemux().add(dest, ByTransferDest(dest));
- return subscribeInternal(q, dest, fc);
-}
-void SubscriptionManager::setFlowControl(
- const std::string& dest, uint32_t messages, uint32_t bytes, bool window)
-{
- session.messageSetFlowMode(dest, window);
- session.messageFlow(dest, 0, messages);
- session.messageFlow(dest, 1, bytes);
- session.sync();
-}
+Subscription SubscriptionManager::subscribe(
+ MessageListener& listener, const std::string& q, const std::string& n)
+{ return impl->subscribe(listener, q, n); }
-void SubscriptionManager::setFlowControl(const std::string& dest, const FlowControl& fc) {
- setFlowControl(dest, fc.messages, fc.bytes, fc.window);
-}
-void SubscriptionManager::setFlowControl(const FlowControl& fc) { flowControl=fc; }
+Subscription SubscriptionManager::subscribe(
+ LocalQueue& lq, const std::string& q, const std::string& n)
+{ return impl->subscribe(lq, q, n); }
-void SubscriptionManager::setFlowControl(
- uint32_t messages_, uint32_t bytes_, bool window_)
-{
- setFlowControl(FlowControl(messages_, bytes_, window_));
-}
+void SubscriptionManager::cancel(const std::string& dest) { return impl->cancel(dest); }
-const FlowControl& SubscriptionManager::getFlowControl() const { return flowControl; }
+void SubscriptionManager::setAutoStop(bool set) { impl->setAutoStop(set); }
-void SubscriptionManager::setAcceptMode(bool c) { acceptMode=c; }
+void SubscriptionManager::run() { impl->run(); }
-void SubscriptionManager::setAcquireMode(bool a) { acquireMode=a; }
+void SubscriptionManager::start() { impl->start(); }
-void SubscriptionManager::setAckPolicy(const AckPolicy& a) { autoAck=a; }
+void SubscriptionManager::wait() { impl->wait(); }
-AckPolicy& SubscriptionManager::getAckPolicy() { return autoAck; }
+void SubscriptionManager::stop() { impl->stop(); }
-void SubscriptionManager::cancel(const std::string dest)
-{
- sync(session).messageCancel(dest);
- dispatcher.cancel(dest);
+bool SubscriptionManager::get(Message& result, const std::string& queue, sys::Duration timeout) {
+ return impl->get(result, queue, timeout);
}
-void SubscriptionManager::setAutoStop(bool set) { autoStop=set; }
+Message SubscriptionManager::get(const std::string& queue, sys::Duration timeout) {
+ return impl->get(queue, timeout);
+}
+
+Session SubscriptionManager::getSession() const { return impl->getSession(); }
-void SubscriptionManager::run()
-{
- dispatcher.setAutoStop(autoStop);
- dispatcher.run();
+Subscription SubscriptionManager::getSubscription(const std::string& name) const {
+ return impl->getSubscription(name);
+}
+void SubscriptionManager::registerFailoverHandler (boost::function<void ()> fh) {
+ impl->registerFailoverHandler(fh);
}
-void SubscriptionManager::stop()
-{
- dispatcher.stop();
+void SubscriptionManager::setFlowControl(const std::string& name, const FlowControl& flow) {
+ impl->setFlowControl(name, flow);
}
-bool SubscriptionManager::get(Message& result, const std::string& queue, sys::Duration timeout) {
- LocalQueue lq;
- std::string unique = framing::Uuid(true).str();
- subscribe(lq, queue, FlowControl::messageCredit(1), unique);
- AutoCancel ac(*this, 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);
+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
-#endif
+
diff --git a/cpp/src/qpid/client/SubscriptionManager.h b/cpp/src/qpid/client/SubscriptionManager.h
deleted file mode 100644
index 3dad15fd29..0000000000
--- a/cpp/src/qpid/client/SubscriptionManager.h
+++ /dev/null
@@ -1,210 +0,0 @@
-#ifndef QPID_CLIENT_SUBSCRIPTIONMANAGER_H
-#define QPID_CLIENT_SUBSCRIPTIONMANAGER_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/MessageListener.h>
-#include <qpid/client/LocalQueue.h>
-#include <qpid/client/FlowControl.h>
-#include <qpid/sys/Runnable.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
- */
-class SubscriptionManager : public sys::Runnable
-{
- typedef sys::Mutex::ScopedLock Lock;
- typedef sys::Mutex::ScopedUnlock Unlock;
-
- void subscribeInternal(const std::string& q, const std::string& dest, const FlowControl&);
-
- qpid::client::Dispatcher dispatcher;
- qpid::client::AsyncSession session;
- FlowControl flowControl;
- AckPolicy autoAck;
- bool acceptMode;
- bool acquireMode;
- bool autoStop;
-
- public:
- /** Create a new SubscriptionManager associated with a session */
- SubscriptionManager(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 flow initial FlowControl for the subscription.
- *@param tag Unique destination tag for the listener.
- * If not specified, the queue name is used.
- */
- void subscribe(MessageListener& listener,
- const std::string& queue,
- const FlowControl& flow,
- const std::string& tag=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 tag Unique destination tag for the listener.
- * If not specified, the queue name is used.
- */
- void subscribe(LocalQueue& localQueue,
- const std::string& queue,
- const FlowControl& flow,
- const std::string& tag=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 tag Unique destination tag for the listener.
- * If not specified, the queue name is used.
- */
- void subscribe(MessageListener& listener,
- const std::string& queue,
- const std::string& tag=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 tag Unique destination tag for the listener.
- * If not specified, the queue name is used.
- */
- void subscribe(LocalQueue& localQueue,
- const std::string& queue,
- const std::string& tag=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);
-
- /** Cancel a subscription. */
- void cancel(const std::string tag);
-
- /** Deliver messages until stop() is called. */
- void run();
-
- /** 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);
-
- /** Cause run() to return */
- void stop();
-
- static const uint32_t UNLIMITED=0xFFFFFFFF;
-
- /** Set the flow control for destination. */
- void setFlowControl(const std::string& destintion, const FlowControl& flow);
-
- /** Set the default initial flow control for subscriptions that do not specify it. */
- void setFlowControl(const FlowControl& flow);
-
- /** Get the default flow control for new subscriptions that do not specify it. */
- const FlowControl& getFlowControl() const;
-
- /** Set the flow control for destination tag.
- *@param tag: name of the destination.
- *@param messages: message credit.
- *@param bytes: byte credit.
- *@param window: if true use window-based flow control.
- */
- void setFlowControl(const std::string& tag, uint32_t messages, uint32_t bytes, bool window=true);
-
- /** Set the initial flow control settings to be applied to each new subscribtion.
- *@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);
-
- /** Set the accept-mode for new subscriptions. Defaults to true.
- *@param required: if true messages must be confirmed by calling
- *Message::acknowledge() or automatically, see setAckPolicy()
- */
- void setAcceptMode(bool required);
-
- /** Set the acquire-mode for new subscriptions. Defaults to false.
- *@param acquire: if false messages pre-acquired, if true
- * messages are dequed on acknowledgement or on transfer
- * depending on acceptMode.
- */
- void setAcquireMode(bool acquire);
-
- /** Set the acknowledgement policy for new subscriptions.
- * Default is to acknowledge every message automatically.
- */
- void setAckPolicy(const AckPolicy& autoAck);
- /**
- *
- */
- AckPolicy& getAckPolicy();
-};
-
-/** AutoCancel cancels a subscription in its destructor */
-class AutoCancel {
- public:
- AutoCancel(SubscriptionManager& sm_, const std::string& tag_) : sm(sm_), tag(tag_) {}
- ~AutoCancel() { sm.cancel(tag); }
- private:
- SubscriptionManager& sm;
- std::string tag;
-};
-
-}} // namespace qpid::client
-
-#endif /*!QPID_CLIENT_SUBSCRIPTIONMANAGER_H*/
diff --git a/cpp/src/qpid/client/SubscriptionManagerImpl.cpp b/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
new file mode 100644
index 0000000000..a558d90be8
--- /dev/null
+++ b/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/cpp/src/qpid/client/SubscriptionManagerImpl.h b/cpp/src/qpid/client/SubscriptionManagerImpl.h
new file mode 100644
index 0000000000..6376a05c45
--- /dev/null
+++ b/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/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp
new file mode 100644
index 0000000000..1a6e51d54d
--- /dev/null
+++ b/cpp/src/qpid/client/TCPConnector.cpp
@@ -0,0 +1,327 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/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;
+
+// Static constructor which registers connector here
+namespace {
+ Connector* create(framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) {
+ return new TCPConnector(v, s, c);
+ }
+
+ struct StaticInit {
+ StaticInit() {
+ Connector::registerFactory("tcp", &create);
+ };
+ } init;
+}
+
+struct TCPConnector::Buff : public AsynchIO::BufferBase {
+ Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {}
+ ~Buff() { delete [] bytes;}
+};
+
+TCPConnector::TCPConnector(ProtocolVersion ver,
+ const ConnectionSettings& settings,
+ ConnectionImpl* cimpl)
+ : maxFrameSize(settings.maxFrameSize),
+ lastEof(0),
+ currentSize(0),
+ bounds(cimpl),
+ version(ver),
+ initiated(false),
+ closed(true),
+ joined(true),
+ shutdownHandler(0),
+ aio(0),
+ impl(cimpl->shared_from_this())
+{
+ QPID_LOG(debug, "TCPConnector created for " << version.toString());
+ settings.configureSocket(socket);
+}
+
+TCPConnector::~TCPConnector() {
+ close();
+}
+
+void TCPConnector::connect(const std::string& host, int port) {
+ Mutex::ScopedLock l(lock);
+ assert(closed);
+ assert(joined);
+ poller = Poller::shared_ptr(new Poller);
+ AsynchConnector::create(socket,
+ poller,
+ host, port,
+ boost::bind(&TCPConnector::connected, this, _1),
+ boost::bind(&TCPConnector::connectFailed, this, _3));
+ closed = false;
+ joined = false;
+ receiver = Thread(this);
+}
+
+void TCPConnector::connected(const Socket&) {
+ aio = AsynchIO::create(socket,
+ boost::bind(&TCPConnector::readbuff, this, _1, _2),
+ boost::bind(&TCPConnector::eof, this, _1),
+ boost::bind(&TCPConnector::eof, this, _1),
+ 0, // closed
+ 0, // nobuffs
+ boost::bind(&TCPConnector::writebuff, this, _1));
+ for (int i = 0; i < 32; i++) {
+ aio->queueReadBuffer(new Buff(maxFrameSize));
+ }
+ aio->start(poller);
+
+ identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+ ProtocolInitiation init(version);
+ writeDataBlock(init);
+}
+
+void TCPConnector::connectFailed(const std::string& msg) {
+ QPID_LOG(warning, "Connecting failed: " << msg);
+ closed = true;
+ poller->shutdown();
+ closeInternal();
+ if (shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+bool TCPConnector::closeInternal() {
+ bool ret;
+ {
+ Mutex::ScopedLock l(lock);
+ ret = !closed;
+ if (!closed) {
+ closed = true;
+ aio->queueForDeletion();
+ poller->shutdown();
+ }
+ if (joined || receiver.id() == Thread::current().id()) {
+ return ret;
+ }
+ joined = true;
+ }
+ receiver.join();
+ return ret;
+}
+
+void TCPConnector::close() {
+ closeInternal();
+}
+
+void TCPConnector::abort() {
+ // Can't abort a closed connection
+ if (!closed) {
+ if (aio) {
+ // Established connection
+ aio->requestCallback(boost::bind(&TCPConnector::eof, this, _1));
+ } else {
+ // We're still connecting
+ 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) {
+ 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();
+ bool notifyWrite = false;
+ if (frame.getEof()) {
+ lastEof = frames.size();
+ notifyWrite = true;
+ } else {
+ notifyWrite = (currentSize >= maxFrameSize);
+ }
+ if (notifyWrite && !closed) aio->notifyPendingWrite();
+}
+
+void TCPConnector::handleClosed() {
+ if (closeInternal() && shutdownHandler)
+ shutdownHandler->shutdown();
+}
+
+void TCPConnector::writebuff(AsynchIO& /*aio*/)
+{
+ 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 = new Buff(maxFrameSize);
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void TCPConnector::eof(AsynchIO&) {
+ handleClosed();
+}
+
+void TCPConnector::run() {
+ // Keep the connection impl in memory until run() completes.
+ boost::shared_ptr<ConnectionImpl> protect = impl.lock();
+ assert(protect);
+ try {
+ Dispatcher d(poller);
+
+ d.run();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, QPID_MSG("FAIL " << identifier << ": " << e.what()));
+ handleClosed();
+ }
+ try {
+ socket.close();
+ } catch (const std::exception&) {}
+}
+
+void TCPConnector::activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer> sl)
+{
+ securityLayer = sl;
+ securityLayer->init(this);
+}
+
+
+}} // namespace qpid::client
diff --git a/cpp/src/qpid/client/TCPConnector.h b/cpp/src/qpid/client/TCPConnector.h
new file mode 100644
index 0000000000..6dc07d1f5d
--- /dev/null
+++ b/cpp/src/qpid/client/TCPConnector.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 _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 client {
+
+class TCPConnector : public Connector, public sys::Codec, private sys::Runnable
+{
+ 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;
+ bool joined;
+
+ sys::ShutdownHandler* shutdownHandler;
+ framing::InputHandler* input;
+ framing::InitiationHandler* initialiser;
+ framing::OutputHandler* output;
+
+ sys::Thread receiver;
+
+ sys::Socket socket;
+
+ sys::AsynchIO* aio;
+ std::string identifier;
+ boost::shared_ptr<sys::Poller> poller;
+ std::auto_ptr<qpid::sys::SecurityLayer> securityLayer;
+
+ ~TCPConnector();
+
+ void run();
+ void handleClosed();
+ bool closeInternal();
+
+ virtual void connected(const qpid::sys::Socket&);
+ void connectFailed(const std::string& msg);
+ bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void writebuff(qpid::sys::AsynchIO&);
+ void writeDataBlock(const framing::AMQDataBlock& data);
+ void eof(qpid::sys::AsynchIO&);
+
+ boost::weak_ptr<ConnectionImpl> impl;
+
+ void connect(const std::string& host, int port);
+ 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>);
+
+ size_t decode(const char* buffer, size_t size);
+ size_t encode(const char* buffer, size_t size);
+ bool canEncode();
+
+public:
+ TCPConnector(framing::ProtocolVersion pVersion,
+ const ConnectionSettings&,
+ ConnectionImpl*);
+ unsigned int getSSF() { return 0; }
+};
+
+}} // namespace qpid::client
+
+#endif /* _TCPConnector_ */
diff --git a/cpp/src/qpid/client/TypedResult.h b/cpp/src/qpid/client/TypedResult.h
deleted file mode 100644
index 5306997d74..0000000000
--- a/cpp/src/qpid/client/TypedResult.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#ifndef _TypedResult_
-#define _TypedResult_
-
-#include "Completion.h"
-
-namespace qpid {
-namespace client {
-
-/**
- * Returned by asynchronous commands that return a result.
- * You can use get() to wait for completion and get the result value.
- * \ingroup clientapi
- */
-template <class T> class TypedResult : public Completion
-{
- T result;
- bool decoded;
-
-public:
- ///@internal
- TypedResult(Future f, shared_ptr<SessionImpl> s) : Completion(f, s), decoded(false) {}
-
- /**
- * Wait for the asynchronous command that returned this TypedResult to complete
- * and return its result.
- *
- *@return The result returned by the command.
- *@exception If the command returns an error, get() throws an exception.
- *
- */
- T& get()
- {
- if (!decoded) {
- future.decodeResult(result, *session);
- decoded = true;
- }
-
- return result;
- }
-};
-
-}}
-
-#endif
diff --git a/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp b/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
new file mode 100644
index 0000000000..80be5c56f3
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/AcceptTracker.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 "AcceptTracker.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+void AcceptTracker::State::accept()
+{
+ unconfirmed.add(unaccepted);
+ unaccepted.clear();
+}
+
+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::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/cpp/src/qpid/client/amqp0_10/AcceptTracker.h b/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
new file mode 100644
index 0000000000..fb58a3a8c8
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
@@ -0,0 +1,85 @@
+#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 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 accpet has been issued but not
+ * yet confirmed as completed
+ */
+ qpid::framing::SequenceSet unconfirmed;
+
+ void accept();
+ 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/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
new file mode 100644
index 0000000000..b70e67d12f
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -0,0 +1,819 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/client/amqp0_10/Codecs.h"
+#include "qpid/client/amqp0_10/CodecsInternal.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/messaging/Variant.h"
+#include "qpid/Exception.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::InvalidAddress;
+using qpid::messaging::Variant;
+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::framing::message;
+using namespace boost::assign;
+
+
+namespace{
+const Variant EMPTY_VARIANT;
+const FieldTable EMPTY_FIELD_TABLE;
+const std::string EMPTY_STRING;
+
+//option names
+const std::string BROWSE("browse");
+const std::string EXCLUSIVE("exclusive");
+const std::string NO_LOCAL("no-local");
+const std::string FILTER("filter");
+const std::string RELIABILITY("reliability");
+const std::string NAME("subscription-name");
+const std::string NODE_PROPERTIES("node-properties");
+const std::string X_PROPERTIES("x-properties");
+
+//policy types
+const std::string CREATE("create");
+const std::string ASSERT("assert");
+const std::string DELETE("delete");
+//policy values
+const std::string ALWAYS("always");
+const std::string NEVER("never");
+const std::string RECEIVER("receiver");
+const std::string SENDER("sender");
+
+const std::string QUEUE_ADDRESS("queue");
+const std::string TOPIC_ADDRESS("topic");
+
+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");
+const std::string DURABLE_SUBSCRIPTION("durable");
+const std::string DURABLE("durable");
+
+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("*");
+}
+
+//some amqp 0-10 specific options
+namespace xamqp{
+const std::string AUTO_DELETE("auto-delete");
+const std::string EXCHANGE_TYPE("type");
+const std::string EXCLUSIVE("exclusive");
+const std::string ALTERNATE_EXCHANGE("alternate-exchange");
+const std::string QUEUE_ARGUMENTS("x-queue-arguments");
+const std::string SUBSCRIBE_ARGUMENTS("x-subscribe-arguments");
+}
+
+class Node
+{
+ protected:
+ enum CheckMode {FOR_RECEIVER, FOR_SENDER};
+
+ Node(const Address& address);
+
+ const std::string name;
+ Variant createPolicy;
+ Variant assertPolicy;
+ Variant deletePolicy;
+
+ 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:
+ bool durable;
+ bool autoDelete;
+ bool exclusive;
+ std::string alternateExchange;
+ FieldTable arguments;
+
+ void configure(const Address&);
+};
+
+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);
+ const std::string& getDesiredExchangeType() { return type; }
+
+ private:
+ std::string type;
+ bool typeSpecified;
+ bool durable;
+ bool autoDelete;
+ std::string alternateExchange;
+ FieldTable arguments;
+
+ void configure(const Address&);
+};
+
+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& exchangeType="");
+ void subscribe(qpid::client::AsyncSession& session, const std::string& destination);
+ void cancel(qpid::client::AsyncSession& session, const std::string& destination);
+ private:
+ struct Binding
+ {
+ Binding(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE);
+
+ std::string exchange;
+ std::string key;
+ FieldTable options;
+ };
+
+ typedef std::vector<Binding> Bindings;
+
+ const std::string queue;
+ const bool reliable;
+ const bool durable;
+ FieldTable queueOptions;
+ FieldTable subscriptionOptions;
+ Bindings bindings;
+
+ void bindSpecial(const std::string& exchangeType);
+ void bind(const std::string& subject);
+ void bind(const std::string& subject, const Variant& filter);
+ void bind(const std::string& subject, const Variant::Map& filter);
+ void bind(const std::string& subject, const Variant::List& filter);
+ void add(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE);
+ static std::string getSubscriptionName(const std::string& base, const Variant& 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;
+}
+
+bool getReceiverPolicy(const Address& address, const std::string& key)
+{
+ return in(address.getOption(key), list_of<std::string>(ALWAYS)(RECEIVER));
+}
+
+bool getSenderPolicy(const Address& address, const std::string& key)
+{
+ return in(address.getOption(key), list_of<std::string>(ALWAYS)(SENDER));
+}
+
+bool is_unreliable(const Address& address)
+{
+ return in(address.getOption(RELIABILITY), list_of<std::string>(UNRELIABLE)(AT_MOST_ONCE));
+}
+
+bool is_reliable(const Address& address)
+{
+ return in(address.getOption(RELIABILITY), list_of<std::string>(AT_LEAST_ONCE)(EXACTLY_ONCE));
+}
+
+std::string checkAddressType(qpid::client::Session session, const Address& address)
+{
+ std::string type = address.getType();
+ 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 InvalidAddress("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::auto_ptr<MessageSource> source(new Subscription(address));
+ 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 InvalidAddress("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 InvalidAddress("Unrecognised type: " + type);
+ }
+}
+
+const Variant& getNestedOption(const Variant::Map& options, const std::vector<std::string>& keys, size_t index = 0)
+{
+ Variant::Map::const_iterator i = options.find(keys[index]);
+ if (i == options.end()) {
+ return EMPTY_VARIANT;
+ } else if (index+1 < keys.size()) {
+ return getNestedOption(i->second.asMap(), keys, index+1);
+ } else {
+ return i->second;
+ }
+}
+
+QueueSource::QueueSource(const Address& address) :
+ Queue(address),
+ acceptMode(is_unreliable(address) ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT),
+ acquireMode(address.getOption(BROWSE).asBool() ? ACQUIRE_MODE_NOT_ACQUIRED : ACQUIRE_MODE_PRE_ACQUIRED),
+ exclusive(false)
+{
+ //extract subscription arguments from address options
+ const Variant& x = address.getOption(X_PROPERTIES);
+ if (!x.isVoid()) {
+ const Variant::Map& xProps = x.asMap();
+ Variant::Map passthrough;
+ for (Variant::Map::const_iterator i = xProps.begin(); i != xProps.end(); ++i) {
+ if (i->first == xamqp::EXCLUSIVE) exclusive = i->second;
+ else passthrough[i->first] = i->second;
+ }
+ translate(passthrough, options);
+ }
+}
+
+void QueueSource::subscribe(qpid::client::AsyncSession& session, const std::string& destination)
+{
+ checkCreate(session, FOR_RECEIVER);
+ checkAssert(session, FOR_RECEIVER);
+ 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)
+{
+ session.messageCancel(destination);
+ checkDelete(session, FOR_RECEIVER);
+}
+
+std::string Subscription::getSubscriptionName(const std::string& base, const Variant& name)
+{
+ if (name.isVoid()) {
+ return (boost::format("%1%_%2%") % base % Uuid(true).str()).str();
+ } else {
+ return (boost::format("%1%_%2%") % base % name.asString()).str();
+ }
+}
+
+Subscription::Subscription(const Address& address, const std::string& exchangeType)
+ : Exchange(address),
+ queue(getSubscriptionName(name, address.getOption(NAME))),
+ reliable(is_reliable(address)),
+ durable(address.getOption(DURABLE_SUBSCRIPTION).asBool())
+{
+ if (address.getOption(NO_LOCAL).asBool()) queueOptions.setInt(NO_LOCAL, 1);
+ const Variant& x = address.getOption(X_PROPERTIES);
+ if (!x.isVoid()) {
+ const Variant::Map& xProps = x.asMap();
+ Variant::Map passthrough;
+ for (Variant::Map::const_iterator i = xProps.begin(); i != xProps.end(); ++i) {
+ if (i->first == xamqp::QUEUE_ARGUMENTS) convert(i->second.asMap(), queueOptions);
+ else passthrough[i->first] = i->second;
+ }
+ translate(passthrough, subscriptionOptions);
+ }
+
+ const Variant& filter = address.getOption(FILTER);
+ if (!filter.isVoid()) {
+ bind(address.getSubject(), filter);
+ } else if (address.hasSubject()) {
+ //Note: This will not work for headers- or xml- exchange;
+ //fanout exchange will do no filtering.
+ //TODO: for headers- or xml- exchange can construct a match
+ //for the subject in the application-headers
+ bind(address.getSubject());
+ } else {
+ //Neither a subject nor a filter has been defined, treat this
+ //as wanting to match all messages (Note: direct exchange is
+ //currently unable to support this case).
+ if (!exchangeType.empty()) bindSpecial(exchangeType);
+ else if (!getDesiredExchangeType().empty()) bindSpecial(getDesiredExchangeType());
+ }
+}
+
+void Subscription::add(const std::string& exchange, const std::string& key, const FieldTable& options)
+{
+ bindings.push_back(Binding(exchange, key, options));
+}
+
+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);
+ //bind subscription queue to exchange:
+ for (Bindings::const_iterator i = bindings.begin(); i != bindings.end(); ++i) {
+ session.exchangeBind(arg::queue=queue, arg::exchange=i->exchange, arg::bindingKey=i->key, arg::arguments=i->options);
+ }
+ //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)
+{
+ session.messageCancel(destination);
+ session.queueDelete(arg::queue=queue);
+ checkDelete(session, FOR_RECEIVER);
+}
+
+Subscription::Binding::Binding(const std::string& e, const std::string& k, const FieldTable& o):
+ exchange(e), key(k), options(o) {}
+
+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);
+}
+
+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&)
+{
+ 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);
+}
+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&)
+{
+ 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;
+ }
+}
+
+void Subscription::bind(const std::string& subject)
+{
+ add(name, subject);
+}
+
+void Subscription::bind(const std::string& subject, const Variant& filter)
+{
+ switch (filter.getType()) {
+ case qpid::messaging::VAR_MAP:
+ bind(subject, filter.asMap());
+ break;
+ case qpid::messaging::VAR_LIST:
+ bind(subject, filter.asList());
+ break;
+ default:
+ //TODO: if both subject _and_ filter are specified, combine in
+ //some way; for now we just ignore the subject in that case.
+ add(name, filter.asString());
+ break;
+ }
+}
+
+void Subscription::bind(const std::string& subject, const Variant::Map& filter)
+{
+ qpid::framing::FieldTable arguments;
+ translate(filter, arguments);
+ add(name, subject.empty() ? queue : subject, arguments);
+}
+
+void Subscription::bind(const std::string& subject, const Variant::List& filter)
+{
+ for (Variant::List::const_iterator i = filter.begin(); i != filter.end(); ++i) {
+ bind(subject, *i);
+ }
+}
+
+void Subscription::bindSpecial(const std::string& exchangeType)
+{
+ if (exchangeType == TOPIC_EXCHANGE) {
+ add(name, WILDCARD_ANY);
+ } else if (exchangeType == FANOUT_EXCHANGE) {
+ add(name, queue);
+ } else if (exchangeType == HEADERS_EXCHANGE) {
+ //TODO: add special binding for headers exchange to match all messages
+ } else if (exchangeType == XML_EXCHANGE) {
+ //TODO: add special binding for xml exchange to match all messages
+ } else { //E.g. direct
+ throw qpid::Exception(QPID_MSG("Cannot create binding to match all messages for exchange of type " << exchangeType));
+ }
+}
+
+Node::Node(const Address& address) : name(address.getName()),
+ createPolicy(address.getOption(CREATE)),
+ assertPolicy(address.getOption(ASSERT)),
+ deletePolicy(address.getOption(DELETE)) {}
+
+Queue::Queue(const Address& a) : Node(a),
+ durable(false),
+ autoDelete(false),
+ exclusive(false)
+{
+ configure(a);
+}
+
+void Queue::checkCreate(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(createPolicy, mode)) {
+ QPID_LOG(debug, "Auto-creating queue '" << name << "'");
+ try {
+ sync(session).queueDeclare(arg::queue=name,
+ arg::durable=durable,
+ arg::autoDelete=autoDelete,
+ arg::exclusive=exclusive,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=arguments);
+ } catch (const qpid::Exception& e) {
+ throw InvalidAddress((boost::format("Could not create 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 InvalidAddress((boost::format("Queue %1% does not exist") % name).str());
+ } catch (const std::exception& e) {
+ throw InvalidAddress(e.what());
+ }
+ }
+}
+
+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 InvalidAddress((boost::format("Queue not found: %1%") % name).str());
+ } else {
+ if (durable && !result.getDurable()) {
+ throw InvalidAddress((boost::format("Queue not durable: %1%") % name).str());
+ }
+ if (autoDelete && !result.getAutoDelete()) {
+ throw InvalidAddress((boost::format("Queue not set to auto-delete: %1%") % name).str());
+ }
+ if (exclusive && !result.getExclusive()) {
+ throw InvalidAddress((boost::format("Queue not exclusive: %1%") % name).str());
+ }
+ if (!alternateExchange.empty() && result.getAlternateExchange() != alternateExchange) {
+ throw InvalidAddress((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 InvalidAddress((boost::format("Option %1% not set for %2%") % i->first % name).str());
+ } else if (*i->second != *v) {
+ throw InvalidAddress((boost::format("Option %1% does not match for %2%, expected %3%, got %4%")
+ % i->first % name % *(i->second) % *v).str());
+ }
+ }
+ }
+ }
+}
+
+void Queue::configure(const Address& address)
+{
+ const Variant& v = address.getOption(NODE_PROPERTIES);
+ if (!v.isVoid()) {
+ Variant::Map nodeProps = v.asMap();
+ durable = nodeProps[DURABLE];
+ Variant::Map::const_iterator x = nodeProps.find(X_PROPERTIES);
+ if (x != nodeProps.end()) {
+ const Variant::Map& xProps = x->second.asMap();
+ Variant::Map passthrough;
+ for (Variant::Map::const_iterator i = xProps.begin(); i != xProps.end(); ++i) {
+ if (i->first == xamqp::AUTO_DELETE) autoDelete = i->second;
+ else if (i->first == xamqp::EXCLUSIVE) exclusive = i->second;
+ else if (i->first == xamqp::ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ else passthrough[i->first] = i->second;
+ }
+ translate(passthrough, arguments);
+ }
+ }
+}
+
+Exchange::Exchange(const Address& a) : Node(a),
+ type(TOPIC_EXCHANGE),
+ typeSpecified(false),
+ durable(false),
+ autoDelete(false)
+{
+ configure(a);
+}
+
+void Exchange::checkCreate(qpid::client::AsyncSession& session, CheckMode mode)
+{
+ if (enabled(createPolicy, mode)) {
+ try {
+ sync(session).exchangeDeclare(arg::exchange=name,
+ arg::type=type,
+ arg::durable=durable,
+ arg::autoDelete=autoDelete,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=arguments);
+ } catch (const qpid::Exception& e) {
+ throw InvalidAddress((boost::format("Could not create 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 InvalidAddress((boost::format("Exchange %1% does not exist") % name).str());
+ } catch (const std::exception& e) {
+ throw InvalidAddress(e.what());
+ }
+ }
+}
+
+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 InvalidAddress((boost::format("Exchange not found: %1%") % name).str());
+ } else {
+ if (typeSpecified && result.getType() != type) {
+ throw InvalidAddress((boost::format("Exchange %1% is of incorrect type, expected %2% but got %3%")
+ % name % type % result.getType()).str());
+ }
+ if (durable && !result.getDurable()) {
+ throw InvalidAddress((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 InvalidAddress((boost::format("Option %1% not set for %2%") % i->first % name).str());
+ } else if (i->second != v) {
+ throw InvalidAddress((boost::format("Option %1% does not match for %2%, expected %3%, got %4%")
+ % i->first % name % *(i->second) % *v).str());
+ }
+ }
+ }
+ }
+}
+
+void Exchange::configure(const Address& address)
+{
+ const Variant& v = address.getOption(NODE_PROPERTIES);
+ if (!v.isVoid()) {
+ Variant::Map nodeProps = v.asMap();
+ durable = nodeProps[DURABLE];
+ Variant::Map::const_iterator x = nodeProps.find(X_PROPERTIES);
+ if (x != nodeProps.end()) {
+ const Variant::Map& xProps = x->second.asMap();
+ Variant::Map passthrough;
+ for (Variant::Map::const_iterator i = xProps.begin(); i != xProps.end(); ++i) {
+ if (i->first == xamqp::AUTO_DELETE) autoDelete = i->second;
+ else if (i->first == xamqp::EXCHANGE_TYPE) { type = i->second.asString(); typeSpecified = true; }
+ else if (i->first == xamqp::ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ else passthrough[i->first] = i->second;
+ }
+ translate(passthrough, arguments);
+ }
+ }
+}
+
+
+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 = address.getOption(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);
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/cpp/src/qpid/client/amqp0_10/AddressResolution.h
new file mode 100644
index 0000000000..01c8c51595
--- /dev/null
+++ b/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/messaging/Variant.h"
+#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&);
+
+ private:
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H*/
diff --git a/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/cpp/src/qpid/client/amqp0_10/Codecs.cpp
new file mode 100644
index 0000000000..ff72dfbf4e
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/Codecs.cpp
@@ -0,0 +1,299 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/messaging/Variant.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 <algorithm>
+#include <functional>
+
+using namespace qpid::framing;
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+namespace {
+const std::string iso885915("iso-8859-15");
+const std::string utf8("utf8");
+const std::string utf16("utf16");
+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;
+ }
+}
+
+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, 1>(); break;
+ case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break;
+ case 0x04: break; //TODO: iso-8859-15 char
+ case 0x08: out = in->getIntegerValue<bool, 1>(); break;
+ case 0x010: out.setEncoding(amqp0_10_binary);
+ case 0x011: out = in->getIntegerValue<int16_t, 2>(); break;
+ case 0x012: out = in->getIntegerValue<uint16_t, 2>(); break;
+ case 0x020: out.setEncoding(amqp0_10_binary);
+ case 0x021: out = in->getIntegerValue<int32_t, 4>(); break;
+ case 0x022: out = in->getIntegerValue<uint32_t, 4>(); break;
+ case 0x023: out = in->get<float>(); break;
+ case 0x027: break; //TODO: utf-32 char
+ case 0x030: out.setEncoding(amqp0_10_binary);
+ case 0x031: out = in->getIntegerValue<int64_t, 8>(); break;
+ case 0x038: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding
+ case 0x032: out = in->getIntegerValue<uint64_t, 8>(); break;
+ case 0x033:out = in->get<double>(); 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:
+ setEncodingFor(out, in->getType());
+ out = in->get<std::string>();
+ 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> 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;
+ //TODO: check encoding (and length?) when deciding what AMQP type to treat string as
+ case VAR_STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break;
+ case VAR_MAP:
+ //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<FieldTableValue>(in.asMap(), &toFieldTableEntry));
+ out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap()));
+ break;
+ case VAR_LIST:
+ //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<ListValue>(in.asList(), &toFieldValue));
+ 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& value, std::string& data)
+{
+ _encode<FieldTable>(value.asMap(), data, &toFieldTableEntry);
+}
+
+void MapCodec::decode(const std::string& data, Variant& value)
+{
+ value = Variant::Map();
+ _decode<FieldTable>(data, value.asMap(), &toVariantMapEntry);
+}
+
+void ListCodec::encode(const Variant& value, std::string& data)
+{
+ _encode<List>(value.asList(), data, &toFieldValue);
+}
+
+void ListCodec::decode(const std::string& data, Variant& value)
+{
+ value = Variant::List();
+ _decode<List>(data, value.asList(), &toVariant);
+}
+
+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::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/CodecsInternal.h b/cpp/src/qpid/client/amqp0_10/CodecsInternal.h
new file mode 100644
index 0000000000..b5a561a9c3
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/CodecsInternal.h
@@ -0,0 +1,41 @@
+#ifndef QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H
+#define QPID_CLIENT_AMQP0_10_CODECSINTERNAL_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/Variant.h"
+#include "qpid/framing/FieldTable.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+/**
+ * Declarations of a couple of conversion functions implemented in
+ * Codecs.cpp but not exposed through API
+ */
+
+void translate(const qpid::messaging::Variant::Map& from, qpid::framing::FieldTable& to);
+void translate(const qpid::framing::FieldTable& from, qpid::messaging::Variant::Map& to);
+
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_CODECSINTERNAL_H*/
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
new file mode 100644
index 0000000000..cd5c0214e3
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
@@ -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.
+ *
+ */
+#include "ConnectionImpl.h"
+#include "SessionImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include <boost/intrusive_ptr.hpp>
+#include <vector>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Variant;
+using qpid::framing::Uuid;
+using namespace qpid::sys;
+
+template <class T> void setIfFound(const Variant::Map& map, const std::string& key, T& value)
+{
+ Variant::Map::const_iterator i = map.find(key);
+ if (i != map.end()) {
+ value = (T) i->second;
+ }
+}
+
+void convert(const Variant::Map& from, ConnectionSettings& to)
+{
+ setIfFound(from, "username", to.username);
+ setIfFound(from, "password", to.password);
+ setIfFound(from, "sasl-mechanism", to.mechanism);
+ setIfFound(from, "sasl-service", to.service);
+ setIfFound(from, "sasl-min-ssf", to.minSsf);
+ setIfFound(from, "sasl-max-ssf", to.maxSsf);
+
+ setIfFound(from, "heartbeat", to.heartbeat);
+ setIfFound(from, "tcp-nodelay", to.tcpNoDelay);
+
+ setIfFound(from, "locale", to.locale);
+ setIfFound(from, "max-channels", to.maxChannels);
+ setIfFound(from, "max-frame-size", to.maxFrameSize);
+ setIfFound(from, "bounds", to.bounds);
+}
+
+ConnectionImpl::ConnectionImpl(const std::string& u, const Variant::Map& options) :
+ url(u), reconnectionEnabled(true), timeout(-1),
+ minRetryInterval(1), maxRetryInterval(30)
+{
+ QPID_LOG(debug, "Opening connection to " << url << " with " << options);
+ convert(options, settings);
+ setIfFound(options, "reconnection-enabled", reconnectionEnabled);
+ setIfFound(options, "reconnection-timeout", timeout);
+ setIfFound(options, "min-retry-interval", minRetryInterval);
+ setIfFound(options, "max-retry-interval", maxRetryInterval);
+ connection.open(url, settings);
+}
+
+void ConnectionImpl::close()
+{
+ std::vector<std::string> names;
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (Sessions::const_iterator i = sessions.begin(); i != sessions.end(); ++i) names.push_back(i->first);
+ }
+ for (std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
+ getSession(*i).close();
+ }
+
+ qpid::sys::Mutex::ScopedLock l(lock);
+ connection.close();
+}
+
+boost::intrusive_ptr<SessionImpl> getImplPtr(qpid::messaging::Session& session)
+{
+ return boost::dynamic_pointer_cast<SessionImpl>(
+ qpid::client::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));
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ sessions[name] = impl;
+ }
+ try {
+ getImplPtr(impl)->setSession(connection.newSession(name));
+ } catch (const TransportFailure&) {
+ reconnect();
+ }
+ return impl;
+}
+
+void ConnectionImpl::reconnect()
+{
+ AbsTime start = now();
+ ScopedLock<Semaphore> l(semaphore);
+ if (!connection.isOpen()) connect(start);
+}
+
+bool expired(const AbsTime& start, int timeout)
+{
+ if (timeout == 0) return true;
+ if (timeout < 0) return false;
+ Duration used(start, now());
+ Duration allowed = timeout * TIME_SEC;
+ return allowed > used;
+}
+
+void ConnectionImpl::connect(const AbsTime& started)
+{
+ for (int i = minRetryInterval; !tryConnect(); i = std::min(i * 2, maxRetryInterval)) {
+ if (expired(started, timeout)) throw TransportFailure();
+ else qpid::sys::sleep(i);
+ }
+}
+
+bool ConnectionImpl::tryConnect()
+{
+ if (tryConnect(url) ||
+ (failoverListener.get() && tryConnect(failoverListener->getKnownBrokers())))
+ {
+ return resetSessions();
+ } else {
+ return false;
+ }
+}
+
+bool ConnectionImpl::tryConnect(const Url& u)
+{
+ try {
+ QPID_LOG(info, "Trying to connect to " << url << "...");
+ connection.open(u, settings);
+ failoverListener.reset(new FailoverListener(connection));
+ return true;
+ } catch (const Exception& e) {
+ //TODO: need to fix timeout on open so that it throws TransportFailure
+ QPID_LOG(info, "Failed to connect to " << u << ": " << e.what());
+ }
+ return false;
+}
+
+bool ConnectionImpl::tryConnect(const std::vector<Url>& urls)
+{
+ for (std::vector<Url>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
+ if (tryConnect(*i)) return true;
+ }
+ return false;
+}
+
+bool ConnectionImpl::resetSessions()
+{
+ 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 TransportFailure&) {
+ QPID_LOG(debug, "Connection failed while re-inialising sessions");
+ return false;
+ }
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
new file mode 100644
index 0000000000..979cc6c82a
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
@@ -0,0 +1,72 @@
+#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/messaging/Variant.h"
+#include "qpid/Url.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Semaphore.h"
+#include <map>
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+class SessionImpl;
+
+class ConnectionImpl : public qpid::messaging::ConnectionImpl
+{
+ public:
+ ConnectionImpl(const std::string& url, const qpid::messaging::Variant::Map& options);
+ 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 reconnect();
+ 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::auto_ptr<FailoverListener> failoverListener;
+ qpid::Url url;
+ qpid::client::ConnectionSettings settings;
+ bool reconnectionEnabled;
+ int timeout;
+ int minRetryInterval;
+ int maxRetryInterval;
+
+ void connect(const qpid::sys::AbsTime& started);
+ bool tryConnect();
+ bool tryConnect(const std::vector<Url>& urls);
+ bool tryConnect(const Url&);
+ bool resetSessions();
+};
+}}} // namespace qpid::client::amqp0_10
+
+#endif /*!QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H*/
diff --git a/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
new file mode 100644
index 0000000000..e66dc5915c
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
@@ -0,0 +1,303 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/client/amqp0_10/IncomingMessages.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/client/amqp0_10/CodecsInternal.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/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+#include "qpid/messaging/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 qpid::sys::AbsTime;
+using qpid::sys::Duration;
+using qpid::messaging::MessageImplAccess;
+using qpid::messaging::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)
+{
+ session = s;
+ incoming = SessionBase_0_10Access(session).get()->getDemux().getDefault();
+ acceptTracker.reset();
+}
+
+bool IncomingMessages::get(Handler& handler, Duration timeout)
+{
+ //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)
+{
+ //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()
+{
+ acceptTracker.accept(session);
+}
+
+void IncomingMessages::releaseAll()
+{
+ //first process any received messages...
+ 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
+ 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...
+ 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;
+ 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");
+ received.push_back(content);
+ }
+ } else {
+ //TODO: handle other types of commands (e.g. message-accept, message-flow etc)
+ }
+ }
+ 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");
+ 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()
+{
+ return acceptTracker.acceptsPending();
+}
+uint32_t IncomingMessages::pendingAccept(const std::string& destination)
+{
+ 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
+ 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
+ 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);
+}
+
+void populateHeaders(qpid::messaging::Message& message,
+ const DeliveryProperties* deliveryProperties,
+ const MessageProperties* messageProperties)
+{
+ if (deliveryProperties) {
+ message.setSubject(deliveryProperties->getRoutingKey());
+ //TODO: convert other delivery properties
+ }
+ if (messageProperties) {
+ message.setContentType(messageProperties->getContentType());
+ if (messageProperties->hasReplyTo()) {
+ message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo()));
+ }
+ message.getHeaders().clear();
+ translate(messageProperties->getApplicationHeaders(), message.getHeaders());
+ //TODO: convert other message properties
+ }
+}
+
+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());
+
+ command.getContent(message.getContent());
+
+ populateHeaders(message, command.getHeaders());
+}
+
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
new file mode 100644
index 0000000000..2bc6dd49c4
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
@@ -0,0 +1,98 @@
+#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 {
+
+/**
+ *
+ */
+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 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;
+
+ 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/cpp/src/qpid/client/AckPolicy.h b/cpp/src/qpid/client/amqp0_10/MessageSink.h
index b34f1d15d1..8d87a3c7bb 100644
--- a/cpp/src/qpid/client/AckPolicy.h
+++ b/cpp/src/qpid/client/amqp0_10/MessageSink.h
@@ -1,7 +1,8 @@
-#ifndef QPID_CLIENT_ACKPOLICY_H
-#define QPID_CLIENT_ACKPOLICY_H
+#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
@@ -9,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -20,39 +21,32 @@
* under the License.
*
*/
-
-#include "qpid/framing/SequenceSet.h"
+#include <string>
#include "qpid/client/AsyncSession.h"
-#include "qpid/client/Message.h"
namespace qpid {
+
+namespace messaging {
+class Message;
+}
+
namespace client {
+namespace amqp0_10 {
+
+struct OutgoingMessage;
/**
- * Policy for automatic acknowledgement of messages.
- *
*
- * \ingroup clientapi
*/
-class AckPolicy
+class MessageSink
{
- framing::SequenceSet accepted;
- size_t interval;
- size_t count;
-
public:
- /**
- * Sends accepts and marks completion of received transfers.
- *
- *@param n: acknowledge every n messages.
- *n==0 means no automatic acknowledgement.
- */
- AckPolicy(size_t n=1);
- void ack(const Message& msg, AsyncSession session);
- void ackOutstanding(AsyncSession session);};
-
-}} // namespace qpid::client
-
-
-
-#endif /*!QPID_CLIENT_ACKPOLICY_H*/
+ 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/cpp/src/qpid/client/amqp0_10/MessageSource.h b/cpp/src/qpid/client/amqp0_10/MessageSource.h
new file mode 100644
index 0000000000..74f2732f59
--- /dev/null
+++ b/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/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
new file mode 100644
index 0000000000..abd4f4c28c
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.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/client/amqp0_10/OutgoingMessage.h"
+#include "qpid/client/amqp0_10/AddressResolution.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+#include "qpid/client/amqp0_10/CodecsInternal.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Address;
+using qpid::messaging::MessageImplAccess;
+
+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());
+ const Address& address = from.getReplyTo();
+ if (address) {
+ message.getMessageProperties().setReplyTo(AddressResolution::convert(address));
+ }
+ translate(from.getHeaders(), message.getMessageProperties().getApplicationHeaders());
+ //TODO: set other message properties
+ message.getDeliveryProperties().setRoutingKey(from.getSubject());
+ //TODO: set other delivery properties
+}
+
+namespace {
+const std::string SUBJECT("subject");
+}
+
+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/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
new file mode 100644
index 0000000000..0cdd2a2336
--- /dev/null
+++ b/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/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp
new file mode 100644
index 0000000000..bc5c53fde6
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/ReceiverImpl.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 "ReceiverImpl.h"
+#include "AddressResolution.h"
+#include "MessageSource.h"
+#include "SessionImpl.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+using qpid::messaging::Receiver;
+
+void ReceiverImpl::received(qpid::messaging::Message&)
+{
+ //TODO: should this be configurable
+ if (capacity && --window <= capacity/2) {
+ session.sendCompletion();
+ window = capacity;
+ }
+}
+
+qpid::messaging::Message ReceiverImpl::get(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!get(result, timeout)) throw Receiver::NoMessageAvailable();
+ return result;
+}
+
+qpid::messaging::Message ReceiverImpl::fetch(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Message result;
+ if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable();
+ return result;
+}
+
+bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ Get f(*this, message, timeout);
+ while (!parent.execute(f)) {}
+ return f.result;
+}
+
+bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ Fetch f(*this, message, timeout);
+ while (!parent.execute(f)) {}
+ return f.result;
+}
+
+void ReceiverImpl::cancel()
+{
+ execute<Cancel>();
+}
+
+void ReceiverImpl::start()
+{
+ if (state == STOPPED) {
+ state = STARTED;
+ startFlow();
+ }
+}
+
+void ReceiverImpl::stop()
+{
+ state = STOPPED;
+ session.messageStop(destination);
+}
+
+void ReceiverImpl::setCapacity(uint32_t c)
+{
+ execute1<SetCapacity>(c);
+}
+
+void ReceiverImpl::startFlow()
+{
+ 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)
+{
+
+ session = s;
+ if (state == UNRESOLVED) {
+ source = resolver.resolveSource(session, address);
+ state = STARTED;
+ }
+ if (state == CANCELLED) {
+ source->cancel(session, destination);
+ parent.receiverCancelled(destination);
+ } else {
+ source->subscribe(session, destination);
+ start();
+ }
+}
+
+
+const std::string& ReceiverImpl::getName() const { return destination; }
+
+uint32_t ReceiverImpl::getCapacity()
+{
+ return capacity;
+}
+
+uint32_t ReceiverImpl::available()
+{
+ return parent.available(destination);
+}
+
+uint32_t ReceiverImpl::pendingAck()
+{
+ return parent.pendingAck(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::sys::Duration timeout)
+{
+ return parent.get(*this, message, timeout);
+}
+
+bool ReceiverImpl::fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ if (state == CANCELLED) return false;//TODO: or should this be an error?
+
+ 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 {
+ sync(session).messageFlush(destination);
+ startFlow();//reallocate credit
+ return getImpl(message, 0);
+ }
+}
+
+void ReceiverImpl::cancelImpl()
+{
+ if (state != CANCELLED) {
+ state = CANCELLED;
+ source->cancel(session, destination);
+ parent.receiverCancelled(destination);
+ }
+}
+
+void ReceiverImpl::setCapacityImpl(uint32_t c)
+{
+ if (c != capacity) {
+ capacity = c;
+ if (state == STARTED) {
+ session.messageStop(destination);
+ startFlow();
+ }
+ }
+}
+qpid::messaging::Session ReceiverImpl::getSession() const
+{
+ return qpid::messaging::Session(&parent);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
new file mode 100644
index 0000000000..d40aac4058
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
@@ -0,0 +1,148 @@
+#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/messaging/Variant.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include "qpid/sys/Time.h"
+#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::sys::Duration timeout);
+ qpid::messaging::Message get(qpid::sys::Duration timeout);
+ bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ qpid::messaging::Message fetch(qpid::sys::Duration timeout);
+ void cancel();
+ void start();
+ void stop();
+ const std::string& getName() const;
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t available();
+ uint32_t pendingAck();
+ void received(qpid::messaging::Message& message);
+ qpid::messaging::Session getSession() const;
+ private:
+ 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();
+ //implementation of public facing methods
+ bool fetchImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ bool getImpl(qpid::messaging::Message& message, qpid::sys::Duration timeout);
+ void cancelImpl();
+ void setCapacityImpl(uint32_t);
+
+ //functors for public facing methods (allows locking and retry
+ //logic to be centralised)
+ struct Command
+ {
+ ReceiverImpl& impl;
+
+ Command(ReceiverImpl& i) : impl(i) {}
+ };
+
+ struct Get : Command
+ {
+ qpid::messaging::Message& message;
+ qpid::sys::Duration timeout;
+ bool result;
+
+ Get(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::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::sys::Duration timeout;
+ bool result;
+
+ Fetch(ReceiverImpl& i, qpid::messaging::Message& m, qpid::sys::Duration t) :
+ Command(i), message(m), timeout(t), result(false) {}
+ void operator()() { result = impl.fetchImpl(message, timeout); }
+ };
+
+ struct Cancel : Command
+ {
+ Cancel(ReceiverImpl& i) : Command(i) {}
+ void operator()() { impl.cancelImpl(); }
+ };
+
+ 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/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
new file mode 100644
index 0000000000..4d6b9869e6
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/SenderImpl.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 "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) {}
+
+void SenderImpl::send(const qpid::messaging::Message& message)
+{
+ Send f(*this, &message);
+ while (f.repeat) parent.execute(f);
+}
+
+void SenderImpl::cancel()
+{
+ execute<Cancel>();
+}
+
+void SenderImpl::setCapacity(uint32_t c)
+{
+ bool flush = c < capacity;
+ capacity = c;
+ execute1<CheckPendingSends>(flush);
+}
+uint32_t SenderImpl::getCapacity() { return capacity; }
+uint32_t SenderImpl::pending()
+{
+ CheckPendingSends f(*this, false);
+ parent.execute(f);
+ return f.pending;
+}
+
+void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver)
+{
+ session = s;
+ if (state == UNRESOLVED) {
+ sink = resolver.resolveSink(session, address);
+ state = ACTIVE;
+ }
+ if (state == CANCELLED) {
+ sink->cancel(session, name);
+ parent.senderCancelled(name);
+ } else {
+ sink->declare(session, name);
+ replay();
+ }
+}
+
+void SenderImpl::waitForCapacity()
+{
+ //TODO: add option to throw exception rather than blocking?
+ if (capacity <= (flushed ? checkPendingSends(false) : 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);
+ }
+ //flush periodically and check for conmpleted sends
+ if (++window > (capacity / 4)) {//TODO: make this configurable?
+ checkPendingSends(true);
+ window = 0;
+ }
+}
+
+void SenderImpl::sendImpl(const qpid::messaging::Message& m)
+{
+ //TODO: make recording for replay optional (would still want to track completion however)
+ 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::replay()
+{
+ for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) {
+ sink->send(session, name, *i);
+ }
+}
+
+uint32_t SenderImpl::checkPendingSends(bool flush)
+{
+ if (flush) {
+ session.flush();
+ flushed = true;
+ } else {
+ flushed = false;
+ }
+ while (!outgoing.empty() && outgoing.front().status.isComplete()) {
+ outgoing.pop_front();
+ }
+ return outgoing.size();
+}
+
+void SenderImpl::cancelImpl()
+{
+ state = CANCELLED;
+ sink->cancel(session, name);
+ parent.senderCancelled(name);
+}
+
+const std::string& SenderImpl::getName() const
+{
+ return name;
+}
+
+qpid::messaging::Session SenderImpl::getSession() const
+{
+ return qpid::messaging::Session(&parent);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/cpp/src/qpid/client/amqp0_10/SenderImpl.h
new file mode 100644
index 0000000000..80d9843d9e
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/SenderImpl.h
@@ -0,0 +1,140 @@
+#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/messaging/Variant.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/amqp0_10/SessionImpl.h"
+#include <memory>
+#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&);
+ void cancel();
+ void setCapacity(uint32_t);
+ uint32_t getCapacity();
+ uint32_t pending();
+ void init(qpid::client::AsyncSession, AddressResolution&);
+ const std::string& getName() const;
+ qpid::messaging::Session getSession() const;
+
+ private:
+ 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;
+
+ uint32_t checkPendingSends(bool flush);
+ void replay();
+ void waitForCapacity();
+
+ //logic for application visible methods:
+ void sendImpl(const qpid::messaging::Message&);
+ void cancelImpl();
+
+
+ //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 Cancel : Command
+ {
+ Cancel(SenderImpl& i) : Command(i) {}
+ void operator()() { impl.cancelImpl(); }
+ };
+
+ 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/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
new file mode 100644
index 0000000000..c7dff05dd7
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
@@ -0,0 +1,433 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/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 "qpid/framing/reply_exceptions.h"
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+using qpid::messaging::KeyError;
+using qpid::messaging::MessageImplAccess;
+using qpid::messaging::Sender;
+using qpid::messaging::Receiver;
+using qpid::messaging::VariantMap;
+
+namespace qpid {
+namespace client {
+namespace amqp0_10 {
+
+SessionImpl::SessionImpl(ConnectionImpl& c, bool t) : connection(c), transactional(t) {}
+
+
+void SessionImpl::sync()
+{
+ retry<Sync>();
+}
+
+void SessionImpl::flush()
+{
+ retry<Flush>();
+}
+
+void SessionImpl::commit()
+{
+ if (!execute<Commit>()) {
+ throw Exception();//TODO: what type?
+ }
+}
+
+void SessionImpl::rollback()
+{
+ //If the session fails during this operation, the transaction will
+ //be rolled back anyway.
+ execute<Rollback>();
+}
+
+void SessionImpl::acknowledge()
+{
+ //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>();
+}
+
+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::close()
+{
+ //cancel all the senders and receivers (get copy of names and then
+ //make the calls to avoid modifying maps while iterating over
+ //them):
+ std::vector<std::string> s;
+ std::vector<std::string> r;
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ for (Senders::const_iterator i = senders.begin(); i != senders.end(); ++i) s.push_back(i->first);
+ for (Receivers::const_iterator i = receivers.begin(); i != receivers.end(); ++i) r.push_back(i->first);
+ }
+ for (std::vector<std::string>::const_iterator i = s.begin(); i != s.end(); ++i) getSender(*i).cancel();
+ for (std::vector<std::string>::const_iterator i = r.begin(); i != r.end(); ++i) getReceiver(*i).cancel();
+
+
+ connection.closed(*this);
+ session.close();
+}
+
+template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t)
+{
+ return boost::dynamic_pointer_cast<S>(qpid::client::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)
+{
+ qpid::sys::Mutex::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);
+ }
+}
+
+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)
+{
+ 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)
+{
+ 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 qpid::Exception(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)
+{
+ 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;
+ }
+}
+
+bool SessionImpl::getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout)
+{
+ return incoming.get(handler, timeout);
+}
+
+bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout)
+{
+ IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, _1));
+ return getIncoming(handler, timeout);
+}
+
+bool SessionImpl::nextReceiver(qpid::messaging::Receiver& receiver, qpid::sys::Duration timeout)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ while (true) {
+ try {
+ std::string destination;
+ if (incoming.getNextDestination(destination, timeout)) {
+ Receivers::const_iterator i = receivers.find(destination);
+ if (i == receivers.end()) {
+ throw qpid::Exception(QPID_MSG("Received message for unknown destination " << destination));
+ } else {
+ receiver = i->second;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } catch (TransportFailure&) {
+ reconnect();
+ }
+ }
+}
+
+qpid::messaging::Receiver SessionImpl::nextReceiver(qpid::sys::Duration timeout)
+{
+ qpid::messaging::Receiver receiver;
+ if (!nextReceiver(receiver, timeout)) throw Receiver::NoMessageAvailable();
+ if (!receiver) throw qpid::Exception("Bad receiver returned!");
+ return receiver;
+}
+
+uint32_t SessionImpl::available()
+{
+ return get1<Available, uint32_t>((const std::string*) 0);
+}
+uint32_t SessionImpl::available(const std::string& destination)
+{
+ return get1<Available, uint32_t>(&destination);
+}
+
+struct SessionImpl::Available : Command
+{
+ const std::string* destination;
+ uint32_t result;
+
+ Available(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {}
+ void operator()() { result = impl.availableImpl(destination); }
+};
+
+uint32_t SessionImpl::availableImpl(const std::string* destination)
+{
+ if (destination) {
+ return incoming.available(*destination);
+ } else {
+ return incoming.available();
+ }
+}
+
+uint32_t SessionImpl::pendingAck()
+{
+ return get1<PendingAck, uint32_t>((const std::string*) 0);
+}
+
+uint32_t SessionImpl::pendingAck(const std::string& destination)
+{
+ return get1<PendingAck, uint32_t>(&destination);
+}
+
+struct SessionImpl::PendingAck : Command
+{
+ const std::string* destination;
+ uint32_t result;
+
+ PendingAck(SessionImpl& i, const std::string* d) : Command(i), destination(d), result(0) {}
+ void operator()() { result = impl.pendingAckImpl(destination); }
+};
+
+uint32_t SessionImpl::pendingAckImpl(const std::string* destination)
+{
+ if (destination) {
+ return incoming.pendingAccept(*destination);
+ } else {
+ return incoming.pendingAccept();
+ }
+}
+
+void SessionImpl::syncImpl()
+{
+ session.sync();
+}
+
+void SessionImpl::flushImpl()
+{
+ session.flush();
+}
+
+
+void SessionImpl::commitImpl()
+{
+ incoming.accept();
+ session.txCommit();
+}
+
+void SessionImpl::rollbackImpl()
+{
+ 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()
+{
+ incoming.accept();
+}
+
+void SessionImpl::rejectImpl(qpid::messaging::Message& m)
+{
+ SequenceSet set;
+ set.add(MessageImplAccess::get(m).getInternalId());
+ session.messageReject(set);
+}
+
+void SessionImpl::receiverCancelled(const std::string& name)
+{
+ receivers.erase(name);
+ session.sync();
+ incoming.releasePending(name);
+}
+
+void SessionImpl::senderCancelled(const std::string& name)
+{
+ senders.erase(name);
+}
+
+void SessionImpl::reconnect()
+{
+ connection.reconnect();
+}
+
+qpid::messaging::Connection SessionImpl::getConnection() const
+{
+ return qpid::messaging::Connection(&connection);
+}
+
+}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/cpp/src/qpid/client/amqp0_10/SessionImpl.h
new file mode 100644
index 0000000000..96c7ca93a3
--- /dev/null
+++ b/cpp/src/qpid/client/amqp0_10/SessionImpl.h
@@ -0,0 +1,212 @@
+#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/Variant.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"
+
+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();
+ void reject(qpid::messaging::Message&);
+ void close();
+ void sync();
+ void flush();
+ 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::sys::Duration timeout);
+ qpid::messaging::Receiver nextReceiver(qpid::sys::Duration timeout);
+
+ qpid::messaging::Connection getConnection() const;
+
+ bool get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout);
+
+ void receiverCancelled(const std::string& name);
+ void senderCancelled(const std::string& name);
+
+ uint32_t available();
+ uint32_t available(const std::string& destination);
+
+ uint32_t pendingAck();
+ uint32_t pendingAck(const std::string& destination);
+
+ void setSession(qpid::client::Session);
+
+ template <class T> bool execute(T& f)
+ {
+ try {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ f();
+ return true;
+ } catch (TransportFailure&) {
+ reconnect();
+ return false;
+ }
+ }
+
+ 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;
+ 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::sys::Duration timeout);
+ bool getNextReceiver(qpid::messaging::Receiver* receiver, IncomingMessages::MessageTransfer& transfer);
+ void reconnect();
+
+ void commitImpl();
+ void rollbackImpl();
+ void acknowledgeImpl();
+ void rejectImpl(qpid::messaging::Message&);
+ void closeImpl();
+ void syncImpl();
+ void flushImpl();
+ qpid::messaging::Sender createSenderImpl(const qpid::messaging::Address& address);
+ qpid::messaging::Receiver createReceiverImpl(const qpid::messaging::Address& address);
+ uint32_t availableImpl(const std::string* destination);
+ uint32_t pendingAckImpl(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(); }
+ };
+
+ struct Flush : Command
+ {
+ Flush(SessionImpl& i) : Command(i) {}
+ void operator()() { impl.flushImpl(); }
+ };
+
+ struct Reject : Command
+ {
+ qpid::messaging::Message& message;
+
+ Reject(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {}
+ void operator()() { impl.rejectImpl(message); }
+ };
+
+ struct CreateSender;
+ struct CreateReceiver;
+ struct PendingAck;
+ struct Available;
+
+ //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/cpp/src/qpid/client/windows/SaslFactory.cpp b/cpp/src/qpid/client/windows/SaslFactory.cpp
new file mode 100644
index 0000000000..87df187ab2
--- /dev/null
+++ b/cpp/src/qpid/client/windows/SaslFactory.cpp
@@ -0,0 +1,146 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/client/SaslFactory.h"
+#include "qpid/client/ConnectionSettings.h"
+
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/sys/SecurityLayer.h"
+#include "qpid/log/Statement.h"
+
+#include "boost/tokenizer.hpp"
+
+namespace qpid {
+namespace client {
+
+using qpid::sys::SecurityLayer;
+using qpid::framing::InternalErrorException;
+
+class WindowsSasl : public Sasl
+{
+ public:
+ WindowsSasl(const ConnectionSettings&);
+ ~WindowsSasl();
+ std::string start(const std::string& mechanisms, unsigned int ssf);
+ std::string step(const std::string& challenge);
+ std::string getMechanism();
+ std::string getUserId();
+ std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
+ private:
+ ConnectionSettings 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 ConnectionSettings& settings)
+{
+ std::auto_ptr<Sasl> sasl(new WindowsSasl(settings));
+ return sasl;
+}
+
+namespace {
+ const std::string ANONYMOUS = "ANONYMOUS";
+ const std::string PLAIN = "PLAIN";
+}
+
+WindowsSasl::WindowsSasl(const ConnectionSettings& s)
+ : settings(s)
+{
+}
+
+WindowsSasl::~WindowsSasl()
+{
+}
+
+std::string WindowsSasl::start(const std::string& mechanisms,
+ unsigned int /*ssf*/)
+{
+ 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::client
diff --git a/cpp/src/qpid/cluster/ClassifierHandler.cpp b/cpp/src/qpid/cluster/ClassifierHandler.cpp
deleted file mode 100644
index b78f795d20..0000000000
--- a/cpp/src/qpid/cluster/ClassifierHandler.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *
- * 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 "ClassifierHandler.h"
-
-#include "qpid/framing/FrameDefaultVisitor.h"
-#include "qpid/framing/AMQFrame.h"
-
-namespace qpid {
-namespace cluster {
-
-using namespace framing;
-
-struct ClassifierHandler::Visitor : public FrameDefaultVisitor {
- Visitor(AMQFrame& f, ClassifierHandler& c)
- : chosen(0), frame(f), classifier(c) { f.getBody()->accept(*this); }
-
- void visit(const ExchangeDeclareBody&) { chosen=&classifier.wiring; }
- void visit(const ExchangeDeleteBody&) { chosen=&classifier.wiring; }
- void visit(const ExchangeBindBody&) { chosen=&classifier.wiring; }
- void visit(const ExchangeUnbindBody&) { chosen=&classifier.wiring; }
- void visit(const QueueDeclareBody&) { chosen=&classifier.wiring; }
- void visit(const QueueDeleteBody&) { chosen=&classifier.wiring; }
- void defaultVisit(const AMQBody&) { chosen=&classifier.other; }
-
- using framing::FrameDefaultVisitor::visit;
- using framing::FrameDefaultVisitor::defaultVisit;
-
- FrameHandler* chosen;
- AMQFrame& frame;
- ClassifierHandler& classifier;
-};
-
-void ClassifierHandler::handle(AMQFrame& f) { Visitor(f, *this).chosen->handle(f); }
-
-}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/ClassifierHandler.h b/cpp/src/qpid/cluster/ClassifierHandler.h
deleted file mode 100644
index 696e457c04..0000000000
--- a/cpp/src/qpid/cluster/ClassifierHandler.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef QPID_CLUSTER_CLASSIFIERHANDLER_H
-#define QPID_CLUSTER_CLASSIFIERHANDLER_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"
-
-namespace qpid {
-namespace cluster {
-
-/**
- * Classify frames and forward to the appropriate handler.
- */
-class ClassifierHandler : public framing::FrameHandler
-{
- public:
- ClassifierHandler(framing::FrameHandler& wiring_,
- framing::FrameHandler& other_)
- : wiring(wiring_), other(other_) {}
-
- void handle(framing::AMQFrame&);
-
- private:
- struct Visitor;
- friend struct Visitor;
- framing::FrameHandler& wiring;
- framing::FrameHandler& other;
-};
-
-}} // namespace qpid::cluster
-
-
-
-#endif /*!QPID_CLUSTER_CLASSIFIERHANDLER_H*/
diff --git a/cpp/src/qpid/cluster/Cluster.cpp b/cpp/src/qpid/cluster/Cluster.cpp
index 05ab9148b5..d049001eb0 100644
--- a/cpp/src/qpid/cluster/Cluster.cpp
+++ b/cpp/src/qpid/cluster/Cluster.cpp
@@ -16,305 +16,964 @@
*
*/
-#include "Cluster.h"
-#include "ConnectionInterceptor.h"
-
+/** CLUSTER IMPLEMENTATION OVERVIEW
+ *
+ * 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.
+ *
+ * CLUSTER PROTOCOL OVERVIEW
+ *
+ * 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.
+ *
+ * CLUSTER INITIALIZATION OVERVIEW
+ *
+ * When a new member joins the CPG group, all members (including the
+ * new one) multicast their "initial status." The new member is in
+ * INIT mode until it gets a complete set of initial status messages
+ * from all cluster members.
+ *
+ * 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?
+ *
+ * Initialization happens in the Cluster constructor (plugin
+ * early-init phase) because it needs to be done before the store
+ * initializes. In INIT mode sending & receiving from the cluster are
+ * done single-threaded, bypassing the normal PollableQueues because
+ * the Poller is not active at this point to service them.
+ */
+#include "qpid/Exception.h"
+#include "qpid/cluster/Cluster.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/UpdateExchange.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/SessionState.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/broker/SignalHandler.h"
#include "qpid/framing/AMQFrame.h"
-#include "qpid/framing/ClusterNotifyBody.h"
-#include "qpid/framing/ClusterConnectionCloseBody.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/MessageTransferBody.h"
+#include "qpid/log/Helpers.h"
#include "qpid/log/Statement.h"
+#include "qpid/management/IdAllocator.h"
+#include "qpid/management/ManagementAgent.h"
#include "qpid/memory.h"
-#include "qpid/shared_ptr.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 broker::Connection;
+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 = 884125;
-ostream& operator <<(ostream& out, const Cluster& cluster) {
- return out << cluster.name.str() << "-" << cluster.self;
-}
+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_) {}
-ostream& operator<<(ostream& out, const Cluster::MemberMap::value_type& m) {
- return out << m.first << "=" << m.second.url;
-}
+ void updateRequest(const std::string& url) { cluster.updateRequest(member, url, l); }
-ostream& operator <<(ostream& out, const Cluster::MemberMap& members) {
- ostream_iterator<Cluster::MemberMap::value_type> o(out, " ");
- copy(members.begin(), members.end(), o);
- return out;
-}
+ void initialStatus(uint32_t version, bool active, const Uuid& clusterId,
+ uint8_t storeState, const Uuid& shutdownId)
+ {
+ cluster.initialStatus(member, version, active, clusterId,
+ framing::cluster::StoreState(storeState), shutdownId, l);
+ }
+ void ready(const std::string& url) { cluster.ready(member, url, l); }
+ void configChange(const std::string& current) { cluster.configChange(member, current, 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 shutdown(const Uuid& id) { cluster.shutdown(member, id, l); }
+
+ bool invoke(AMQBody& body) { return framing::invoke(*this, body).wasHandled(); }
+};
-Cluster::Cluster(const std::string& name_, const Url& url_, broker::Broker& b) :
- broker(&b),
+Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
+ settings(set),
+ broker(b),
+ mgmtObject(0),
poller(b.getPoller()),
cpg(*this),
- name(name_),
- url(url_),
+ name(settings.name),
+ myUrl(settings.url.empty() ? Url() : Url(settings.url)),
self(cpg.self()),
- cpgDispatchHandle(cpg,
- boost::bind(&Cluster::dispatch, this, _1), // read
- 0, // write
- boost::bind(&Cluster::disconnect, this, _1) // disconnect
- ),
- deliverQueue(boost::bind(&Cluster::deliverQueueCb, this, _1, _2)),
- mcastQueue(boost::bind(&Cluster::mcastQueueCb, this, _1, _2))
+ clusterId(true),
+ 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",
+ poller),
+ deliverFrameQueue(boost::bind(&Cluster::deliveredFrame, this, _1),
+ boost::bind(&Cluster::leave, this),
+ "Error delivering frames",
+ poller),
+ quorum(boost::bind(&Cluster::leave, this)),
+ decoder(boost::bind(&Cluster::deliverFrame, this, _1)),
+ discarding(true),
+ state(INIT),
+ initMap(self, settings.size),
+ store(broker.getDataDir().getPath()),
+ lastSize(0),
+ lastBroker(false),
+ updateRetracted(false),
+ error(*this)
{
- broker->addFinalizer(boost::bind(&Cluster::leave, this));
- QPID_LOG(trace, "Joining cluster: " << name_);
- cpg.join(name);
- notify();
+ mAgent = broker.getManagementAgent();
+ if (mAgent != 0){
+ _qmf::Package packageInit(mAgent);
+ mgmtObject = new _qmf::Cluster (mAgent, this, &broker,name,myUrl.str());
+ mAgent->addObject (mgmtObject);
+ mgmtObject->set_status("JOINING");
+ }
- // FIXME aconway 2008-08-11: can we remove this loop?
- // Dispatch till we show up in the cluster map.
- while (empty())
- cpg.dispatchOne();
+ // Failover exchange provides membership updates to clients.
+ failoverExchange.reset(new FailoverExchange(this));
+ 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)));
+ // Load my store status before we go into initialization
+ if (! broker::NullMessageStore::isNullStore(&broker.getStore())) {
+ store.load();
+ if (store.getClusterId())
+ clusterId = store.getClusterId(); // Use stored ID if there is one.
+ QPID_LOG(notice, "Cluster store state: " << store)
+ }
- // Start dispatching from the poller.
- cpgDispatchHandle.startWatch(poller);
- deliverQueue.start(poller);
- mcastQueue.start(poller);
+ cpg.join(name);
+ // Pump the CPG dispatch manually till we get initialized.
+ while (state == INIT)
+ cpg.dispatchOne();
}
Cluster::~Cluster() {
- for (ShadowConnectionMap::iterator i = shadowConnectionMap.begin();
- i != shadowConnectionMap.end();
- ++i)
- {
- i->second->dirtyClose();
+ if (updateThread.id()) updateThread.join(); // Join the previous updatethread.
+}
+
+void Cluster::initialize() {
+ if (settings.quorum) quorum.start(poller);
+ if (myUrl.empty())
+ myUrl = Url::getIpAddressesUrl(broker.getPort(broker::Broker::TCP_TRANSPORT));
+ // Cluster constructor will leave us in either READY or JOINER state.
+ switch (state) {
+ case READY:
+ mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
+ break;
+ case JOINER:
+ mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self);
+ break;
+ default:
+ assert(0);
}
- std::for_each(localConnectionSet.begin(), localConnectionSet.end(), boost::bind(&ConnectionInterceptor::dirtyClose, _1));
+ QPID_LOG(notice, *this << (state == READY ? " joined" : " joining") << " cluster " << name);
+ broker.getKnownBrokers = boost::bind(&Cluster::getUrls, this);
+ broker.setExpiryPolicy(expiryPolicy);
+ dispatcher.start();
+ deliverEventQueue.start();
+ deliverFrameQueue.start();
+
+ // Add finalizer last for exception safety.
+ broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this));
}
-// local connection initializes plugins
-void Cluster::initialize(broker::Connection& c) {
- bool isLocal = &c.getOutput() != &shadowOut;
- if (isLocal)
- localConnectionSet.insert(new ConnectionInterceptor(c, *this));
+// Called in connection thread to insert a client connection.
+void Cluster::addLocalConnection(const boost::intrusive_ptr<Connection>& c) {
+ QPID_LOG(info, *this << " new local connection " << c->getId());
+ localConnections.insert(c);
+ assert(c->getId().getMember() == self);
+ // Announce the connection to the cluster.
+ if (c->isLocalClient())
+ mcast.mcastControl(ClusterConnectionAnnounceBody(ProtocolVersion(),
+ c->getBrokerConnection().getSSF() ),
+ c->getId());
}
-void Cluster::leave() {
- Mutex::ScopedLock l(lock);
- if (!broker) return; // Already left.
- // At this point the poller has already been shut down so
- // no dispatches can occur thru the cpgDispatchHandle.
- //
- // FIXME aconway 2008-08-11: assert this is the cae.
-
- QPID_LOG(debug, "Leaving cluster " << *this);
- cpg.leave(name);
- // broker= is set to 0 when the final config-change is delivered.
- while(broker) {
- Mutex::ScopedUnlock u(lock);
- cpg.dispatchAll();
- }
- cpg.shutdown();
+// Called in connection thread to insert an updated shadow connection.
+void Cluster::addShadowConnection(const boost::intrusive_ptr<Connection>& c) {
+ QPID_LOG(info, *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);
}
-template <class T> void decodePtr(Buffer& buf, T*& ptr) {
- uint64_t value = buf.getLongLong();
- ptr = reinterpret_cast<T*>(value);
+// Called by Connection::deliverClose() in deliverFrameQueue thread.
+void Cluster::erase(const ConnectionId& id, Lock&) {
+ QPID_LOG(info, *this << " connection closed " << id);
+ connections.erase(id);
+ decoder.erase(id);
}
-template <class T> void encodePtr(Buffer& buf, T* ptr) {
- uint64_t value = reinterpret_cast<uint64_t>(ptr);
- buf.putLongLong(value);
+std::vector<string> Cluster::getIds() const {
+ Lock l(lock);
+ return getIds(l);
}
-void Cluster::send(const AMQFrame& frame, ConnectionInterceptor* connection) {
- QPID_LOG(trace, "MCAST [" << connection << "] " << frame);
- mcastQueue.push(Message(frame, self, connection));
+std::vector<string> Cluster::getIds(Lock&) const {
+ return map.memberIds();
}
-void Cluster::mcastQueueCb(const MessageQueue::iterator& begin,
- const MessageQueue::iterator& end)
+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.
+ 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)
{
- // Static is OK because there is only one cluster allowed per
- // process and only one thread in mcastQueueCb at a time.
- static char buffer[64*1024]; // FIXME aconway 2008-07-04: buffer management.
- MessageQueue::iterator i = begin;
- while (i != end) {
- Buffer buf(buffer, sizeof(buffer));
- while (i != end && buf.available() > i->frame.size() + sizeof(uint64_t)) {
- i->frame.encode(buf);
- encodePtr(buf, i->connection);
- ++i;
+ 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) {
+ // During initialization, execute events directly in the same thread.
+ // Once initialized, push to pollable queue to be processed in another thread.
+ if (state == INIT)
+ deliveredEvent(e);
+ else
+ deliverEventQueue.push(e);
+}
+
+void Cluster::deliverFrame(const EventFrame& e) {
+ // During initialization, execute events directly in the same thread.
+ // Once initialized, push to pollable queue to be processed in another thread.
+ if (state == INIT)
+ deliveredFrame(e);
+ else
+ 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.
+ const ClusterUpdateOfferBody* offer = castUpdateOffer(ef.frame.getBody());
+ if (offer) {
+ 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));
+ }
}
- iovec iov = { buffer, buf.getPosition() };
- cpg.mcast(name, &iov, 1);
}
}
-void Cluster::notify() {
- send(AMQFrame(in_place<ClusterNotifyBody>(ProtocolVersion(), url.str())), 0);
+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);
}
-size_t Cluster::size() const {
+// Handler for deliverFrameQueue.
+// This thread executes the main logic.
+void Cluster::deliveredFrame(const EventFrame& efConst) {
Mutex::ScopedLock l(lock);
- return members.size();
+ 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);
}
-Cluster::MemberList Cluster::getMembers() const {
- Mutex::ScopedLock l(lock);
- MemberList result(members.size());
- std::transform(members.begin(), members.end(), result.begin(),
- boost::bind(&MemberMap::value_type::second, _1));
- return result;
+
+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
+ QPID_LOG(trace, *this << " DROP (no connection): " << e);
+ }
+ else // Drop connection frames while state < CATCHUP
+ QPID_LOG(trace, *this << " DROP (joining): " << e);
}
-// ################ HERE - leaking shadow connections.
-// FIXME aconway 2008-08-11: revisit memory management for shadow
-// connections, what if the Connection is closed other than via
-// disconnect? Dangling pointer in shadow map. Use ptr_map for shadow
-// map, add deleted state to ConnectionInterceptor? Interceptors need
-// to know about map? Check how Connections can be deleted.
+// 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.
+ if (e.frame.getBody() && e.frame.getMethod() &&
+ e.frame.getMethod()->isA<ClusterConnectionAnnounceBody>())
+ {
+ if (id.getMember() == self) { // Announces one of my own
+ cp = localConnections.getErase(id);
+ assert(cp);
+ }
+ else { // New remote connection, create a shadow.
+ std::ostringstream mgmtId;
+ unsigned int ssf;
+ const ClusterConnectionAnnounceBody *announce = castAnnounce(e.frame.getBody());
+
+ mgmtId << id;
+ ssf = (announce && announce->hasSsf()) ? announce->getSsf() : 0;
+ QPID_LOG(debug, *this << "new connection's ssf =" << ssf );
+ cp = new Connection(*this, shadowOut, mgmtId.str(), id, ssf );
+ }
+ connections.insert(ConnectionMap::value_type(id, cp));
+ }
+ return cp;
+}
-ConnectionInterceptor* Cluster::getShadowConnection(const Cpg::Id& member, void* remotePtr) {
- ShadowConnectionId id(member, remotePtr);
- ShadowConnectionMap::iterator i = shadowConnectionMap.find(id);
- if (i == shadowConnectionMap.end()) { // A new shadow connection.
- std::ostringstream os;
- os << name << ":" << member << ":" << remotePtr;
- assert(broker);
- broker::Connection* c = new broker::Connection(&shadowOut, *broker, os.str());
- ShadowConnectionMap::value_type value(id, new ConnectionInterceptor(*c, *this, id));
- i = shadowConnectionMap.insert(value).first;
+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;
+}
+
+struct AddrList {
+ const cpg_address* addrs;
+ int count;
+ const char *prefix, *suffix;
+ AddrList(const cpg_address* a, int n, const char* p="", const char* s="")
+ : addrs(a), count(n), prefix(p), suffix(s) {}
+};
+
+ostream& operator<<(ostream& o, const AddrList& a) {
+ if (!a.count) return o;
+ o << a.prefix;
+ for (const cpg_address* p = a.addrs; p < a.addrs+a.count; ++p) {
+ const char* reasonString;
+ switch (p->reason) {
+ case CPG_REASON_JOIN: reasonString = "(joined) "; break;
+ case CPG_REASON_LEAVE: reasonString = "(left) "; break;
+ case CPG_REASON_NODEDOWN: reasonString = "(node-down) "; break;
+ case CPG_REASON_NODEUP: reasonString = "(node-up) "; break;
+ case CPG_REASON_PROCDOWN: reasonString = "(process-down) "; break;
+ default: reasonString = " ";
+ }
+ qpid::cluster::MemberId member(*p);
+ o << member << reasonString;
}
- return i->second;
+ return o << a.suffix;
}
-void Cluster::deliver(
+void Cluster::configChange (
cpg_handle_t /*handle*/,
- cpg_name* /*group*/,
- uint32_t nodeid,
- uint32_t pid,
- void* msg,
- int msg_len)
+ const cpg_name */*group*/,
+ const cpg_address *current, int nCurrent,
+ const cpg_address *left, int nLeft,
+ const cpg_address *joined, int nJoined)
{
- Id from(nodeid, pid);
- try {
- Buffer buf(static_cast<char*>(msg), msg_len);
- while (buf.available() > 0) {
- AMQFrame frame;
- if (!frame.decode(buf)) // Not enough data.
- throw Exception("Received incomplete cluster event.");
- void* connection;
- decodePtr(buf, connection);
- deliverQueue.push(Message(frame, from, connection));
+ Mutex::ScopedLock l(lock);
+ QPID_LOG(notice, *this << " membership change: "
+ << AddrList(current, nCurrent) << "("
+ << AddrList(joined, nJoined, "joined: ")
+ << AddrList(left, nLeft, "left: ")
+ << ")");
+ std::string addresses;
+ for (const cpg_address* p = current; p < current+nCurrent; ++p)
+ addresses.append(MemberId(*p).str());
+ deliverEvent(Event::control(ClusterConfigChangeBody(ProtocolVersion(), addresses), self));
+}
+
+void Cluster::setReady(Lock&) {
+ state = READY;
+ if (mgmtObject!=0) mgmtObject->set_status("ACTIVE");
+ mcast.setReady();
+ broker.getQueueEvents().enable();
+}
+
+void Cluster::initMapCompleted(Lock& l) {
+ // Called on completion of the initial status map.
+ QPID_LOG(debug, *this << " initial status map complete. ");
+ if (state == INIT) {
+ // We have status for all members so we can make join descisions.
+ initMap.checkConsistent();
+ elders = initMap.getElders();
+ QPID_LOG(debug, *this << " elders: " << elders);
+ if (!elders.empty()) { // I'm not the elder, I don't handle links & replication.
+ broker.getLinks().setPassive(true);
+ broker.getQueueEvents().disable();
+ QPID_LOG(info, *this << " not active for links.");
+ }
+ else {
+ QPID_LOG(info, this << " active for links.");
+ }
+ setClusterId(initMap.getClusterId(), l);
+ if (store.hasStore()) store.dirty(clusterId);
+
+ if (initMap.isUpdateNeeded()) { // Joining established cluster.
+ broker.setRecovery(false); // Ditch my current store.
+ broker.setClusterUpdatee(true);
+ state = JOINER;
+ }
+ else { // I can go ready.
+ discarding = false;
+ setReady(l);
+ memberUpdate(l);
}
+ QPID_LOG(debug, *this << "Initialization complete");
+ }
+}
+
+void Cluster::configChange(const MemberId&, const std::string& configStr, Lock& l) {
+ if (state == LEFT) return;
+
+ MemberSet config = decodeMemberSet(configStr);
+ elders = intersection(elders, config);
+ 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(config);
+
+ // Update initital status for new members joining.
+ initMap.configChange(config);
+ if (initMap.isResendNeeded()) {
+ mcast.mcastControl(
+ ClusterInitialStatusBody(
+ ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId,
+ store.getState(), store.getShutdownId()
+ ),
+ self);
+ }
+ if (initMap.transitionToComplete()) initMapCompleted(l);
+
+ if (state >= CATCHUP && memberChange) {
+ memberUpdate(l);
+ if (elders.empty()) {
+ // We are the oldest, reactive links if necessary
+ QPID_LOG(info, this << " becoming active for links.");
+ broker.getLinks().setPassive(false);
+ }
+ }
+}
+
+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);
+ }
+}
+
+// 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() {
+ try { cpg.shutdown(); }
catch (const std::exception& e) {
- // FIXME aconway 2008-01-30: exception handling.
- QPID_LOG(critical, "Error in cluster deliver: " << e.what());
- assert(0);
- throw;
+ 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::deliverQueueCb(const MessageQueue::iterator& begin,
- const MessageQueue::iterator& end)
+void Cluster::initialStatus(const MemberId& member, uint32_t version, bool active,
+ const framing::Uuid& id,
+ framing::cluster::StoreState store,
+ const framing::Uuid& shutdownId,
+ Lock& l)
{
- for (MessageQueue::iterator i = begin; i != end; ++i) {
- AMQFrame& frame(i->frame);
- Id from(i->from);
- ConnectionInterceptor* connection = reinterpret_cast<ConnectionInterceptor*>(i->connection);
- try {
- QPID_LOG(trace, "DLVR [" << from << " " << connection << "] " << frame);
-
- if (!broker) {
- QPID_LOG(warning, "Unexpected DLVR, already left the cluster.");
- return;
- }
- if (connection && from != self) // Look up shadow for remote connections
- connection = getShadowConnection(from, connection);
+ if (version != CLUSTER_VERSION) {
+ QPID_LOG(critical, *this << " incompatible cluster versions " <<
+ version << " != " << CLUSTER_VERSION);
+ leave(l);
+ return;
+ }
+ initMap.received(
+ member,
+ ClusterInitialStatusBody(ProtocolVersion(), version, active, id, store, shutdownId)
+ );
+ if (initMap.transitionToComplete()) initMapCompleted(l);
+}
- if (frame.getMethod() && frame.getMethod()->amqpClassId() == CLUSTER_CLASS_ID)
- handleMethod(from, connection, *frame.getMethod());
- else
- connection->deliver(frame);
+void Cluster::ready(const MemberId& id, const std::string& url, Lock& l) {
+ if (map.ready(id, Url(url)))
+ memberUpdate(l);
+ if (state == CATCHUP && id == self) {
+ setReady(l);
+ QPID_LOG(notice, *this << " caught up.");
+ }
+}
+
+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
}
- catch (const std::exception& e) {
- // FIXME aconway 2008-01-30: exception handling.
- QPID_LOG(critical, "Error in cluster deliverQueueCb: " << e.what());
- assert(0);
- throw;
+ }
+ else if (updatee == self && url) {
+ assert(state == JOINER);
+ state = UPDATEE;
+ QPID_LOG(notice, *this << " receiving update from " << updater);
+ checkUpdateIn(l);
+ }
+ else {
+ QPID_LOG(debug,*this << " unstall, ignore update " << updater
+ << " to " << updatee);
+ deliverEventQueue.start(); // Not involved in update.
+ }
+}
+
+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.id())
+ 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.id())
+ 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)));
}
-// Handle cluster methods
-// FIXME aconway 2008-07-11: Generate/template a better dispatch mechanism.
-void Cluster::handleMethod(Id from, ConnectionInterceptor* connection, AMQMethodBody& method) {
- assert(method.amqpClassId() == CLUSTER_CLASS_ID);
- switch (method.amqpMethodId()) {
- case CLUSTER_NOTIFY_METHOD_ID: {
- ClusterNotifyBody& notify=static_cast<ClusterNotifyBody&>(method);
- Mutex::ScopedLock l(lock);
- members[from].url=notify.getUrl();
- lock.notifyAll();
- break;
- }
- case CLUSTER_CONNECTION_CLOSE_METHOD_ID: {
- if (!connection->isLocal())
- shadowConnectionMap.erase(connection->getShadowId());
- else
- localConnectionSet.erase(connection);
- connection->deliverClosed();
- break;
- }
- case CLUSTER_CONNECTION_DO_OUTPUT_METHOD_ID: {
- connection->deliverDoOutput();
- break;
- }
+// 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);
+}
+
+void Cluster::checkUpdateIn(Lock& l) {
+ if (state != UPDATEE) return; // Wait till we reach the stall point.
+ if (updatedMap) { // We're up to date
+ map = *updatedMap;
+ memberUpdate(l);
+ mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
+ state = CATCHUP;
+ broker.setClusterUpdatee(false);
+ discarding = false; // ok to set, we're stalled for update.
+ QPID_LOG(notice, *this << " update complete, starting catch-up.");
+ deliverEventQueue.start();
+ }
+ else if (updateRetracted) { // Update was retracted, request another update
+ updateRetracted = 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(Uuid(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:
- assert(0);
+ return Manageable::STATUS_UNKNOWN_METHOD;
}
+ return Manageable::STATUS_OK;
}
-void Cluster::configChange(
- cpg_handle_t /*handle*/,
- cpg_name */*group*/,
- cpg_address *current, int nCurrent,
- cpg_address *left, int nLeft,
- cpg_address */*joined*/, int nJoined)
-{
- Mutex::ScopedLock l(lock);
- for (int i = 0; i < nLeft; ++i)
- members.erase(left[i]);
- for(int j = 0; j < nCurrent; ++j)
- members[current[j]].id = current[j];
- QPID_LOG(debug, "Cluster members: " << nCurrent << " ("<< nLeft << " left, " << nJoined << " joined):"
- << members);
- assert(members.size() == size_t(nCurrent));
- if (members.find(self) == members.end())
- broker = 0; // We have left the group, this is the final config change.
- lock.notifyAll(); // Threads waiting for membership changes.
+void Cluster::stopClusterNode(Lock& l) {
+ QPID_LOG(notice, *this << " cluster member stopped by administrator.");
+ leave(l);
}
-void Cluster::dispatch(sys::DispatchHandle& h) {
- cpg.dispatchAll();
- h.rewatch();
+void Cluster::stopFullCluster(Lock& ) {
+ QPID_LOG(notice, *this << " shutting down cluster " << name);
+ mcast.mcastControl(ClusterShutdownBody(ProtocolVersion(), Uuid(true)), self);
}
-void Cluster::disconnect(sys::DispatchHandle& h) {
- h.stopWatch();
- // FIXME aconway 2008-08-11: error handling if we are disconnected.
- // Kill the broker?
- assert(0);
+void Cluster::memberUpdate(Lock& l) {
+ QPID_LOG(info, *this << " member update: " << map);
+ std::vector<Url> urls = getUrls(l);
+ std::vector<string> ids = getIds(l);
+ size_t size = urls.size();
+ failoverExchange->setUrls(urls);
+
+ if (size == 1 && lastSize > 1 && state >= CATCHUP) {
+ QPID_LOG(notice, *this << " last broker standing, update queue policies");
+ lastBroker = true;
+ broker.getQueues().updateQueueClusterState(true);
+ }
+ else if (size > 1 && lastBroker) {
+ QPID_LOG(notice, *this << " last broker standing joined by " << size-1 << " replicas, updating queue policies" << size);
+ lastBroker = false;
+ broker.getQueues().updateQueueClusterState(false);
+ }
+ lastSize = size;
+
+ if (mgmtObject) {
+ mgmtObject->set_clusterSize(size);
+ string urlstr;
+ for(std::vector<Url>::iterator iter = urls.begin(); iter != urls.end(); iter++ ) {
+ if (iter != urls.begin()) urlstr += ";";
+ urlstr += iter->str();
+ }
+ string idstr;
+ for(std::vector<string>::iterator iter = ids.begin(); iter != ids.end(); iter++ ) {
+ if (iter != ids.begin()) idstr += ";";
+ idstr += (*iter);
+ }
+ mgmtObject->set_members(urlstr);
+ mgmtObject->set_memberIDs(idstr);
+ }
+
+ // 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->getBrokerConnection().closed();
+ erase(j->second->getId(), l);
+ }
+ }
}
-}} // namespace qpid::cluster
+std::ostream& operator<<(std::ostream& o, const Cluster& cluster) {
+ static const char* STATE[] = {
+ "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 (mgmtObject) {
+ stringstream stream;
+ stream << self;
+ mgmtObject->set_clusterID(clusterId.str());
+ mgmtObject->set_memberID(stream.str());
+ }
+ QPID_LOG(debug, *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);
+}
+
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/Cluster.h b/cpp/src/qpid/cluster/Cluster.h
index 2b40193dd3..7872588307 100644
--- a/cpp/src/qpid/cluster/Cluster.h
+++ b/cpp/src/qpid/cluster/Cluster.h
@@ -19,151 +19,261 @@
*
*/
-#include "qpid/cluster/Cpg.h"
-#include "qpid/cluster/ShadowConnectionOutputHandler.h"
-#include "qpid/cluster/PollableQueue.h"
-
+#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/broker/Connection.h"
-#include "qpid/sys/Dispatcher.h"
+#include "qpid/management/Manageable.h"
#include "qpid/sys/Monitor.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/log/Logger.h"
-#include "qpid/Url.h"
-#include "qpid/RefCounted.h"
-#include <boost/optional.hpp>
-#include <boost/function.hpp>
+#include <boost/bind.hpp>
#include <boost/intrusive_ptr.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
#include <map>
#include <vector>
namespace qpid {
+
+namespace framing {
+class AMQBody;
+class Uuid;
+}
+
namespace cluster {
-class ConnectionInterceptor;
+class Connection;
+class EventFrame;
/**
- * Connection to the cluster.
- * Keeps cluster membership data.
+ * Connection to the cluster
*/
-class Cluster : private Cpg::Handler, public RefCounted
-{
+class Cluster : private Cpg::Handler, public management::Manageable {
public:
- typedef boost::tuple<Cpg::Id, void*> ShadowConnectionId;
+ typedef boost::intrusive_ptr<Connection> ConnectionPtr;
+ typedef std::vector<ConnectionPtr> ConnectionVector;
- /** Details of a cluster member */
- struct Member {
- Cpg::Id id;
- Url url;
- };
-
- typedef std::vector<Member> MemberList;
-
- /**
- * Join a cluster.
- * @param name of the cluster.
- * @param url of this broker, sent to the cluster.
- */
- Cluster(const std::string& name, const Url& url, broker::Broker&);
+ // Public functions are thread safe unless otherwise mentioned in a comment.
+ // Construct the cluster in plugin earlyInitialize.
+ Cluster(const ClusterSettings&, broker::Broker&);
virtual ~Cluster();
- /** Initialize interceptors for a new connection */
- void initialize(broker::Connection&);
-
- /** Get the current cluster membership. */
- MemberList getMembers() const;
-
- /** Number of members in the cluster. */
- size_t size() const;
+ // Called by plugin initialize: cluster start-up requires transport plugins .
+ // Thread safety: only called by plugin initialize.
+ void initialize();
- bool empty() const { return size() == 0; }
+ // Connection map.
+ void addLocalConnection(const ConnectionPtr&);
+ void addShadowConnection(const ConnectionPtr&);
+ void erase(const ConnectionId&);
- /** Send frame to the cluster */
- void send(const framing::AMQFrame&, ConnectionInterceptor*);
+ // 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 */
+ // Leave the cluster - called when fatal errors occur.
void leave();
-
- // Cluster frame handing functions
- void notify(const std::string& url);
- void connectionClose();
+
+ // Update completed - called in update thread
+ void updateInDone(const ClusterMap&);
+ void updateInRetracted();
+
+ 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; }
private:
- typedef Cpg::Id Id;
- typedef std::map<Id, Member> MemberMap;
- typedef std::map<ShadowConnectionId, ConnectionInterceptor*> ShadowConnectionMap;
- typedef std::set<ConnectionInterceptor*> LocalConnectionSet;
-
- /** Message sent over the cluster. */
- struct Message {
- framing::AMQFrame frame; Id from; void* connection;
- Message(const framing::AMQFrame& f, const Id i, void* c)
- : frame(f), from(i), connection(c) {}
- };
- typedef PollableQueue<Message> MessageQueue;
-
- boost::function<void()> shutdownNext;
-
- void notify(); ///< Notify cluster of my details.
+ typedef sys::Monitor::ScopedLock Lock;
- /** CPG deliver callback. */
- void deliver(
+ 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,
+ Lock&);
+ void ready(const MemberId&, const std::string&, Lock&);
+ void configChange(const MemberId&, const std::string& current, Lock& l);
+ void messageExpired(const MemberId&, uint64_t, Lock& l);
+ void errorCheck(const MemberId&, uint8_t type, SequenceNumber frameSeq, Lock&);
+
+ void shutdown(const MemberId&, const framing::Uuid& shutdownId, 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 initMapCompleted(Lock&);
+
+
+
+ // == Called in CPG dispatch thread
+ void deliver( // CPG deliver callback.
cpg_handle_t /*handle*/,
- struct cpg_name *group,
+ const struct cpg_name *group,
uint32_t /*nodeid*/,
uint32_t /*pid*/,
void* /*msg*/,
int /*msg_len*/);
- /** CPG config change callback */
- void configChange(
+ void deliverEvent(const Event&);
+
+ void configChange( // CPG config change callback.
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*/
+ 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*/
);
- /** Callback to handle delivered frames from the deliverQueue. */
- void deliverQueueCb(const MessageQueue::iterator& begin,
- const MessageQueue::iterator& end);
-
- /** Callback to multi-cast frames from mcastQueue */
- void mcastQueueCb(const MessageQueue::iterator& begin,
- const MessageQueue::iterator& end);
+ // == 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&);
- /** Callback to dispatch CPG events. */
- void dispatch(sys::DispatchHandle&);
- /** Callback if CPG fd is disconnected. */
- void disconnect(sys::DispatchHandle&);
+ // == Called in connection IO threads .
+ void checkUpdateIn(Lock&);
- void handleMethod(Id from, ConnectionInterceptor* connection, framing::AMQMethodBody& method);
+ // == Called in UpdateClient thread.
+ void updateOutDone();
+ void updateOutError(const std::exception&);
+ void updateOutDone(Lock&);
- ConnectionInterceptor* getShadowConnection(const Cpg::Id&, void*);
-
- mutable sys::Monitor lock; // Protect access to members.
- broker::Broker* broker;
+ // 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;
- Cpg::Name name;
- Url url;
- MemberMap members;
- Id self;
- ShadowConnectionMap shadowConnectionMap;
- LocalConnectionSet localConnectionSet;
- ShadowConnectionOutputHandler shadowOut;
- sys::DispatchHandle cpgDispatchHandle;
- MessageQueue deliverQueue;
- MessageQueue mcastQueue;
-
- friend std::ostream& operator <<(std::ostream&, const Cluster&);
- friend std::ostream& operator <<(std::ostream&, const MemberMap::value_type&);
- friend std::ostream& operator <<(std::ostream&, const MemberMap&);
+ 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;
+ Quorum quorum;
+ LockedConnectionMap localConnections;
+
+ // Used only in deliverEventQueue thread or when stalled for update.
+ Decoder decoder;
+ bool discarding;
+
+
+ // Remaining members are protected by lock.
+
+ // TODO aconway 2009-03-06: Most of these members are also only used in
+ // deliverFrameQueue thread or during stall. Review and separate members
+ // that require a lock, drop lock when not needed.
+
+ mutable sys::Monitor lock;
+
+
+ // Local cluster state, cluster map
+ enum {
+ INIT, ///< Establishing inital cluster stattus.
+ 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;
+ size_t lastSize;
+ bool lastBroker;
+ sys::Thread updateThread;
+ boost::optional<ClusterMap> updatedMap;
+ bool updateRetracted;
+ ErrorCheck error;
+ UpdateReceiver updateReceiver;
+
+ friend std::ostream& operator<<(std::ostream&, const Cluster&);
+ friend class ClusterDispatcher;
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/ClusterMap.cpp b/cpp/src/qpid/cluster/ClusterMap.cpp
new file mode 100644
index 0000000000..8cac470ef3
--- /dev/null
+++ b/cpp/src/qpid/cluster/ClusterMap.cpp
@@ -0,0 +1,175 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#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());
+}
+
+void assignFieldTable(FieldTable& ft, const ClusterMap::Map& map) {
+ ft.clear();
+ for_each(map.begin(), map.end(), bind(&insertFieldTableFromMapValue, ref(ft), _1));
+}
+
+}
+
+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 << " ";
+ }
+ return o;
+}
+
+bool ClusterMap::updateRequest(const MemberId& id, const string& url) {
+ if (isAlive(id)) {
+ joiners[id] = Url(url);
+ return true;
+ }
+ 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/cpp/src/qpid/cluster/ClusterMap.h b/cpp/src/qpid/cluster/ClusterMap.h
new file mode 100644
index 0000000000..98572813a8
--- /dev/null
+++ b/cpp/src/qpid/cluster/ClusterMap.h
@@ -0,0 +1,105 @@
+#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() { 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/cpp/src/qpid/cluster/ClusterPlugin.cpp b/cpp/src/qpid/cluster/ClusterPlugin.cpp
index 1d07660455..e4aee6730b 100644
--- a/cpp/src/qpid/cluster/ClusterPlugin.cpp
+++ b/cpp/src/qpid/cluster/ClusterPlugin.cpp
@@ -16,89 +16,157 @@
*
*/
-#include "ConnectionInterceptor.h"
+#include "config.h"
+#include "qpid/cluster/Connection.h"
+#include "qpid/cluster/ConnectionCodec.h"
+#include "qpid/cluster/ClusterSettings.h"
-#include "qpid/broker/Broker.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/shared_ptr.h"
-
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/log/Statement.h"
+
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/management/IdAllocator.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::IdAllocator;
+using management::ManagementAgent;
-struct ClusterValues {
- string name;
- string url;
-
- Url getUrl(uint16_t port) const {
- if (url.empty()) return Url::getIpAddressesUrl(port);
- return Url(url);
- }
-};
-/** Note separating options from values to work around boost version differences.
+/** 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 {
- ClusterValues& values;
+ ClusterSettings& settings;
- ClusterOptions(ClusterValues& v) : Options("Cluster Options"), values(v) {
+ ClusterOptions(ClusterSettings& v) : Options("Cluster Options"), settings(v) {
addOptions()
- ("cluster-name", optValue(values.name, "NAME"), "Name of cluster to join")
- ("cluster-url", optValue(values.url,"URL"),
+ ("cluster-name", optValue(settings.name, "NAME"), "Name of cluster to join")
+ ("cluster-url", optValue(settings.url,"URL"),
"URL of this broker, advertized to the cluster.\n"
"Defaults to a URL listing all the local IP addresses\n")
+ ("cluster-username", optValue(settings.username, "USER"), "Username for connections between brokers")
+ ("cluster-password", optValue(settings.password, "PASS"), "Password for connections between brokers")
+ ("cluster-mechanism", optValue(settings.mechanism, "MECH"), "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.")
;
}
};
+struct UpdateClientIdAllocator : management::IdAllocator
+{
+ qpid::sys::AtomicValue<uint64_t> sequence;
+
+ UpdateClientIdAllocator() : sequence(0x4000000000000000LL) {}
+
+ uint64_t getIdFor(management::Manageable* m)
+ {
+ if (isUpdateQueue(m) || isUpdateExchange(m) || isUpdateSession(m) || isUpdateBinding(m)) {
+ return ++sequence;
+ } else {
+ return 0;
+ }
+ }
+
+ bool isUpdateQueue(management::Manageable* manageable)
+ {
+ qpid::broker::Queue* queue = dynamic_cast<qpid::broker::Queue*>(manageable);
+ return queue && queue->getName() == UpdateClient::UPDATE;
+ }
+
+ bool isUpdateExchange(management::Manageable* manageable)
+ {
+ qpid::broker::Exchange* exchange = dynamic_cast<qpid::broker::Exchange*>(manageable);
+ return exchange && exchange->getName() == UpdateClient::UPDATE;
+ }
+
+ bool isUpdateSession(management::Manageable* manageable)
+ {
+ broker::SessionState* session = dynamic_cast<broker::SessionState*>(manageable);
+ return session && session->getId().getName() == UpdateClient::UPDATE;
+ }
+
+ bool isUpdateBinding(management::Manageable* manageable)
+ {
+ broker::Exchange::Binding* binding = dynamic_cast<broker::Exchange::Binding*>(manageable);
+ return binding && binding->queue->getName() == UpdateClient::UPDATE;
+ }
+};
+
struct ClusterPlugin : public Plugin {
- ClusterValues values;
+ ClusterSettings settings;
ClusterOptions options;
- boost::intrusive_ptr<Cluster> cluster;
+ Cluster* cluster;
+ boost::scoped_ptr<ConnectionCodec::Factory> factory;
- ClusterPlugin() : options(values) {}
+ 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 init(broker::Broker& b) {
- if (values.name.empty()) return; // Only if --cluster-name option was specified.
- if (cluster) throw Exception("Cluster plugin cannot be initialized twice in one process.");
- cluster = new Cluster(values.name, values.getUrl(b.getPort()), b);
- b.addFinalizer(boost::bind(&ClusterPlugin::shutdown, this));
+ 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);
+ broker->setConnectionFactory(
+ boost::shared_ptr<sys::ConnectionCodec::Factory>(
+ new ConnectionCodec::Factory(broker->getConnectionFactory(), *cluster)));
+ ManagementAgent* mgmt = broker->getManagementAgent();
+ if (mgmt) {
+ std::auto_ptr<IdAllocator> allocator(new UpdateClientIdAllocator());
+ mgmt->setAllocator(allocator);
+ }
}
- template <class T> void init(T& t) {
- if (cluster) cluster->initialize(t);
+ void disallow(ManagementAgent* agent, const string& className, const string& methodName) {
+ string message = "Management method " + className + ":" + methodName + " is not allowed on a clustered broker.";
+ agent->disallow(className, methodName, message);
}
-
- template <class T> bool init(Plugin::Target& target) {
- T* t = dynamic_cast<T*>(&target);
- if (t) init(*t);
- return t;
+ void disallowManagementMethods(ManagementAgent* agent) {
+ if (!agent) return;
+ disallow(agent, "queue", "purge");
+ disallow(agent, "session", "detach");
+ disallow(agent, "session", "close");
+ disallow(agent, "connection", "close");
}
- void earlyInitialize(Plugin::Target&) {}
-
void initialize(Plugin::Target& target) {
- if (init<broker::Broker>(target)) return;
- if (!cluster) return; // Remaining plugins only valid if cluster initialized.
- if (init<broker::Connection>(target)) return;
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (broker && cluster) {
+ disallowManagementMethods(broker->getManagementAgent());
+ cluster->initialize();
+ }
}
-
- void shutdown() { cluster = 0; }
};
static ClusterPlugin instance; // Static initialization.
-// For test purposes.
-boost::intrusive_ptr<Cluster> getGlobalCluster() { return instance.cluster; }
-
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/sys/posix/PrivatePosix.h b/cpp/src/qpid/cluster/ClusterSettings.h
index 33c0cd81bc..d37c7792bf 100644
--- a/cpp/src/qpid/sys/posix/PrivatePosix.h
+++ b/cpp/src/qpid/cluster/ClusterSettings.h
@@ -1,5 +1,5 @@
-#ifndef _sys_posix_PrivatePosix_h
-#define _sys_posix_PrivatePosix_h
+#ifndef QPID_CLUSTER_CLUSTERSETTINGS_H
+#define QPID_CLUSTER_CLUSTERSETTINGS_H
/*
*
@@ -22,31 +22,29 @@
*
*/
-#include "qpid/sys/Time.h"
-
-struct timespec;
-struct timeval;
+#include <qpid/Url.h>
+#include <string>
namespace qpid {
-namespace sys {
+namespace cluster {
-// Private Time related implementation details
-struct timespec& toTimespec(struct timespec& ts, const Duration& t);
-struct timeval& toTimeval(struct timeval& tv, const Duration& t);
-Duration toTime(const struct timespec& ts);
+struct ClusterSettings {
+ std::string name;
+ std::string url;
+ bool quorum;
+ size_t readMax;
+ std::string username, password, mechanism;
+ size_t size;
-// Private fd related implementation details
-class IOHandlePrivate {
-public:
- IOHandlePrivate(int f = -1) :
- fd(f)
+ ClusterSettings() : quorum(false), readMax(10), mechanism("ANONYMOUS"), size(1)
{}
-
- int fd;
+
+ Url getUrl(uint16_t port) const {
+ if (url.empty()) return Url::getIpAddressesUrl(port);
+ return Url(url);
+ }
};
-int toFd(const IOHandlePrivate* h);
-
-}}
+}} // namespace qpid::cluster
-#endif /*!_sys_posix_PrivatePosix_h*/
+#endif /*!QPID_CLUSTER_CLUSTERSETTINGS_H*/
diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp
new file mode 100644
index 0000000000..d223244f15
--- /dev/null
+++ b/cpp/src/qpid/cluster/Connection.cpp
@@ -0,0 +1,479 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Connection.h"
+#include "UpdateClient.h"
+#include "Cluster.h"
+#include "UpdateReceiver.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/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/ConnectionCloseBody.h"
+#include "qpid/framing/ConnectionCloseOkBody.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/current_function.hpp>
+
+// TODO aconway 2008-11-03:
+//
+// Refactor code for receiving an update into a separate UpdateConnection
+// class.
+//
+
+
+namespace qpid {
+namespace cluster {
+
+using namespace framing;
+using namespace framing::cluster;
+
+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& logId,
+ const ConnectionId& id, unsigned int ssf)
+ : cluster(c), self(id), catchUp(false), output(*this, out),
+ connection(&output, cluster.getBroker(), shadowPrefix+logId, ssf), expectProtocolHeader(false),
+ mcastFrameHandler(cluster.getMulticast(), self),
+ consumerNumbering(c.getUpdateReceiver().consumerNumbering)
+{ init(); }
+
+// Local connection
+Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
+ const std::string& logId, MemberId member,
+ bool isCatchUp, bool isLink, unsigned int ssf
+) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), output(*this, out),
+ connection(&output, cluster.getBroker(),
+ isCatchUp ? shadowPrefix+logId : logId,
+ ssf,
+ isLink,
+ isCatchUp ? ++catchUpId : 0),
+ expectProtocolHeader(isLink), mcastFrameHandler(cluster.getMulticast(), self),
+ consumerNumbering(c.getUpdateReceiver().consumerNumbering)
+{ init(); }
+
+void Connection::init() {
+ QPID_LOG(debug, cluster << " new connection: " << *this);
+ if (isLocalClient()) {
+ connection.setClusterOrderOutput(mcastFrameHandler); // Actively send cluster-order frames from local node
+ cluster.addLocalConnection(this);
+ giveReadCredit(cluster.getSettings().readMax);
+ }
+ else { // Shadow or catch-up connection
+ connection.setClusterOrderOutput(nullFrameHandler); // Passive, discard cluster-order frames
+ connection.setClientThrottling(false); // Disable client throttling, done by active node.
+ connection.setShadow(); // Mark the broker connection as a shadow.
+ }
+ if (!isCatchUp())
+ connection.setErrorListener(this);
+}
+
+void Connection::giveReadCredit(int credit) {
+ if (cluster.getSettings().readMax && credit)
+ output.giveReadCredit(credit);
+}
+
+Connection::~Connection() {
+ connection.setErrorListener(0);
+ QPID_LOG(debug, cluster << " deleted connection: " << *this);
+}
+
+bool Connection::doOutput() {
+ return output.doOutput();
+}
+
+// Received from a directly connected client.
+void Connection::received(framing::AMQFrame& f) {
+ 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() { connection.giveReadCredit(credit); }
+};
+
+// 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.
+void Connection::closed() {
+ try {
+ if (catchUp) {
+ QPID_LOG(critical, cluster << " catch-up connection closed prematurely " << *this);
+ cluster.leave();
+ }
+ else if (isUpdated()) {
+ QPID_LOG(debug, cluster << " closed update connection " << *this);
+ connection.closed();
+ }
+ else if (isLocal()) {
+ QPID_LOG(debug, cluster << " local close of replicated connection " << *this);
+ // 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();
+ 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 () {
+ assert(!catchUp);
+ connection.closed();
+ cluster.erase(self);
+}
+
+// The connection has been killed for misbehaving
+void Connection::abort() {
+ connection.abort();
+ cluster.erase(self);
+}
+
+// Member of a shadow connection left the cluster.
+void Connection::left() {
+ assert(isShadow());
+ connection.closed();
+}
+
+// ConnectoinCodec::decode receives read buffers from directly-connected clients.
+size_t Connection::decode(const char* buffer, size_t size) {
+ if (catchUp) { // Handle catch-up locally.
+ Buffer buf(const_cast<char*>(buffer), size);
+ while (localDecoder.decode(buf))
+ received(localDecoder.getFrame());
+ }
+ else { // Multicast local connections.
+ assert(isLocal());
+ const char* remainingData = buffer;
+ size_t remainingSize = size;
+ if (expectProtocolHeader) {
+ //If this is an outgoing link, we will receive a protocol
+ //header which needs to be decoded first
+ framing::ProtocolInitiation pi;
+ Buffer buf(const_cast<char*>(buffer), size);
+ if (pi.decode(buf)) {
+ //TODO: check the version is correct
+ QPID_LOG(debug, "Outgoing clustered link connection received INIT(" << pi << ")");
+ expectProtocolHeader = false;
+ remainingData = buffer + pi.encodedSize();
+ remainingSize = size - pi.encodedSize();
+ } else {
+ QPID_LOG(debug, "Not enough data for protocol header on outgoing clustered link");
+ giveReadCredit(1); // We're not going to mcast so give read credit now.
+ return 0;
+ }
+ }
+ cluster.getMulticast().mcastBuffer(remainingData, remainingSize, self);
+ }
+ return size;
+}
+
+broker::SessionState& Connection::sessionState() {
+ return *connection.getChannel(currentChannel).getSession();
+}
+
+broker::SemanticState& Connection::semanticState() {
+ return sessionState().getSemanticState();
+}
+
+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();
+ 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& username, const string& fragment, uint32_t sendMax) {
+ 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);
+ cluster.updateInDone(ClusterMap(joiners, members, frameSeq));
+ consumerNumbering.clear();
+ self.second = 0; // Mark this as completed update connection.
+}
+
+void Connection::retractOffer() {
+ QPID_LOG(debug, cluster << " incoming update retracted on connection " << *this);
+ cluster.updateInRetracted();
+ self.second = 0; // Mark this as completed update connection.
+}
+
+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->enqueued(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::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";
+ 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);
+ QPID_LOG(debug, cluster << " updated exchange " << ex->getName());
+}
+
+void Connection::queue(const std::string& encoded) {
+ Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
+ broker::Queue::shared_ptr q = broker::Queue::decode(cluster.getBroker().getQueues(), buf);
+ QPID_LOG(debug, cluster << " updated queue " << q->getName());
+}
+
+void Connection::sessionError(uint16_t , const std::string& msg) {
+ cluster.flagError(*this, ERROR_TYPE_SESSION, msg);
+
+}
+
+void Connection::connectionError(const std::string& msg) {
+ cluster.flagError(*this, ERROR_TYPE_CONNECTION, msg);
+}
+
+void Connection::addQueueListener(const std::string& q, uint32_t listener) {
+ if (listener >= consumerNumbering.size())
+ throw Exception(QPID_MSG("Invalid listener ID: " << listener));
+ findQueue(q)->getListeners().addListener(consumerNumbering[listener]);
+}
+
+}} // Namespace qpid::cluster
+
diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h
new file mode 100644
index 0000000000..7f94338348
--- /dev/null
+++ b/cpp/src/qpid/cluster/Connection.h
@@ -0,0 +1,210 @@
+#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 "EventFrame.h"
+#include "McastFrameHandler.h"
+#include "UpdateReceiver.h"
+
+#include "qpid/broker/Connection.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/framing/SequenceNumber.h"
+#include "qpid/framing/FrameDecoder.h"
+
+#include <iosfwd>
+
+namespace qpid {
+
+namespace framing { class AMQFrame; }
+
+namespace broker {
+class SemanticState;
+class QueuedMessage;
+class TxBuffer;
+class TxAccept;
+}
+
+namespace cluster {
+class Cluster;
+class Event;
+
+/** 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& logId, MemberId, bool catchUp, bool isLink,
+ unsigned int ssf);
+ /** Shadow connection. */
+ Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& logId, const ConnectionId& id,
+ unsigned int ssf);
+ ~Connection();
+
+ ConnectionId getId() const { return self; }
+ broker::Connection& getBrokerConnection() { return connection; }
+
+ /** 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();
+ bool hasOutput() { return connection.hasOutput(); }
+ void idleOut() { connection.idleOut(); }
+ void idleIn() { connection.idleIn(); }
+
+ /** Called if the connectors member has left the cluster */
+ void left();
+
+ // 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 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& 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 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 queue/exchange replication.
+ void queue(const std::string& encoded);
+ void exchange(const std::string& encoded);
+
+ void giveReadCredit(int credit);
+ void announce(uint32_t) {} // handled by Cluster.
+ void abort();
+ void deliverClose();
+
+ OutputInterceptor& getOutput() { return output; }
+
+ void addQueueListener(const std::string& queue, uint32_t listener);
+
+ private:
+ struct NullFrameHandler : public framing::FrameHandler {
+ void handle(framing::AMQFrame&) {}
+ };
+
+
+ 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) { output.deliverDoOutput(limit); }
+
+ boost::shared_ptr<broker::Queue> findQueue(const std::string& qname);
+ broker::SessionState& sessionState();
+ broker::SemanticState& semanticState();
+ broker::QueuedMessage getUpdateMessage();
+
+ Cluster& cluster;
+ ConnectionId self;
+ bool catchUp;
+ OutputInterceptor output;
+ framing::FrameDecoder localDecoder;
+ broker::Connection connection;
+ framing::SequenceNumber deliverSeq;
+ framing::ChannelId currentChannel;
+ boost::shared_ptr<broker::TxBuffer> txBuffer;
+ bool expectProtocolHeader;
+ McastFrameHandler mcastFrameHandler;
+ UpdateReceiver::ConsumerNumbering& consumerNumbering;
+
+ 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/cpp/src/qpid/cluster/ConnectionCodec.cpp b/cpp/src/qpid/cluster/ConnectionCodec.cpp
new file mode 100644
index 0000000000..8f6f1d9ad5
--- /dev/null
+++ b/cpp/src/qpid/cluster/ConnectionCodec.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/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,
+ unsigned int ssf) {
+ if (v == ProtocolVersion(0, 10))
+ return new ConnectionCodec(v, out, id, cluster, false, false, ssf);
+ else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) // Catch-up connection
+ return new ConnectionCodec(v, out, id, cluster, true, false, ssf);
+ return 0;
+}
+
+// Used for outgoing Link connections
+sys::ConnectionCodec*
+ConnectionCodec::Factory::create(sys::OutputControl& out, const std::string& logId,
+ unsigned int ssf) {
+ return new ConnectionCodec(ProtocolVersion(0,10), out, logId, cluster, false, true, ssf);
+}
+
+ConnectionCodec::ConnectionCodec(
+ const ProtocolVersion& v, sys::OutputControl& out,
+ const std::string& logId, Cluster& cluster, bool catchUp, bool isLink, unsigned int ssf
+) : codec(out, logId, isLink),
+ interceptor(new Connection(cluster, codec, logId, cluster.getId(), catchUp, isLink, ssf))
+{
+ 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/cpp/src/qpid/cluster/ConnectionCodec.h b/cpp/src/qpid/cluster/ConnectionCodec.h
new file mode 100644
index 0000000000..74cb3c507d
--- /dev/null
+++ b/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,
+ unsigned int conn_ssf);
+ sys::ConnectionCodec* create(sys::OutputControl&, const std::string& id,
+ unsigned int conn_ssf);
+ };
+
+ ConnectionCodec(const framing::ProtocolVersion&, sys::OutputControl& out,
+ const std::string& logId, Cluster& c, bool catchUp, bool isLink,
+ unsigned int ssf);
+ ~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;
+
+
+ private:
+ amqp_0_10::Connection codec;
+ boost::intrusive_ptr<cluster::Connection> interceptor;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_CONNCTIONCODEC_H*/
diff --git a/cpp/src/qpid/cluster/ConnectionInterceptor.cpp b/cpp/src/qpid/cluster/ConnectionInterceptor.cpp
deleted file mode 100644
index c13651eccb..0000000000
--- a/cpp/src/qpid/cluster/ConnectionInterceptor.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "ConnectionInterceptor.h"
-#include "qpid/framing/ClusterConnectionCloseBody.h"
-#include "qpid/framing/ClusterConnectionDoOutputBody.h"
-#include "qpid/framing/AMQFrame.h"
-
-namespace qpid {
-namespace cluster {
-
-using namespace framing;
-
-template <class T, class U, class V> void shift(T& a, U& b, const V& c) { a = b; b = c; }
-
-ConnectionInterceptor::ConnectionInterceptor(
- broker::Connection& conn, Cluster& clust, Cluster::ShadowConnectionId shadowId_)
- : connection(&conn), cluster(clust), isClosed(false), shadowId(shadowId_)
-{
- connection->addFinalizer(boost::bind(operator delete, this));
- // Attach my functions to Connection extension points.
- shift(receivedNext, connection->receivedFn, boost::bind(&ConnectionInterceptor::received, this, _1));
- shift(closedNext, connection->closedFn, boost::bind(&ConnectionInterceptor::closed, this));
- shift(doOutputNext, connection->doOutputFn, boost::bind(&ConnectionInterceptor::doOutput, this));
-}
-
-ConnectionInterceptor::~ConnectionInterceptor() {
- assert(connection == 0);
-}
-
-void ConnectionInterceptor::received(framing::AMQFrame& f) {
- if (isClosed) return;
- cluster.send(f, this);
-}
-
-void ConnectionInterceptor::deliver(framing::AMQFrame& f) {
- receivedNext(f);
-}
-
-void ConnectionInterceptor::closed() {
- if (isClosed) return;
- try {
- // Called when the local network connection is closed. We still
- // need to process any outstanding cluster frames for this
- // connection to ensure our sessions are up-to-date. We defer
- // closing the Connection object till deliverClosed(), but replace
- // its output handler with a null handler since the network output
- // handler will be deleted.
- //
- connection->setOutputHandler(&discardHandler);
- cluster.send(AMQFrame(in_place<ClusterConnectionCloseBody>()), this);
- isClosed = true;
- }
- catch (const std::exception& e) {
- QPID_LOG(error, QPID_MSG("While closing connection: " << e.what()));
- }
-}
-
-void ConnectionInterceptor::deliverClosed() {
- closedNext();
- // Drop reference so connection will be deleted, which in turn
- // will delete this via finalizer added in ctor.
- connection = 0;
-}
-
-void ConnectionInterceptor::dirtyClose() {
- // Not closed via cluster self-delivery but closed locally.
- // Used for dirty cluster shutdown where active connections
- // must be cleaned up.
- connection = 0;
-}
-
-bool ConnectionInterceptor::doOutput() {
- // FIXME aconway 2008-08-15: this is not correct.
- // Run in write threads so order of execution of doOutput is not determinate.
- // Will only work reliably for in single-consumer tests.
-
- if (connection->hasOutput()) {
- cluster.send(AMQFrame(in_place<ClusterConnectionDoOutputBody>()), this);
- return doOutputNext();
- }
- return false;
-}
-
-void ConnectionInterceptor::deliverDoOutput() {
- // FIXME aconway 2008-08-15: see comment in doOutput.
- if (isShadow())
- doOutputNext();
-}
-
-}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/ConnectionInterceptor.h b/cpp/src/qpid/cluster/ConnectionInterceptor.h
deleted file mode 100644
index 370572bd9d..0000000000
--- a/cpp/src/qpid/cluster/ConnectionInterceptor.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef QPID_CLUSTER_CONNECTIONPLUGIN_H
-#define QPID_CLUSTER_CONNECTIONPLUGIN_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 "Cluster.h"
-#include "qpid/broker/Connection.h"
-#include "qpid/sys/ConnectionOutputHandler.h"
-
-namespace qpid {
-namespace framing { class AMQFrame; }
-namespace cluster {
-
-/**
- * Plug-in associated with broker::Connections, both local and shadow.
- */
-class ConnectionInterceptor {
- public:
- ConnectionInterceptor(broker::Connection&, Cluster&,
- Cluster::ShadowConnectionId shadowId=Cluster::ShadowConnectionId(0,0));
- ~ConnectionInterceptor();
-
- Cluster::ShadowConnectionId getShadowId() const { return shadowId; }
-
- bool isLocal() const { return shadowId == Cluster::ShadowConnectionId(0,0); }
-
- // self-delivery of intercepted extension points.
- void deliver(framing::AMQFrame& f);
- void deliverClosed();
- void deliverDoOutput();
-
- void dirtyClose();
-
- private:
- struct NullConnectionHandler : public qpid::sys::ConnectionOutputHandler {
- void close() {}
- void send(framing::AMQFrame&) {}
- void doOutput() {}
- void activateOutput() {}
- };
-
- bool isShadow() { return shadowId != Cluster::ShadowConnectionId(0,0); }
-
- // Functions to intercept to Connection extension points.
- void received(framing::AMQFrame&);
- void closed();
- bool doOutput();
-
- boost::function<void (framing::AMQFrame&)> receivedNext;
- boost::function<void ()> closedNext;
- boost::function<bool ()> doOutputNext;
-
- boost::intrusive_ptr<broker::Connection> connection;
- Cluster& cluster;
- NullConnectionHandler discardHandler;
- bool isClosed;
- Cluster::ShadowConnectionId shadowId;
-};
-
-}} // namespace qpid::cluster
-
-#endif /*!QPID_CLUSTER_CONNECTIONPLUGIN_H*/
-
diff --git a/cpp/src/qpid/cluster/Cpg.cpp b/cpp/src/qpid/cluster/Cpg.cpp
index 2ffd3509bf..3ae0c970c7 100644
--- a/cpp/src/qpid/cluster/Cpg.cpp
+++ b/cpp/src/qpid/cluster/Cpg.cpp
@@ -16,33 +16,85 @@
*
*/
-#include "Cpg.h"
+#include "qpid/cluster/Cpg.h"
#include "qpid/sys/Mutex.h"
-// Note cpg is currently unix-specific. Refactor if availble on other platforms.
+#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;
- check(cpg_context_get(handle, &cpg), "Cannot get CPG instance.");
+ 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))) {
+ QPID_LOG(info, c.opName << " successful.");
+ 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,
@@ -65,103 +117,119 @@ void Cpg::globalConfigChange(
int Cpg::getFd() {
int fd;
- check(cpg_fd_get(handle, &fd), "Cannot get CPG file descriptor");
+ 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 = { &globalDeliver, &globalConfigChange };
- check(cpg_initialize(&handle, &callbacks), "Cannot initialize CPG");
- check(cpg_context_set(handle, this), "Cannot set CPG context");
+ 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();
- QPID_LOG(debug, "Initialized CPG handle 0x" << std::hex << handle);
}
Cpg::~Cpg() {
try {
shutdown();
} catch (const std::exception& e) {
- QPID_LOG(error, "Exception in Cpg destructor: " << e.what());
+ QPID_LOG(error, "Error during CPG shutdown: " << e.what());
}
}
-void Cpg::join(const Name& group) {
- check(cpg_join(handle, const_cast<Name*>(&group)),cantJoinMsg(group));
+void Cpg::join(const std::string& name) {
+ group = name;
+ callCpg ( cpgJoinOp );
}
-void Cpg::leave(const Name& group) {
- check(cpg_leave(handle,const_cast<Name*>(&group)),cantLeaveMsg(group));
+void Cpg::leave() {
+ callCpg ( cpgLeaveOp );
}
-bool Cpg::isFlowControlEnabled() {
+
+
+
+bool Cpg::mcast(const iovec* iov, int iovLen) {
+ // Check for flow control
cpg_flow_control_state_t flowState;
- check(cpg_flow_control_state_get(handle, &flowState), "Cannot get CPG flow control status.");
- return flowState == CPG_FLOW_CONTROL_ENABLED;
-}
-
-// TODO aconway 2008-08-07: better handling of flow control.
-// Wait for flow control to be disabled.
-// FIXME aconway 2008-08-08: does flow control check involve a round-trip? If so maybe remove...
-void Cpg::waitForFlowControl() {
- int delayNs=1000; // one millisecond
- int tries=8; // double the delay on each try.
- while (isFlowControlEnabled() && tries > 0) {
- QPID_LOG(warning, "CPG flow control enabled, retry in " << delayNs << "ns");
- ::usleep(delayNs);
- --tries;
- delayNs *= 2;
- };
- if (tries == 0) {
- // FIXME aconway 2008-08-07: this is a fatal leave-the-cluster condition.
- throw Cpg::Exception("CPG flow control enabled, failed to send.");
- }
-}
+ CPG_CHECK(cpg_flow_control_state_get(handle, &flowState), "Cannot get CPG flow control status.");
+ if (flowState == CPG_FLOW_CONTROL_ENABLED)
+ return false;
-void Cpg::mcast(const Name& group, const iovec* iov, int iovLen) {
- waitForFlowControl();
cpg_error_t result;
do {
result = cpg_mcast_joined(handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen);
- if (result != CPG_ERR_TRY_AGAIN) check(result, cantMcastMsg(group));
+ 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;
- check(cpg_finalize(handle), "Error in shutdown of CPG");
+
+ 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: return msg+": ok";
- case CPG_ERR_LIBRARY: return msg+": library";
- case CPG_ERR_TIMEOUT: return msg+": timeout";
- case CPG_ERR_TRY_AGAIN: return msg+": timeout. The aisexec daemon may not be running";
- case CPG_ERR_INVALID_PARAM: return msg+": invalid param";
- case CPG_ERR_NO_MEMORY: return msg+": no memory";
- case CPG_ERR_BAD_HANDLE: return msg+": bad handle";
- case CPG_ERR_ACCESS: return msg+": access denied. You may need to set your group ID to 'ais'";
- case CPG_ERR_NOT_EXIST: return msg+": not exist";
- case CPG_ERR_EXIST: return msg+": exist";
- case CPG_ERR_NOT_SUPPORTED: return msg+": not supported";
- case CPG_ERR_SECURITY: return msg+": security";
- case CPG_ERR_TOO_MANY_GROUPS: return msg+": too many groups";
- default:
- assert(0);
- return ": unknown";
+ 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();
}
@@ -170,27 +238,44 @@ std::string Cpg::cantMcastMsg(const Name& group) {
return "Cannot mcast to CPG group "+group.str();
}
-Cpg::Id Cpg::self() const {
+MemberId Cpg::self() const {
unsigned int nodeid;
- check(cpg_local_get(handle, &nodeid), "Cannot get local CPG identity");
- return Id(nodeid, getpid());
+ CPG_CHECK(cpg_local_get(handle, &nodeid), "Cannot get local CPG identity");
+ return MemberId(nodeid, getpid());
}
-ostream& operator<<(ostream& o, std::pair<cpg_address*,int> a) {
- ostream_iterator<Cpg::Id> i(o, " ");
- std::copy(a.first, a.first+a.second, i);
- return o;
+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& out, const Cpg::Id& id) {
- return out << id.getNodeId() << "-" << id.getPid();
+ostream& operator<<(ostream& o, const ConnectionId& c) {
+ return o << c.first << "-" << c.second;
}
-ostream& operator <<(ostream& out, const cpg_name& name) {
- return out << string(name.value, name.length);
+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/cpp/src/qpid/cluster/Cpg.h b/cpp/src/qpid/cluster/Cpg.h
index 96fd692a77..6b81c602bd 100644
--- a/cpp/src/qpid/cluster/Cpg.h
+++ b/cpp/src/qpid/cluster/Cpg.h
@@ -20,25 +20,18 @@
*/
#include "qpid/Exception.h"
+#include "qpid/cluster/types.h"
#include "qpid/sys/IOHandle.h"
-#include "qpid/cluster/Dispatchable.h"
+#include "qpid/sys/Mutex.h"
-#include <boost/tuple/tuple.hpp>
-#include <boost/tuple/tuple_comparison.hpp>
#include <boost/scoped_ptr.hpp>
#include <cassert>
-
#include <string.h>
-extern "C" {
-#include <openais/cpg.h>
-}
-
namespace qpid {
namespace cluster {
-
/**
* Lightweight C++ interface to cpg.h operations.
*
@@ -46,6 +39,7 @@ namespace cluster {
* On error all functions throw Cpg::Exception.
*
*/
+
class Cpg : public sys::IOHandle {
public:
struct Exception : public ::qpid::Exception {
@@ -53,6 +47,7 @@ class Cpg : public sys::IOHandle {
};
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()); }
@@ -65,14 +60,6 @@ class Cpg : public sys::IOHandle {
std::string str() const { return std::string(value, length); }
};
- // boost::tuple gives us == and < for free.
- struct Id : public boost::tuple<uint32_t, uint32_t> {
- Id(uint32_t n=0, uint32_t p=0) : boost::tuple<uint32_t, uint32_t>(n, p) {}
- Id(const cpg_address& addr) : boost::tuple<uint32_t, uint32_t>(addr.nodeid, addr.pid) {}
- uint32_t getNodeId() const { return boost::get<0>(*this); }
- uint32_t getPid() const { return boost::get<1>(*this); }
- };
-
static std::string str(const cpg_name& n) {
return std::string(n.value, n.length);
}
@@ -81,7 +68,7 @@ class Cpg : public sys::IOHandle {
virtual ~Handler() {};
virtual void deliver(
cpg_handle_t /*handle*/,
- struct cpg_name *group,
+ const struct cpg_name *group,
uint32_t /*nodeid*/,
uint32_t /*pid*/,
void* /*msg*/,
@@ -89,10 +76,10 @@ class Cpg : public sys::IOHandle {
virtual void configChange(
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*/
+ 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;
};
@@ -107,41 +94,115 @@ class Cpg : public sys::IOHandle {
/** Disconnect from CPG */
void shutdown();
- /** Dispatch CPG events.
- *@param type one of
- * - CPG_DISPATCH_ONE - dispatch exactly one event.
- * - CPG_DISPATCH_ALL - dispatch all available events, don't wait.
- * - CPG_DISPATCH_BLOCKING - blocking dispatch loop.
- */
- void dispatch(cpg_dispatch_t type) {
- check(cpg_dispatch(handle,type), "Error in CPG dispatch");
- }
+ void dispatchOne();
+ void dispatchAll();
+ void dispatchBlocking();
- void dispatchOne() { dispatch(CPG_DISPATCH_ONE); }
- void dispatchAll() { dispatch(CPG_DISPATCH_ALL); }
- void dispatchBlocking() { dispatch(CPG_DISPATCH_BLOCKING); }
+ void join(const std::string& group);
+ void leave();
- void join(const Name& group);
- void leave(const Name& group);
- void mcast(const Name& group, const iovec* iov, int iovLen);
+ /** 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; }
- Id self() const;
+ 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&); std::string cantMcastMsg(const Name&);
-
- static void check(cpg_error_t result, const std::string& msg) {
- if (result != CPG_OK) throw Exception(errorStr(result, msg));
- }
+ 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,
@@ -158,18 +219,13 @@ class Cpg : public sys::IOHandle {
struct cpg_address *joined, int nJoined
);
- bool isFlowControlEnabled();
- void waitForFlowControl();
-
cpg_handle_t handle;
Handler& handler;
bool isShutdown;
+ Name group;
+ sys::Mutex dispatchLock;
};
-std::ostream& operator <<(std::ostream& out, const cpg_name& name);
-std::ostream& operator <<(std::ostream& out, const Cpg::Id& id);
-std::ostream& operator <<(std::ostream& out, const std::pair<cpg_address*,int> addresses);
-
inline bool operator==(const cpg_name& a, const cpg_name& b) {
return a.length==b.length && strncmp(a.value, b.value, a.length) == 0;
}
@@ -177,5 +233,4 @@ inline bool operator!=(const cpg_name& a, const cpg_name& b) { return !(a == b);
}} // namespace qpid::cluster
-
#endif /*!CPG_H*/
diff --git a/cpp/src/qpid/cluster/Decoder.cpp b/cpp/src/qpid/cluster/Decoder.cpp
new file mode 100644
index 0000000000..23ba372d78
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/Decoder.h b/cpp/src/qpid/cluster/Decoder.h
new file mode 100644
index 0000000000..2e2af2868f
--- /dev/null
+++ b/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 {
+
+class 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/cpp/src/qpid/cluster/ErrorCheck.cpp b/cpp/src/qpid/cluster/ErrorCheck.cpp
new file mode 100644
index 0000000000..d66db8551d
--- /dev/null
+++ b/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->getCurrent()));
+ 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/cpp/src/qpid/cluster/ErrorCheck.h b/cpp/src/qpid/cluster/ErrorCheck.h
new file mode 100644
index 0000000000..de8cedafb3
--- /dev/null
+++ b/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 {
+
+class 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/cpp/src/qpid/cluster/Event.cpp b/cpp/src/qpid/cluster/Event.cpp
new file mode 100644
index 0000000000..52564990f6
--- /dev/null
+++ b/cpp/src/qpid/cluster/Event.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/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/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) {
+ if (buf.available() <= HEADER_SIZE)
+ throw Exception("Not enough for multicast header");
+ type = (EventType)buf.getOctet();
+ if(type != DATA && type != CONTROL)
+ throw Exception("Invalid multicast event type");
+ connectionId = ConnectionId(m, buf.getLongLong());
+ size = buf.getLong();
+}
+
+Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) {
+ Event e;
+ e.decode(m, buf); // Header
+ if (buf.available() < e.size)
+ throw Exception("Not enough data for multicast event");
+ 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/cpp/src/qpid/cluster/Event.h b/cpp/src/qpid/cluster/Event.h
new file mode 100644
index 0000000000..07f74d3ba5
--- /dev/null
+++ b/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/RefCountedBuffer.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 + HEADER_SIZE; }
+ const char* getData() const { return store + HEADER_SIZE; }
+
+ // Store including header
+ char* getStore() { return store; }
+ const char* getStore() const { return store; }
+
+ const framing::AMQFrame& getFrame() const;
+
+ operator framing::Buffer() const;
+
+ iovec toIovec() const;
+
+ private:
+ void encodeHeader() const;
+
+ RefCountedBuffer::pointer 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/cpp/src/qpid/cluster/EventFrame.cpp b/cpp/src/qpid/cluster/EventFrame.cpp
new file mode 100644
index 0000000000..5fbe1fe57c
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/EventFrame.h b/cpp/src/qpid/cluster/EventFrame.h
new file mode 100644
index 0000000000..61447c5525
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/ExpiryPolicy.cpp b/cpp/src/qpid/cluster/ExpiryPolicy.cpp
new file mode 100644
index 0000000000..190eeb7293
--- /dev/null
+++ b/cpp/src/qpid/cluster/ExpiryPolicy.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/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(0), 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(policy), expiryId(id) {}
+ void fire() { expiryPolicy->sendExpire(expiryId); }
+ boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
+ const uint64_t expiryId;
+};
+
+void ExpiryPolicy::willExpire(broker::Message& m) {
+ uint64_t id = expiryId++;
+ assert(unexpiredById.find(id) == unexpiredById.end());
+ assert(unexpiredByMessage.find(&m) == unexpiredByMessage.end());
+ unexpiredById[id] = &m;
+ unexpiredByMessage[&m] = id;
+ timer.add(new ExpiryTask(this, id, m.getExpiration()));
+}
+
+void ExpiryPolicy::forget(broker::Message& m) {
+ MessageIdMap::iterator i = unexpiredByMessage.find(&m);
+ assert(i != unexpiredByMessage.end());
+ unexpiredById.erase(i->second);
+ unexpiredByMessage.erase(i);
+}
+
+bool ExpiryPolicy::hasExpired(broker::Message& m) {
+ return unexpiredByMessage.find(&m) == unexpiredByMessage.end();
+}
+
+void ExpiryPolicy::sendExpire(uint64_t id) {
+ mcast.mcastControl(framing::ClusterMessageExpiredBody(framing::ProtocolVersion(), id), memberId);
+}
+
+void ExpiryPolicy::deliverExpire(uint64_t id) {
+ IdMessageMap::iterator i = unexpiredById.find(id);
+ if (i != unexpiredById.end()) {
+ i->second->setExpiryPolicy(expiredPolicy); // hasExpired() == true;
+ unexpiredByMessage.erase(i->second);
+ unexpiredById.erase(i);
+ }
+}
+
+boost::optional<uint64_t> ExpiryPolicy::getId(broker::Message& m) {
+ 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/cpp/src/qpid/cluster/ExpiryPolicy.h b/cpp/src/qpid/cluster/ExpiryPolicy.h
new file mode 100644
index 0000000000..bdbe3a61dc
--- /dev/null
+++ b/cpp/src/qpid/cluster/ExpiryPolicy.h
@@ -0,0 +1,89 @@
+#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) { expiryId = id; }
+ uint64_t getId() const { return expiryId; }
+
+ boost::optional<uint64_t> getId(broker::Message&);
+
+ private:
+ typedef std::map<broker::Message*, uint64_t> MessageIdMap;
+ typedef std::map<uint64_t, broker::Message*> IdMessageMap;
+
+ struct Expired : public broker::ExpiryPolicy {
+ bool hasExpired(broker::Message&);
+ void willExpire(broker::Message&);
+ };
+
+ 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/cpp/src/qpid/cluster/FailoverExchange.cpp b/cpp/src/qpid/cluster/FailoverExchange.cpp
new file mode 100644
index 0000000000..e01c41494b
--- /dev/null
+++ b/cpp/src/qpid/cluster/FailoverExchange.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/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::TYPE_NAME("amq.failover");
+
+FailoverExchange::FailoverExchange(management::Manageable* parent) : Exchange(TYPE_NAME, parent) {
+ if (mgmtExchange != 0)
+ mgmtExchange->set_type(TYPE_NAME);
+}
+
+
+void FailoverExchange::setUrls(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 TYPE_NAME; }
+
+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 " << TYPE_NAME << " 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, TYPE_NAME, 1, 0));
+ command.setLastSegment(false);
+ msg->getFrames().append(command);
+ AMQHeaderBody header;
+ header.get<MessageProperties>(true)->setContentLength(0);
+ header.get<MessageProperties>(true)->getApplicationHeaders().setArray(TYPE_NAME, array);
+ AMQFrame headerFrame(header);
+ headerFrame.setFirstSegment(false);
+ msg->getFrames().append(headerFrame);
+ DeliverableMessage(msg).deliverTo(queue);
+}
+
+}} // namespace cluster
diff --git a/cpp/src/qpid/cluster/FailoverExchange.h b/cpp/src/qpid/cluster/FailoverExchange.h
new file mode 100644
index 0000000000..738cd2a602
--- /dev/null
+++ b/cpp/src/qpid/cluster/FailoverExchange.h
@@ -0,0 +1,68 @@
+#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 TYPE_NAME;
+
+ FailoverExchange(management::Manageable* parent);
+
+ void setUrls(const std::vector<Url>&);
+
+ // Exchange overrides
+ std::string getType() const;
+ bool bind(broker::Queue::shared_ptr queue, const std::string& routingKey, const framing::FieldTable* args);
+ bool unbind(broker::Queue::shared_ptr queue, const std::string& routingKey, const framing::FieldTable* args);
+ bool isBound(broker::Queue::shared_ptr 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 broker::Queue::shared_ptr&);
+
+ typedef sys::Mutex::ScopedLock Lock;
+ typedef std::vector<Url> Urls;
+ typedef std::set<broker::Queue::shared_ptr> Queues;
+
+ sys::Mutex lock;
+ Urls urls;
+ Queues queues;
+
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_FAILOVEREXCHANGE_H*/
diff --git a/cpp/src/qpid/cluster/InitialStatusMap.cpp b/cpp/src/qpid/cluster/InitialStatusMap.cpp
new file mode 100644
index 0000000000..59338f89d4
--- /dev/null
+++ b/cpp/src/qpid/cluster/InitialStatusMap.cpp
@@ -0,0 +1,197 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "InitialStatusMap.h"
+#include "StoreStatus.h"
+#include <algorithm>
+#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
+ 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
+ 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() {
+ return !map.empty() && find_if(map.begin(), map.end(), &notInitialized) == map.end()
+ && (map.size() >= size);
+}
+
+bool InitialStatusMap::transitionToComplete() {
+ return completed;
+}
+
+bool InitialStatusMap::isResendNeeded() {
+ bool ret = resendNeeded;
+ resendNeeded = false;
+ return ret;
+}
+
+bool InitialStatusMap::isActive(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::isUpdateNeeded() {
+ assert(isComplete());
+ // We need an update if there are any active members.
+ if (find_if(map.begin(), map.end(), &isActive) != map.end()) 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() {
+ assert(isComplete());
+ MemberSet elders;
+ // Elders are from first config change, active or higher node-id.
+ for (MemberSet::iterator i = firstConfig.begin(); i != firstConfig.end(); ++i) {
+ if (map.find(*i) != map.end() && (map[*i]->getActive() || *i > self))
+ elders.insert(*i);
+ }
+ return elders;
+}
+
+// Get cluster ID from an active member or the youngest newcomer.
+framing::Uuid InitialStatusMap::getClusterId() {
+ assert(isComplete());
+ assert(!map.empty());
+ Map::iterator i = find_if(map.begin(), map.end(), &isActive);
+ 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;
+
+ for (Map::iterator i = map.begin(); i != map.end(); ++i) {
+ 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.");
+ 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 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");
+}
+
+
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/InitialStatusMap.h b/cpp/src/qpid/cluster/InitialStatusMap.h
new file mode 100644
index 0000000000..40fd9ee49d
--- /dev/null
+++ b/cpp/src/qpid/cluster/InitialStatusMap.h
@@ -0,0 +1,75 @@
+#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.
+ */
+class InitialStatusMap
+{
+ public:
+ typedef framing::ClusterInitialStatusBody Status;
+
+ InitialStatusMap(const MemberId& self, size_t size);
+ /** Process a config change. @return true if we need to re-send our status */
+ 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 is complete. */
+ bool isComplete();
+ /**@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();
+ /**@pre isComplete(). @return True if we need 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();
+
+ private:
+ typedef std::map<MemberId, boost::optional<Status> > Map;
+ static bool notInitialized(const Map::value_type&);
+ static bool isActive(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/cpp/src/qpid/cluster/LockedConnectionMap.h b/cpp/src/qpid/cluster/LockedConnectionMap.h
new file mode 100644
index 0000000000..ac744d4f94
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/McastFrameHandler.h b/cpp/src/qpid/cluster/McastFrameHandler.h
new file mode 100644
index 0000000000..17e4c2e9f0
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/MemberSet.cpp b/cpp/src/qpid/cluster/MemberSet.cpp
new file mode 100644
index 0000000000..5dc148609f
--- /dev/null
+++ b/cpp/src/qpid/cluster/MemberSet.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 "MemberSet.h"
+#include <ostream>
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+
+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/cpp/src/qpid/cluster/MemberSet.h b/cpp/src/qpid/cluster/MemberSet.h
new file mode 100644
index 0000000000..df3df7c319
--- /dev/null
+++ b/cpp/src/qpid/cluster/MemberSet.h
@@ -0,0 +1,43 @@
+#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;
+
+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/cpp/src/qpid/cluster/Multicaster.cpp b/cpp/src/qpid/cluster/Multicaster.cpp
new file mode 100644
index 0000000000..229d7edb1e
--- /dev/null
+++ b/cpp/src/qpid/cluster/Multicaster.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 "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)
+{
+ queue.start();
+}
+
+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) {
+ if (e.isConnection()) holdingQueue.push_back(e);
+ else {
+ iovec iov = e.toIovec();
+ // FIXME aconway 2009-11-23: configurable retry --cluster-retry
+ if (!cpg.mcast(&iov, 1))
+ throw Exception("CPG flow control error during initialization");
+ QPID_LOG(trace, "MCAST (direct) " << e);
+ }
+ return;
+ }
+ }
+ QPID_LOG(trace, "MCAST " << e);
+ 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::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/cpp/src/qpid/cluster/Multicaster.h b/cpp/src/qpid/cluster/Multicaster.h
new file mode 100644
index 0000000000..2db84a9ce0
--- /dev/null
+++ b/cpp/src/qpid/cluster/Multicaster.h
@@ -0,0 +1,87 @@
+#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.
+ *
+ * Runs in two modes;
+ *
+ * initializing: Hold connection mcast events. Multicast cluster
+ * events directly in the calling thread. This mode is used before
+ * joining the cluster where the poller may not yet be active and we
+ * want to hold any connection traffic till we join.
+ *
+ * ready: normal operation. Queues all mcasts on a pollable queue,
+ * multicasts connection and cluster events.
+ */
+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);
+
+ /** Switch to ready mode. */
+ 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;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_MULTICASTER_H*/
diff --git a/cpp/src/qpid/cluster/ShadowConnectionOutputHandler.h b/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h
index 6d429535e6..566a82476e 100644
--- a/cpp/src/qpid/cluster/ShadowConnectionOutputHandler.h
+++ b/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h
@@ -1,5 +1,5 @@
-#ifndef QPID_CLUSTER_SHADOWCONNECTIONOUTPUTHANDLER_H
-#define QPID_CLUSTER_SHADOWCONNECTIONOUTPUTHANDLER_H
+#ifndef QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H
+#define QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H
/*
*
@@ -30,17 +30,18 @@ namespace framing { class AMQFrame; }
namespace cluster {
/**
- * Output handler for frames sent to shadow connections.
- * Simply discards frames.
+ * Output handler shadow connections, simply discards frames.
*/
-class ShadowConnectionOutputHandler : public sys::ConnectionOutputHandler
+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_SHADOWCONNECTIONOUTPUTHANDLER_H*/
+#endif /*!QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H*/
diff --git a/cpp/src/qpid/cluster/Numbering.h b/cpp/src/qpid/cluster/Numbering.h
new file mode 100644
index 0000000000..2d2d931384
--- /dev/null
+++ b/cpp/src/qpid/cluster/Numbering.h
@@ -0,0 +1,70 @@
+#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();
+ }
+
+ bool contains(const T& t) const { return (*this)[t] == 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/cpp/src/qpid/cluster/OutputInterceptor.cpp b/cpp/src/qpid/cluster/OutputInterceptor.cpp
new file mode 100644
index 0000000000..cb75fe5561
--- /dev/null
+++ b/cpp/src/qpid/cluster/OutputInterceptor.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/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(1), sent(0), sentDoOutput(false)
+{}
+
+void OutputInterceptor::send(framing::AMQFrame& f) {
+ sys::Mutex::ScopedLock l(lock);
+ next->send(f);
+}
+
+void OutputInterceptor::activateOutput() {
+ if (parent.isCatchUp()) {
+ sys::Mutex::ScopedLock l(lock);
+ next->activateOutput();
+ }
+ else
+ sendDoOutput(sendMax);
+}
+
+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 do nothing in the write thread, we run doOutput only on delivery
+// of doOutput requests.
+bool OutputInterceptor::doOutput() { return false; }
+
+// Send output up to limit, calculate new limit.
+void OutputInterceptor::deliverDoOutput(uint32_t limit) {
+ sentDoOutput = false;
+ sendMax = limit;
+ size_t newLimit = limit;
+ if (parent.isLocal()) {
+ size_t buffered = 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 = sent-1;
+ }
+ sent = 0;
+ while (sent < limit && parent.getBrokerConnection().doOutput())
+ ++sent;
+ if (sent == limit) sendDoOutput(newLimit);
+}
+
+void OutputInterceptor::sendDoOutput(size_t newLimit) {
+ if (parent.isLocal() && !sentDoOutput && !closing) {
+ sentDoOutput = true;
+ parent.getCluster().getMulticast().mcastControl(
+ ClusterConnectionDeliverDoOutputBody(ProtocolVersion(), newLimit),
+ parent.getId());
+ }
+}
+
+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/cpp/src/qpid/cluster/OutputInterceptor.h b/cpp/src/qpid/cluster/OutputInterceptor.h
new file mode 100644
index 0000000000..65bd82a4fc
--- /dev/null
+++ b/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);
+
+ 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/cpp/src/qpid/cluster/PollableCondition.cpp b/cpp/src/qpid/cluster/PollableCondition.cpp
deleted file mode 100644
index eecf95ff8d..0000000000
--- a/cpp/src/qpid/cluster/PollableCondition.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#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.
- *
- */
-
-// FIXME aconway 2008-08-11: this could be of more general interest,
-// move to common lib.
-//
-
-#include "qpid/sys/posix/PrivatePosix.h"
-#include "qpid/cluster/PollableCondition.h"
-#include "qpid/Exception.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-
-namespace qpid {
-namespace cluster {
-
-PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) {
- 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"));
-}
-
-bool PollableCondition::clear() {
- char buf[256];
- ssize_t n;
- bool wasSet = false;
- while ((n = ::read(impl->fd, buf, sizeof(buf))) > 0)
- wasSet = true;
- if (n == -1 && errno != EAGAIN) throw ErrnoException(QPID_MSG("Error clearing PollableCondition"));
- return wasSet;
-}
-
-void PollableCondition::set() {
- static const char dummy=0;
- ssize_t n = ::write(writeFd, &dummy, 1);
- if (n == -1 && errno != EAGAIN) throw ErrnoException("Error setting PollableCondition");
-}
-
-
-#if 0
-// FIXME aconway 2008-08-12: More efficient Linux implementation using
-// eventfd system call. Do a configure.ac test to enable this when
-// eventfd is available.
-
-#include <sys/eventfd.h>
-
-namespace qpid {
-namespace cluster {
-
-PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) {
- impl->fd = ::eventfd(0, 0);
- if (impl->fd < 0) throw ErrnoException("conditionfd() failed");
-}
-
-bool PollableCondition::clear() {
- char buf[8];
- ssize_t n = ::read(impl->fd, buf, 8);
- if (n != 8) throw ErrnoException("read failed on conditionfd");
- return *reinterpret_cast<uint64_t*>(buf);
-}
-
-void PollableCondition::set() {
- static const uint64_t value=1;
- ssize_t n = ::write(impl->fd, reinterpret_cast<const void*>(&value), 8);
- if (n != 8) throw ErrnoException("write failed on conditionfd");
-}
-
-#endif
-
-}} // namespace qpid::cluster
-
-#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/
diff --git a/cpp/src/qpid/cluster/PollableQueue.h b/cpp/src/qpid/cluster/PollableQueue.h
index 0bba2ba790..65f615d8b6 100644
--- a/cpp/src/qpid/cluster/PollableQueue.h
+++ b/cpp/src/qpid/cluster/PollableQueue.h
@@ -22,78 +22,53 @@
*
*/
-#include "qpid/cluster/PollableCondition.h"
-#include "qpid/sys/Dispatcher.h"
-#include "qpid/sys/Mutex.h"
-#include <boost/function.hpp>
-#include <boost/bind.hpp>
-#include <deque>
+#include "qpid/sys/PollableQueue.h"
+#include <qpid/log/Statement.h>
namespace qpid {
-
-namespace sys { class Poller; }
-
namespace cluster {
-// FIXME aconway 2008-08-11: this could be of more general interest,
-// move to common lib.
-
/**
- * A queue that can be polled by sys::Poller. Any thread can push to
- * the queue, on wakeup the poller thread processes all items on the
- * queue by passing them to a callback in a batch.
+ * More convenient version of PollableQueue that handles iterating
+ * over the batch and error handling.
*/
-template <class T>
-class PollableQueue {
- typedef std::deque<T> Queue;
-
+template <class T> class PollableQueue : public sys::PollableQueue<T> {
public:
- typedef typename Queue::iterator iterator;
-
- /** Callback to process a range of items. */
- typedef boost::function<void (const iterator&, const iterator&)> Callback;
-
- /** When the queue is selected by the poller, values are passed to callback cb. */
- explicit PollableQueue(const Callback& cb);
-
- /** Push a value onto the queue. Thread safe */
- void push(const T& t) { ScopedLock l(lock); queue.push_back(t); condition.set(); }
+ 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)
+ {}
+
+ 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(error, message << ": " << e.what());
+ this->stop();
+ error();
+ return values.end();
+ }
+ }
- /** Start polling. */
- void start(const boost::shared_ptr<sys::Poller>& poller) { handle.startWatch(poller); }
-
- /** Stop polling. */
- void stop() { handle.stopWatch(); }
-
private:
- typedef sys::Mutex::ScopedLock ScopedLock;
- typedef sys::Mutex::ScopedUnlock ScopedUnlock;
-
- void dispatch(sys::DispatchHandle&);
-
- sys::Mutex lock;
Callback callback;
- PollableCondition condition;
- sys::DispatchHandle handle;
- Queue queue;
- Queue batch;
+ ErrorCallback error;
+ std::string message;
};
-template <class T> PollableQueue<T>::PollableQueue(const Callback& cb) // FIXME aconway 2008-08-12:
- : callback(cb),
- handle(condition, boost::bind(&PollableQueue<T>::dispatch, this, _1), 0, 0)
-{}
-
-template <class T> void PollableQueue<T>::dispatch(sys::DispatchHandle& h) {
- ScopedLock l(lock); // Lock for concurrent push()
- batch.clear();
- batch.swap(queue);
- condition.clear();
- ScopedUnlock u(lock);
- callback(batch.begin(), batch.end()); // Process the batch outside the lock.
- h.rewatch();
-}
-
+
}} // namespace qpid::cluster
#endif /*!QPID_CLUSTER_POLLABLEQUEUE_H*/
diff --git a/cpp/src/qpid/cluster/PollerDispatch.cpp b/cpp/src/qpid/cluster/PollerDispatch.cpp
new file mode 100644
index 0000000000..a839ef863b
--- /dev/null
+++ b/cpp/src/qpid/cluster/PollerDispatch.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/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& ) {
+ QPID_LOG(critical, "Disconnected from cluster");
+ onError();
+}
+
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/PollerDispatch.h b/cpp/src/qpid/cluster/PollerDispatch.h
new file mode 100644
index 0000000000..63801e0de9
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/ProxyInputHandler.h b/cpp/src/qpid/cluster/ProxyInputHandler.h
new file mode 100644
index 0000000000..228f8d092d
--- /dev/null
+++ b/cpp/src/qpid/cluster/ProxyInputHandler.h
@@ -0,0 +1,57 @@
+#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(); }
+ bool hasOutput() { return target->hasOutput(); }
+
+ private:
+ boost::intrusive_ptr<cluster::Connection> target;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_PROXYINPUTHANDLER_H*/
diff --git a/cpp/src/qpid/sys/Condition.h b/cpp/src/qpid/cluster/Quorum.h
index 961c15e1ee..bbfa473f94 100644
--- a/cpp/src/qpid/sys/Condition.h
+++ b/cpp/src/qpid/cluster/Quorum.h
@@ -1,5 +1,5 @@
-#ifndef _sys_Condition_h
-#define _sys_Condition_h
+#ifndef QPID_CLUSTER_QUORUM_H
+#define QPID_CLUSTER_QUORUM_H
/*
*
@@ -21,11 +21,12 @@
* under the License.
*
*/
+#include "config.h"
-#ifdef USE_APR_PLATFORM
-#include "apr/Condition.h"
+#if HAVE_LIBCMAN_H
+#include "qpid/cluster/Quorum_cman.h"
#else
-#include "posix/Condition.h"
+#include "qpid/cluster/Quorum_null.h"
#endif
-#endif /*!_sys_Condition_h*/
+#endif /*!QPID_CLUSTER_QUORUM_H*/
diff --git a/cpp/src/qpid/cluster/Quorum_cman.cpp b/cpp/src/qpid/cluster/Quorum_cman.cpp
new file mode 100644
index 0000000000..507d9649b9
--- /dev/null
+++ b/cpp/src/qpid/cluster/Quorum_cman.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 "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) : enable(false), cman(0), cmanFd(0) {
+ errorFn = err;
+}
+
+Quorum::~Quorum() {
+ dispatchHandle.reset();
+ if (cman) cman_finish(cman);
+}
+
+void Quorum::start(boost::shared_ptr<sys::Poller> p) {
+ poller = p;
+ enable = true;
+ 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;
+ dispatchHandle.reset(
+ new sys::DispatchHandleRef(
+ sys::PosixIOHandle(cmanFd),
+ 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/cpp/src/qpid/cluster/Quorum_cman.h b/cpp/src/qpid/cluster/Quorum_cman.h
new file mode 100644
index 0000000000..130f1baf64
--- /dev/null
+++ b/cpp/src/qpid/cluster/Quorum_cman.h
@@ -0,0 +1,64 @@
+#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;
+}
+
+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);
+
+ bool enable;
+ cman_handle_t cman;
+ int cmanFd;
+ std::auto_ptr<sys::DispatchHandleRef> dispatchHandle;
+ boost::shared_ptr<sys::Poller> poller;
+};
+
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_QUORUM_CMAN_H*/
diff --git a/cpp/src/qpid/client/FutureResult.h b/cpp/src/qpid/cluster/Quorum_null.h
index e97d80476d..dc27f0a43b 100644
--- a/cpp/src/qpid/client/FutureResult.h
+++ b/cpp/src/qpid/cluster/Quorum_null.h
@@ -1,3 +1,6 @@
+#ifndef QPID_CLUSTER_QUORUM_NULL_H
+#define QPID_CLUSTER_QUORUM_NULL_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,29 +22,21 @@
*
*/
-#ifndef _FutureResult_
-#define _FutureResult_
-
-#include <string>
-#include "qpid/framing/amqp_framing.h"
-#include "FutureCompletion.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
namespace qpid {
-namespace client {
+namespace cluster {
+class Cluster;
-class SessionImpl;
+/** Null implementation of quorum. */
-///@internal
-class FutureResult : public FutureCompletion
-{
- std::string result;
-public:
- const std::string& getResult(SessionImpl& session) const;
- void received(const std::string& result);
+class Quorum {
+ public:
+ Quorum(boost::function<void ()>) {}
+ void start(boost::shared_ptr<sys::Poller>) {}
};
-}}
-
-
+#endif /*!QPID_CLUSTER_QUORUM_NULL_H*/
-#endif
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/RetractClient.cpp b/cpp/src/qpid/cluster/RetractClient.cpp
new file mode 100644
index 0000000000..7d9f52fc39
--- /dev/null
+++ b/cpp/src/qpid/cluster/RetractClient.cpp
@@ -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.
+ *
+ */
+#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)->handle(retract);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, " while retracting retract to " << url << ": " << e.what());
+ }
+}
+
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/RetractClient.h b/cpp/src/qpid/cluster/RetractClient.h
new file mode 100644
index 0000000000..fb896197cc
--- /dev/null
+++ b/cpp/src/qpid/cluster/RetractClient.h
@@ -0,0 +1,49 @@
+#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"
+
+
+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/cpp/src/qpid/cluster/StoreStatus.cpp b/cpp/src/qpid/cluster/StoreStatus.cpp
new file mode 100644
index 0000000000..6e412c23f7
--- /dev/null
+++ b/cpp/src/qpid/cluster/StoreStatus.cpp
@@ -0,0 +1,110 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "StoreStatus.h"
+#include "qpid/Exception.h"
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <fstream>
+
+namespace qpid {
+namespace cluster {
+
+using framing::Uuid;
+using namespace framing::cluster;
+namespace fs=boost::filesystem;
+using std::ostream;
+
+StoreStatus::StoreStatus(const std::string& d)
+ : state(STORE_STATE_NO_STORE), dataDir(d)
+{}
+
+namespace {
+
+const char* SUBDIR="cluster";
+const char* CLUSTER_ID_FILE="cluster.uuid";
+const char* SHUTDOWN_ID_FILE="shutdown.uuid";
+
+Uuid loadUuid(const fs::path& path) {
+ Uuid ret;
+ if (exists(path)) {
+ fs::ifstream i(path);
+ i >> ret;
+ }
+ return ret;
+}
+
+void saveUuid(const fs::path& path, const Uuid& uuid) {
+ fs::ofstream o(path);
+ o << uuid;
+}
+
+} // namespace
+
+
+void StoreStatus::load() {
+ fs::path dir = fs::path(dataDir, fs::native)/SUBDIR;
+ create_directory(dir);
+ clusterId = loadUuid(dir/CLUSTER_ID_FILE);
+ shutdownId = loadUuid(dir/SHUTDOWN_ID_FILE);
+
+ if (clusterId && shutdownId) state = STORE_STATE_CLEAN_STORE;
+ else if (clusterId) state = STORE_STATE_DIRTY_STORE;
+ else state = STORE_STATE_EMPTY_STORE;
+}
+
+void StoreStatus::save() {
+ fs::path dir = fs::path(dataDir, fs::native)/SUBDIR;
+ create_directory(dir);
+ saveUuid(dir/CLUSTER_ID_FILE, clusterId);
+ saveUuid(dir/SHUTDOWN_ID_FILE, shutdownId);
+}
+
+void StoreStatus::dirty(const Uuid& clusterId_) {
+ clusterId = clusterId_;
+ shutdownId = Uuid();
+ state = STORE_STATE_DIRTY_STORE;
+ save();
+}
+
+void StoreStatus::clean(const Uuid& shutdownId_) {
+ state = STORE_STATE_CLEAN_STORE;
+ shutdownId = shutdownId_;
+ save();
+}
+
+ostream& operator<<(ostream& o, const StoreStatus& s) {
+ switch (s.getState()) {
+ case STORE_STATE_NO_STORE: o << "no store"; break;
+ case STORE_STATE_EMPTY_STORE: o << "empty store"; break;
+ case STORE_STATE_DIRTY_STORE:
+ o << "dirty store, cluster-id=" << s.getClusterId();
+ break;
+ case STORE_STATE_CLEAN_STORE:
+ o << "clean store, cluster-id=" << s.getClusterId()
+ << " shutdown-id=" << s.getShutdownId();
+ break;
+ }
+ return o;
+}
+
+}} // namespace qpid::cluster
+
diff --git a/cpp/src/qpid/cluster/StoreStatus.h b/cpp/src/qpid/cluster/StoreStatus.h
new file mode 100644
index 0000000000..522020ed69
--- /dev/null
+++ b/cpp/src/qpid/cluster/StoreStatus.h
@@ -0,0 +1,64 @@
+#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/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; }
+ const Uuid& getShutdownId() const { return shutdownId; }
+
+ void dirty(const Uuid& start); // Start using the store.
+ void clean(const Uuid& stop); // Stop using the store.
+
+ void load();
+ void save();
+
+ bool hasStore() { return state != framing::cluster::STORE_STATE_NO_STORE; }
+
+ private:
+ framing::cluster::StoreState state;
+ Uuid clusterId, shutdownId;
+ std::string dataDir;
+};
+
+std::ostream& operator<<(std::ostream&, const StoreStatus&);
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_STORESTATE_H*/
diff --git a/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp
new file mode 100644
index 0000000000..b20cc907a2
--- /dev/null
+++ b/cpp/src/qpid/cluster/UpdateClient.cpp
@@ -0,0 +1,484 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/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/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/Queue.h"
+#include "qpid/broker/QueueRegistry.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/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/Url.h"
+#include <boost/bind.hpp>
+#include <boost/cast.hpp>
+#include <algorithm>
+
+namespace qpid {
+namespace cluster {
+
+using broker::Broker;
+using broker::Exchange;
+using broker::Queue;
+using broker::QueueBinding;
+using broker::Message;
+using broker::SemanticState;
+
+using namespace framing;
+namespace arg=client::arg;
+using client::SessionBase_0_10Access;
+
+struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection {
+ ClusterConnectionProxy(client::Connection c) :
+ AMQP_AllProxy::ClusterConnection(*client::ConnectionAccess::getImpl(c)) {}
+ ClusterConnectionProxy(client::AsyncSession s) :
+ AMQP_AllProxy::ClusterConnection(SessionBase_0_10Access(s).get()->out) {}
+};
+
+// 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, updaterId << " updating state to " << updateeId
+ << " at " << updateeUrl);
+ Broker& b = updaterBroker;
+ 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);
+ session.close();
+
+ // 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());
+ ClusterConnectionMembershipBody membership;
+ map.toMethodBody(membership);
+ AMQFrame frame(membership);
+ client::ConnectionAccess::getImpl(connection)->handle(frame);
+
+ connection.close();
+ QPID_LOG(debug, updaterId << " update completed to " << updateeId
+ << " at " << updateeUrl << ": " << membership);
+}
+
+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;
+}
+} // namespace
+
+void UpdateClient::updateExchange(const boost::shared_ptr<Exchange>& ex) {
+ QPID_LOG(debug, updaterId << " 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);
+ if (!expiryId) return; // Message already expired, don't replicate.
+ ClusterConnectionProxy(session).expiryId(*expiryId);
+ }
+
+ // 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());
+}
+
+void UpdateClient::updateExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) {
+ QPID_LOG(debug, updaterId << " 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, updaterId << " 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, updaterId << " updating output task " << ci->getName()
+ << " channel=" << channel);
+}
+
+void UpdateClient::updateConnection(const boost::intrusive_ptr<Connection>& updateConnection) {
+ QPID_LOG(debug, updaterId << " updating connection " << *updateConnection);
+ shadowConnection = catchUpConnection();
+
+ broker::Connection& bc = updateConnection->getBrokerConnection();
+ connectionSettings.maxFrameSize = bc.getFrameMax();
+ shadowConnection.open(updateeUrl, connectionSettings);
+ 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.getUserId(),
+ string(fragment.first, fragment.second),
+ updateConnection->getOutput().getSendMax()
+ );
+ shadowConnection.close();
+ QPID_LOG(debug, updaterId << " updated connection " << *updateConnection);
+}
+
+void UpdateClient::updateSession(broker::SessionHandler& sh) {
+ broker::SessionState* ss = sh.getSession();
+ if (!ss) return; // no session.
+
+ QPID_LOG(debug, updaterId << " updating session " << &sh.getConnection()
+ << "[" << sh.getChannel() << "] = " << 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, updaterId << " updating exclusive queues.");
+ ss->getSessionAdapter().eachExclusiveQueue(boost::bind(&UpdateClient::updateExclusiveQueue, this, _1));
+
+ QPID_LOG(debug, updaterId << " updating consumers.");
+ ss->getSemanticState().eachConsumer(
+ boost::bind(&UpdateClient::updateConsumer, this, _1));
+
+ QPID_LOG(debug, updaterId << " 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;
+
+ // 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, updaterId << " updated session " << sh.getSession()->getId());
+}
+
+void UpdateClient::updateConsumer(
+ const broker::SemanticState::ConsumerImpl::shared_ptr& ci)
+{
+ QPID_LOG(debug, updaterId << " 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);
+
+ QPID_LOG(debug, updaterId << " 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, updaterId << " 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)
+{
+ const boost::shared_ptr<SemanticState::ConsumerImpl> ci =
+ boost::dynamic_pointer_cast<SemanticState::ConsumerImpl>(c);
+ size_t n = consumerNumbering[ci];
+ if (n >= consumerNumbering.size())
+ throw Exception(QPID_MSG("Unexpected listener on queue " << q));
+ ClusterConnectionProxy(session).addQueueListener(q, n);
+}
+
+}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/UpdateClient.h b/cpp/src/qpid/cluster/UpdateClient.h
new file mode 100644
index 0000000000..29ef5f9df2
--- /dev/null
+++ b/cpp/src/qpid/cluster/UpdateClient.h
@@ -0,0 +1,119 @@
+#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>
+
+
+namespace qpid {
+
+class Url;
+
+namespace broker {
+
+class Broker;
+class Queue;
+class Exchange;
+class QueueBindings;
+class QueueBinding;
+class QueuedMessage;
+class SessionHandler;
+class DeliveryRecord;
+class SessionState;
+class SemanticState;
+class Decoder;
+
+} // 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);
+
+ Numbering<broker::SemanticState::ConsumerImpl::shared_ptr> 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;
+};
+
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATECLIENT_H*/
diff --git a/cpp/src/qpid/cluster/UpdateExchange.cpp b/cpp/src/qpid/cluster/UpdateExchange.cpp
new file mode 100644
index 0000000000..11937f296f
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/UpdateExchange.h b/cpp/src/qpid/cluster/UpdateExchange.h
new file mode 100644
index 0000000000..9d7d9ee5fc
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/UpdateReceiver.h b/cpp/src/qpid/cluster/UpdateReceiver.h
new file mode 100644
index 0000000000..cc1ce0da8d
--- /dev/null
+++ b/cpp/src/qpid/cluster/UpdateReceiver.h
@@ -0,0 +1,42 @@
+#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;
+};
+}} // namespace qpid::cluster
+
+#endif /*!QPID_CLUSTER_UPDATESTATE_H*/
diff --git a/cpp/src/qpid/cluster/WatchDogPlugin.cpp b/cpp/src/qpid/cluster/WatchDogPlugin.cpp
new file mode 100644
index 0000000000..fc2b830dac
--- /dev/null
+++ b/cpp/src/qpid/cluster/WatchDogPlugin.cpp
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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 "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), 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_EXEC_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/cpp/src/qpid/cluster/management-schema.xml b/cpp/src/qpid/cluster/management-schema.xml
new file mode 100644
index 0000000000..a6292e9113
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/qpidd_watchdog.cpp b/cpp/src/qpid/cluster/qpidd_watchdog.cpp
new file mode 100644
index 0000000000..51c5ed4b3f
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/types.h b/cpp/src/qpid/cluster/types.h
new file mode 100644
index 0000000000..c25370b6b6
--- /dev/null
+++ b/cpp/src/qpid/cluster/types.h
@@ -0,0 +1,83 @@
+#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/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; }
+
+ // AsMethodBody as string, network byte order.
+ 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/cpp/src/qpid/client/Session.h b/cpp/src/qpid/console/Agent.cpp
index bdabd26c82..fa76a13583 100644
--- a/cpp/src/qpid/client/Session.h
+++ b/cpp/src/qpid/console/Agent.cpp
@@ -1,6 +1,3 @@
-#ifndef QPID_CLIENT_SESSION_H
-#define QPID_CLIENT_SESSION_H
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,19 +18,13 @@
* under the License.
*
*/
-#include "qpid/client/Session_0_10.h"
-
-namespace qpid {
-namespace client {
-
-/**
- * Session is an alias for Session_0_10
- *
- * \ingroup clientapi
- */
-typedef Session_0_10 Session;
+#include "qpid/console/Agent.h"
-}} // namespace qpid::client
+std::ostream& qpid::console::operator<<(std::ostream& o, const Agent& agent)
+{
+ o << "Agent at bank " << agent.getBrokerBank() << "." << agent.getAgentBank() <<
+ " (" << agent.getLabel() << ")";
+ return o;
+}
-#endif /*!QPID_CLIENT_SESSION_H*/
diff --git a/cpp/src/qpid/console/Broker.cpp b/cpp/src/qpid/console/Broker.cpp
new file mode 100644
index 0000000000..d2ff8f819e
--- /dev/null
+++ b/cpp/src/qpid/console/Broker.cpp
@@ -0,0 +1,328 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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();
+}
+
+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/cpp/src/qpid/console/ClassKey.cpp b/cpp/src/qpid/console/ClassKey.cpp
new file mode 100644
index 0000000000..7a16113bae
--- /dev/null
+++ b/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/cpp/src/qpid/console/Event.cpp b/cpp/src/qpid/console/Event.cpp
new file mode 100644
index 0000000000..3e14804b35
--- /dev/null
+++ b/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/cpp/src/qpid/console/Object.cpp b/cpp/src/qpid/console/Object.cpp
new file mode 100644
index 0000000000..6570e293ab
--- /dev/null
+++ b/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/cpp/src/qpid/console/ObjectId.cpp b/cpp/src/qpid/console/ObjectId.cpp
new file mode 100644
index 0000000000..fbaad20d57
--- /dev/null
+++ b/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/cpp/src/qpid/sys/Runnable.h b/cpp/src/qpid/console/Package.cpp
index fb3927c612..e5d6fa29fd 100644
--- a/cpp/src/qpid/sys/Runnable.h
+++ b/cpp/src/qpid/console/Package.cpp
@@ -1,5 +1,3 @@
-#ifndef _Runnable_
-#define _Runnable_
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,30 +19,23 @@
*
*/
-#include <boost/function.hpp>
+#include "qpid/console/Package.h"
-namespace qpid {
-namespace sys {
+using namespace qpid::console;
-/**
- * Interface for objects that can be run, e.g. in a thread.
- */
-class Runnable
+SchemaClass* Package::getClass(const std::string& className, uint8_t* hash)
{
- public:
- /** Type to represent a runnable as a Functor */
- typedef boost::function0<void> Functor;
-
- virtual ~Runnable();
-
- /** Derived classes override run(). */
- virtual void run() = 0;
-
- /** Create a functor object that will call this->run(). */
- Functor functor();
-};
-
-}}
-
-
-#endif
+ 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/cpp/src/qpid/console/Schema.cpp b/cpp/src/qpid/console/Schema.cpp
new file mode 100644
index 0000000000..a3dbd91201
--- /dev/null
+++ b/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/cpp/src/qpid/console/SequenceManager.cpp b/cpp/src/qpid/console/SequenceManager.cpp
new file mode 100644
index 0000000000..86ea829749
--- /dev/null
+++ b/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/cpp/src/qpid/console/SessionManager.cpp b/cpp/src/qpid/console/SessionManager.cpp
new file mode 100644
index 0000000000..0285c5f34a
--- /dev/null
+++ b/cpp/src/qpid/console/SessionManager.cpp
@@ -0,0 +1,481 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#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;
+}
+
+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::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/cpp/src/qpid/console/Value.cpp b/cpp/src/qpid/console/Value.cpp
new file mode 100644
index 0000000000..c30660f1dc
--- /dev/null
+++ b/cpp/src/qpid/console/Value.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/console/Value.h"
+#include "qpid/framing/Buffer.h"
+
+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/cpp/src/qpid/doxygen_mainpage.h b/cpp/src/qpid/doxygen_mainpage.h
deleted file mode 100644
index 1502ef536e..0000000000
--- a/cpp/src/qpid/doxygen_mainpage.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// This header file is just for doxygen documentation purposes.
-
-/** \mainpage Qpid C++ Developer Kit.
- *
- * The <a href=http://incubator.apache.org/qpid>Qpid project</a> provides implementations of the <a href="http://amqp.org/">AMQP messaging specification</a> in several programming language.
- *
- * Qpidc provides APIs and libraries to implement AMQP clients in
- * C++. Qpidc clients can interact with any compliant AMQP message
- * broker. The Qpid project also provides an AMQP broker daemon called
- * qpidd that you can use with your qpidc clients.
- *
- * See the \ref clientapi "client API reference" to get started.
- *
- */
-
-
-/**
- * \defgroup clientapi Application API for an AMQP client.
- *
- * A typical client takes the following steps:
- * - Connect to the broker using qpid::client::Connection::open()
- * - Create a qpid::client::Session object.
- *
- * Once a session is created the client can work with the broker:
- * - Create and bind queues using the qpid::client::Session commands.
- * - Send messages using qpid::client::Session::messageTransfer.
- * - Subscribe to queues using qpid::client::SubscriptionManager
- */
diff --git a/cpp/src/qpid/framing/AMQBody.h b/cpp/src/qpid/framing/AMQBody.h
index f3bf65470c..60ac2d3b7e 100644
--- a/cpp/src/qpid/framing/AMQBody.h
+++ b/cpp/src/qpid/framing/AMQBody.h
@@ -22,8 +22,11 @@
*
*/
#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 {
@@ -43,16 +46,20 @@ struct AMQBodyConstVisitor {
virtual void visit(const AMQMethodBody&) = 0;
};
-class AMQBody
-{
+class AMQBody : public RefCounted {
public:
- virtual ~AMQBody();
+ 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 size() const = 0;
+ virtual uint32_t encodedSize() const = 0;
virtual void print(std::ostream& out) const = 0;
virtual void accept(AMQBodyConstVisitor&) const = 0;
@@ -62,9 +69,10 @@ class AMQBody
/** 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;
};
-std::ostream& operator<<(std::ostream& out, const AMQBody& body) ;
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& out, const AMQBody& body) ;
enum BodyTypes {
METHOD_BODY = 1,
diff --git a/cpp/src/qpid/framing/AMQCommandControlBody.h b/cpp/src/qpid/framing/AMQCommandControlBody.h
index 388fb48299..d12b70a168 100644
--- a/cpp/src/qpid/framing/AMQCommandControlBody.h
+++ b/cpp/src/qpid/framing/AMQCommandControlBody.h
@@ -43,7 +43,7 @@ template <class T> class AMQCommandControlBody : public AMQBody, public T
virtual void decode(Buffer& buffer, uint32_t=0) {
Codec::decode(buffer.getIterator(), static_cast<T&>(*this));
}
- virtual uint32_t size() const {
+ virtual uint32_t encodedSize() const {
Codec::size(buffer.getIterator(), static_cast<const T&>(*this));
}
diff --git a/cpp/src/qpid/framing/AMQContentBody.cpp b/cpp/src/qpid/framing/AMQContentBody.cpp
index 59f3619ef2..72f7d9978e 100644
--- a/cpp/src/qpid/framing/AMQContentBody.cpp
+++ b/cpp/src/qpid/framing/AMQContentBody.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "AMQContentBody.h"
+#include "qpid/framing/AMQContentBody.h"
#include <iostream>
qpid::framing::AMQContentBody::AMQContentBody(){
@@ -27,7 +27,7 @@ qpid::framing::AMQContentBody::AMQContentBody(){
qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){
}
-uint32_t qpid::framing::AMQContentBody::size() const{
+uint32_t qpid::framing::AMQContentBody::encodedSize() const{
return data.size();
}
void qpid::framing::AMQContentBody::encode(Buffer& buffer) const{
@@ -39,6 +39,8 @@ void qpid::framing::AMQContentBody::decode(Buffer& buffer, uint32_t _size){
void qpid::framing::AMQContentBody::print(std::ostream& out) const
{
- out << "content (" << size() << " bytes)";
- out << " " << data.substr(0,16) << "...";
+ out << "content (" << encodedSize() << " bytes)";
+ const size_t max = 32;
+ out << " " << data.substr(0, max);
+ if (data.size() > max) out << "...";
}
diff --git a/cpp/src/qpid/framing/AMQContentBody.h b/cpp/src/qpid/framing/AMQContentBody.h
index 5d530a1b9a..69813b221c 100644
--- a/cpp/src/qpid/framing/AMQContentBody.h
+++ b/cpp/src/qpid/framing/AMQContentBody.h
@@ -18,9 +18,10 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "AMQBody.h"
-#include "Buffer.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/CommonImportExport.h"
#ifndef _AMQContentBody_
#define _AMQContentBody_
@@ -33,17 +34,18 @@ class AMQContentBody : public AMQBody
string data;
public:
- AMQContentBody();
- AMQContentBody(const string& data);
+ 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; }
- uint32_t size() const;
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer, uint32_t size);
- void print(std::ostream& out) const;
- void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+ QPID_COMMON_EXTERN inline uint8_t type() const { return CONTENT_BODY; };
+ QPID_COMMON_EXTERN inline const string& getData() const { return data; }
+ QPID_COMMON_EXTERN 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;
+ QPID_COMMON_EXTERN void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+ QPID_COMMON_EXTERN boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
};
}
diff --git a/cpp/src/qpid/framing/AMQDataBlock.h b/cpp/src/qpid/framing/AMQDataBlock.h
index 9b6fdfd966..7f0d0dc2b5 100644
--- a/cpp/src/qpid/framing/AMQDataBlock.h
+++ b/cpp/src/qpid/framing/AMQDataBlock.h
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "Buffer.h"
+#include "qpid/framing/Buffer.h"
#ifndef _AMQDataBlock_
#define _AMQDataBlock_
@@ -32,7 +32,7 @@ public:
virtual ~AMQDataBlock() {}
virtual void encode(Buffer& buffer) const = 0;
virtual bool decode(Buffer& buffer) = 0;
- virtual uint32_t size() const = 0;
+ virtual uint32_t encodedSize() const = 0;
};
}
diff --git a/cpp/src/qpid/framing/AMQFrame.cpp b/cpp/src/qpid/framing/AMQFrame.cpp
index c1fc647b52..5c5920d786 100644
--- a/cpp/src/qpid/framing/AMQFrame.cpp
+++ b/cpp/src/qpid/framing/AMQFrame.cpp
@@ -18,33 +18,66 @@
* under the License.
*
*/
-#include "AMQFrame.h"
+#include "qpid/framing/AMQFrame.h"
-#include "qpid/framing/variant.h"
#include "qpid/framing/AMQMethodBody.h"
#include "qpid/framing/reply_exceptions.h"
-
+#include "qpid/framing/BodyFactory.h"
+#include "qpid/framing/MethodBodyFactory.h"
#include <boost/format.hpp>
-
#include <iostream>
namespace qpid {
namespace framing {
+void AMQFrame::init() {
+ bof = eof = bos = eos = true;
+ subchannel=0;
+ channel=0;
+ encodedSizeCache = 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::setBody(const AMQBody& b) { body = new BodyHolder(b); }
+AMQBody* AMQFrame::getBody() {
+ // Non-const AMQBody* may be used to modify the body.
+ encodedSizeCache = 0;
+ return body.get();
+}
+
+const AMQBody* AMQFrame::getBody() const {
+ return body.get();
+}
-void AMQFrame::setMethod(ClassId c, MethodId m) { body = new BodyHolder(c,m); }
+void AMQFrame::setMethod(ClassId c, MethodId m) {
+ encodedSizeCache = 0;
+ body = MethodBodyFactory::create(c,m);
+}
-uint32_t AMQFrame::size() const {
- return frameOverhead() + body->size();
+uint32_t AMQFrame::encodedSize() const {
+ if (!encodedSizeCache) {
+ encodedSizeCache = frameOverhead() + body->encodedSize();
+ if (body->getMethod())
+ encodedSizeCache += sizeof(ClassId)+sizeof(MethodId);
+ }
+ return encodedSizeCache;
}
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):
@@ -53,20 +86,27 @@ void AMQFrame::encode(Buffer& buffer) const
uint8_t flags = (bof ? 0x08 : 0) | (eof ? 0x04 : 0) | (bos ? 0x02 : 0) | (eos ? 0x01 : 0);
buffer.putOctet(flags);
buffer.putOctet(getBody()->type());
- buffer.putShort(size());
+ 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();
+ encodedSizeCache = 0;
+ uint32_t start = buffer.getPosition();
uint8_t flags = buffer.getOctet();
uint8_t framing_version = (flags & 0xc0) >> 6;
if (framing_version != 0)
@@ -98,8 +138,24 @@ bool AMQFrame::decode(Buffer& buffer)
buffer.restore();
return false;
}
- body = new BodyHolder();
- body->decode(type,buffer, body_size);
+
+ 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);
+ encodedSizeCache = buffer.getPosition() - start;
return true;
}
diff --git a/cpp/src/qpid/framing/AMQFrame.h b/cpp/src/qpid/framing/AMQFrame.h
index 5a8e55f9d2..29e368b671 100644
--- a/cpp/src/qpid/framing/AMQFrame.h
+++ b/cpp/src/qpid/framing/AMQFrame.h
@@ -21,50 +21,33 @@
* under the License.
*
*/
-#include "AMQDataBlock.h"
-#include "AMQHeaderBody.h"
-#include "AMQContentBody.h"
-#include "AMQHeartbeatBody.h"
-#include "ProtocolVersion.h"
-#include "BodyHolder.h"
-
+#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 BodyHolder;
-
class AMQFrame : public AMQDataBlock
{
public:
- AMQFrame(boost::intrusive_ptr<BodyHolder> b=0) : body(b) { init(); }
- AMQFrame(const AMQBody& b) { setBody(b); init(); }
- ~AMQFrame();
-
- template <class InPlace>
- AMQFrame(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0) {
- init(); setBody(ip);
- }
+ 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; }
- boost::intrusive_ptr<BodyHolder> getHolder() { return body; }
-
- AMQBody* getBody() { return body ? body->get() : 0; }
- const AMQBody* getBody() const { return body ? body->get() : 0; }
+ QPID_COMMON_EXTERN AMQBody* getBody();
+ QPID_COMMON_EXTERN const AMQBody* getBody() const;
- AMQMethodBody* getMethod() { return getBody()->getMethod(); }
- const AMQMethodBody* getMethod() const { return getBody()->getMethod(); }
-
- void setBody(const AMQBody& b);
-
- template <class InPlace>
- typename EnableInPlace<InPlace>::type setBody(const InPlace& ip) {
- body = new BodyHolder(ip);
- }
+ AMQMethodBody* getMethod() { return getBody() ? getBody()->getMethod() : 0; }
+ const AMQMethodBody* getMethod() const { return getBody() ? getBody()->getMethod() : 0; }
void setMethod(ClassId c, MethodId m);
@@ -76,9 +59,9 @@ class AMQFrame : public AMQDataBlock
return boost::polymorphic_downcast<const T*>(getBody());
}
- void encode(Buffer& buffer) const;
- bool decode(Buffer& buffer);
- uint32_t size() const;
+ 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)
@@ -104,20 +87,25 @@ class AMQFrame : public AMQDataBlock
bool getEos() const { return eos; }
void setEos(bool isEos) { eos = isEos; }
- static uint32_t frameOverhead();
+ 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() { bof = eof = bos = eos = true; subchannel=0; channel=0; }
+ void init();
- boost::intrusive_ptr<BodyHolder> body;
+ 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;
+ mutable uint32_t encodedSizeCache;
};
-std::ostream& operator<<(std::ostream&, const AMQFrame&);
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const AMQFrame&);
}} // namespace qpid::framing
diff --git a/cpp/src/qpid/framing/AMQHeaderBody.cpp b/cpp/src/qpid/framing/AMQHeaderBody.cpp
index 724c288705..14218f1b45 100644
--- a/cpp/src/qpid/framing/AMQHeaderBody.cpp
+++ b/cpp/src/qpid/framing/AMQHeaderBody.cpp
@@ -18,12 +18,12 @@
* under the License.
*
*/
-#include "AMQHeaderBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
#include "qpid/Exception.h"
#include "qpid/log/Statement.h"
-uint32_t qpid::framing::AMQHeaderBody::size() const {
- return properties.size();
+uint32_t qpid::framing::AMQHeaderBody::encodedSize() const {
+ return properties.encodedSize();
}
void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const {
@@ -52,7 +52,7 @@ uint64_t qpid::framing::AMQHeaderBody::getContentLength() const
void qpid::framing::AMQHeaderBody::print(std::ostream& out) const
{
- out << "header (" << size() << " bytes)";
+ out << "header (" << encodedSize() << " bytes)";
out << "; properties={";
properties.print(out);
out << "}";
diff --git a/cpp/src/qpid/framing/AMQHeaderBody.h b/cpp/src/qpid/framing/AMQHeaderBody.h
index c69a768291..8d96e35720 100644
--- a/cpp/src/qpid/framing/AMQHeaderBody.h
+++ b/cpp/src/qpid/framing/AMQHeaderBody.h
@@ -21,11 +21,12 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "AMQBody.h"
-#include "Buffer.h"
+#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>
@@ -34,16 +35,14 @@
namespace qpid {
namespace framing {
-enum DeliveryMode { TRANSIENT = 1, PERSISTENT = 2};
-
class 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 size() const {
+ uint32_t encodedSize() const {
const boost::optional<T>& p=this->OptProps<T>::props;
- return (p ? p->size() : 0) + Base::size();
+ return (p ? p->encodedSize() : 0) + Base::encodedSize();
}
void encode(Buffer& buffer) const {
const boost::optional<T>& p=this->OptProps<T>::props;
@@ -68,7 +67,7 @@ class AMQHeaderBody : public AMQBody
};
struct Empty {
- uint32_t size() const { return 0; }
+ 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 {}
@@ -83,12 +82,12 @@ public:
inline uint8_t type() const { return HEADER_BODY; }
- uint32_t size() const;
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer, uint32_t size);
- uint64_t getContentLength() const;
- void print(std::ostream& out) const;
- void accept(AMQBodyConstVisitor&) const;
+ 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;
@@ -99,6 +98,8 @@ public:
template <class T> const T* get() const {
return properties.OptProps<T>::props.get_ptr();
}
+
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
};
}}
diff --git a/cpp/src/qpid/framing/AMQHeartbeatBody.cpp b/cpp/src/qpid/framing/AMQHeartbeatBody.cpp
index 140ce2e794..477616221c 100644
--- a/cpp/src/qpid/framing/AMQHeartbeatBody.cpp
+++ b/cpp/src/qpid/framing/AMQHeartbeatBody.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "AMQHeartbeatBody.h"
+#include "qpid/framing/AMQHeartbeatBody.h"
#include <iostream>
qpid::framing::AMQHeartbeatBody::~AMQHeartbeatBody() {}
diff --git a/cpp/src/qpid/framing/AMQHeartbeatBody.h b/cpp/src/qpid/framing/AMQHeartbeatBody.h
index a2701c3398..9b1fe8a4c1 100644
--- a/cpp/src/qpid/framing/AMQHeartbeatBody.h
+++ b/cpp/src/qpid/framing/AMQHeartbeatBody.h
@@ -18,9 +18,10 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "AMQBody.h"
-#include "Buffer.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/CommonImportExport.h"
#ifndef _AMQHeartbeatBody_
#define _AMQHeartbeatBody_
@@ -31,13 +32,14 @@ namespace framing {
class AMQHeartbeatBody : public AMQBody
{
public:
- virtual ~AMQHeartbeatBody();
- inline uint32_t size() const { return 0; }
+ 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*/) {}
- virtual void print(std::ostream& out) const;
+ 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); }
};
}
diff --git a/cpp/src/qpid/framing/AMQMethodBody.cpp b/cpp/src/qpid/framing/AMQMethodBody.cpp
index 924d906d43..594af4c6dc 100644
--- a/cpp/src/qpid/framing/AMQMethodBody.cpp
+++ b/cpp/src/qpid/framing/AMQMethodBody.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "AMQMethodBody.h"
+#include "qpid/framing/AMQMethodBody.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/AMQMethodBody.h b/cpp/src/qpid/framing/AMQMethodBody.h
index da28ee3aa9..c634180712 100644
--- a/cpp/src/qpid/framing/AMQMethodBody.h
+++ b/cpp/src/qpid/framing/AMQMethodBody.h
@@ -21,13 +21,13 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "AMQBody.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
#include "qpid/framing/ProtocolVersion.h"
-#include "qpid/shared_ptr.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/shared_ptr.hpp>
#include <ostream>
-
#include <assert.h>
namespace qpid {
@@ -40,7 +40,7 @@ class MethodBodyConstVisitor;
class AMQMethodBody : public AMQBody {
public:
AMQMethodBody() {}
- virtual ~AMQMethodBody();
+ QPID_COMMON_EXTERN virtual ~AMQMethodBody();
virtual void accept(MethodBodyConstVisitor&) const = 0;
@@ -54,7 +54,7 @@ class AMQMethodBody : public AMQBody {
return amqpClassId()==T::CLASS_ID && amqpMethodId()==T::METHOD_ID;
}
- virtual uint32_t size() const = 0;
+ 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*/ }
diff --git a/cpp/src/qpid/framing/AccumulatedAck.cpp b/cpp/src/qpid/framing/AccumulatedAck.cpp
index 2d3ecf3f6a..2e6433a82f 100644
--- a/cpp/src/qpid/framing/AccumulatedAck.cpp
+++ b/cpp/src/qpid/framing/AccumulatedAck.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "AccumulatedAck.h"
+#include "qpid/framing/AccumulatedAck.h"
#include <assert.h>
#include <iostream>
diff --git a/cpp/src/qpid/framing/AccumulatedAck.h b/cpp/src/qpid/framing/AccumulatedAck.h
index ea78b797e0..8e241b4ba1 100644
--- a/cpp/src/qpid/framing/AccumulatedAck.h
+++ b/cpp/src/qpid/framing/AccumulatedAck.h
@@ -25,8 +25,9 @@
#include <functional>
#include <list>
#include <ostream>
-#include "SequenceNumber.h"
-#include "SequenceNumberSet.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceNumberSet.h"
+#include "qpid/CommonImportExport.h"
namespace qpid {
namespace framing {
@@ -58,17 +59,17 @@ namespace qpid {
*/
std::list<Range> ranges;
- explicit AccumulatedAck(SequenceNumber r = SequenceNumber());
- void update(SequenceNumber firstTag, SequenceNumber lastTag);
- void consolidate();
- void clear();
- bool covers(SequenceNumber tag) const;
+ 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;
- void update(const SequenceNumber cumulative, const SequenceNumberSet& range);
+ QPID_COMMON_EXTERN void update(const SequenceNumber cumulative, const SequenceNumberSet& range);
void operator()(SequenceNumber first, SequenceNumber last) { update(first, last); }
};
- std::ostream& operator<<(std::ostream&, const Range&);
- std::ostream& operator<<(std::ostream&, const AccumulatedAck&);
+ QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const Range&);
+ QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const AccumulatedAck&);
}
}
diff --git a/cpp/src/qpid/framing/Array.cpp b/cpp/src/qpid/framing/Array.cpp
index f0b6331ff3..d95e0d167d 100644
--- a/cpp/src/qpid/framing/Array.cpp
+++ b/cpp/src/qpid/framing/Array.cpp
@@ -18,9 +18,9 @@
* under the License.
*
*/
-#include "Array.h"
-#include "Buffer.h"
-#include "FieldValue.h"
+#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 <assert.h>
@@ -28,25 +28,26 @@
namespace qpid {
namespace framing {
-Array::Array() : typeOctet(0xF0/*void*/) {}
+Array::Array() : type(TYPE_CODE_VOID) {}
-Array::Array(uint8_t type) : typeOctet(type) {}
+Array::Array(TypeCode t) : type(t) {}
+
+Array::Array(uint8_t t) : type(typeCode(t)) {}
Array::Array(const std::vector<std::string>& in)
{
- typeOctet = 0xA4;
+ type = TYPE_CODE_STR16;
for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) {
- ValuePtr value(new StringValue(*i));
+ ValuePtr value(new Str16Value(*i));
values.push_back(value);
}
}
-
-uint32_t Array::size() const {
+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().size();
+ len += (*i)->getData().encodedSize();
}
return len;
}
@@ -55,18 +56,18 @@ int Array::count() const {
return values.size();
}
-std::ostream& operator<<(std::ostream& out, const Array& t) {
- out << "{";
- for(Array::ValueVector::const_iterator i = t.values.begin(); i != t.values.end(); ++i) {
- if (i != t.values.begin()) out << ", ";
- out << *(i->get());
+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(size() - 4);//size added only when array is a top-level type
- buffer.putOctet(typeOctet);
+ 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);
@@ -74,6 +75,7 @@ void Array::encode(Buffer& buffer) const{
}
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) {
@@ -81,21 +83,21 @@ void Array::decode(Buffer& buffer){
<< size << " bytes but only " << available << " available"));
}
if (size) {
- typeOctet = buffer.getOctet();
+ type = TypeCode(buffer.getOctet());
uint32_t count = buffer.getLong();
FieldValue dummy;
- dummy.setType(typeOctet);
+ dummy.setType(type);
available = buffer.available();
- if (available < count * dummy.getData().size()) {
+ if (available < count * dummy.getData().encodedSize()) {
throw IllegalArgumentException(QPID_MSG("Not enough data for array, expected "
- << count << " items of " << dummy.getData().size()
+ << 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(typeOctet);
+ value->setType(type);
value->getData().decode(buffer);
values.push_back(ValuePtr(value));
}
@@ -104,7 +106,7 @@ void Array::decode(Buffer& buffer){
bool Array::operator==(const Array& x) const {
- if (typeOctet != x.typeOctet) return false;
+ 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) {
@@ -114,12 +116,13 @@ bool Array::operator==(const Array& x) const {
return true;
}
-void Array::add(ValuePtr value)
-{
- if (typeOctet != value->getType()) {
- throw IllegalArgumentException(QPID_MSG("Wrong type of value, expected " << typeOctet));
+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.push_back(value);
+ values.insert(i, value);
}
diff --git a/cpp/src/qpid/framing/Array.h b/cpp/src/qpid/framing/Array.h
deleted file mode 100644
index 2cbd4c0b29..0000000000
--- a/cpp/src/qpid/framing/Array.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include <iostream>
-#include <vector>
-#include <boost/shared_ptr.hpp>
-#include <map>
-#include "amqp_types.h"
-#include "FieldValue.h"
-
-#ifndef _Array_
-#define _Array_
-
-namespace qpid {
-namespace framing {
-
-class Buffer;
-
-class Array
-{
- public:
- typedef boost::shared_ptr<FieldValue> ValuePtr;
- typedef std::vector<ValuePtr> ValueVector;
-
- uint32_t size() const;
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer);
-
- int count() const;
- bool operator==(const Array& other) const;
-
- Array();
- Array(uint8_t type);
- //creates a longstr array
- Array(const std::vector<std::string>& in);
-
- void add(ValuePtr value);
-
- template <class T>
- void collect(std::vector<T>& out) const
- {
- for (ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) {
- out.push_back((*i)->get<T>());
- }
- }
-
- private:
- uint8_t typeOctet;
- ValueVector values;
-
- ValueVector::const_iterator begin() const { return values.begin(); }
- ValueVector::const_iterator end() const { return values.end(); }
-
- friend std::ostream& operator<<(std::ostream& out, const Array& body);
-};
-
-}
-}
-
-
-#endif
diff --git a/cpp/src/qpid/framing/Blob.cpp b/cpp/src/qpid/framing/Blob.cpp
index 388d4b64ef..0c8316f3d2 100644
--- a/cpp/src/qpid/framing/Blob.cpp
+++ b/cpp/src/qpid/framing/Blob.cpp
@@ -18,7 +18,7 @@
*
*/
-#include "Blob.h"
+#include "qpid/framing/Blob.h"
namespace qpid {
diff --git a/cpp/src/qpid/framing/Blob.h b/cpp/src/qpid/framing/Blob.h
index 5c84384ad7..e69de29bb2 100644
--- a/cpp/src/qpid/framing/Blob.h
+++ b/cpp/src/qpid/framing/Blob.h
@@ -1,197 +0,0 @@
-#ifndef QPID_FRAMING_BLOB_H
-#define QPID_FRAMING_BLOB_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/static_assert.hpp>
-#include <boost/aligned_storage.hpp>
-#include <boost/checked_delete.hpp>
-#include <boost/utility/typed_in_place_factory.hpp>
-#include <boost/type_traits/is_base_and_derived.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/version.hpp>
-
-#include <new>
-
-#include <assert.h>
-
-
-namespace qpid {
-namespace framing {
-
-using boost::in_place;
-using boost::typed_in_place_factory_base;
-
-/** 0-arg typed_in_place_factory, missing in pre-1.35 boost. */
-#if (BOOST_VERSION < 103500)
-template <class T>
-struct typed_in_place_factory0 : public typed_in_place_factory_base {
- typedef T value_type ;
- void apply ( void* address ) const { new (address) T(); }
-};
-
-/** 0-arg in_place<T>() function, missing from boost. */
-template<class T>
-typed_in_place_factory0<T> in_place() { return typed_in_place_factory0<T>(); }
-#endif
-
-template <class T, class R=void>
-struct EnableInPlace
- : public boost::enable_if<boost::is_base_and_derived<
- typed_in_place_factory_base, T>,
- R>
-{};
-
-template <class T, class R=void>
-struct DisableInPlace
- : public boost::disable_if<boost::is_base_and_derived<
- typed_in_place_factory_base, T>,
- R>
-{};
-
-template <class T> struct BlobHelper {
- static void destroy(void* ptr) { static_cast<T*>(ptr)->~T(); }
- static void copy(void* dest, const void* src) {
- new (dest) T(*static_cast<const T*>(src));
- }
-};
-
-template <> struct BlobHelper<void> {
- static void destroy(void*);
- static void copy(void* to, const void* from);
-};
-
-/**
- * A Blob is a chunk of memory which can contain a single object at
- * a time-arbitrary type, provided sizeof(T)<=blob.size(). Using Blobs
- * ensures proper construction and destruction of its contents,
- * and proper copying between Blobs, but nothing else.
- *
- * In particular you must ensure that the Blob is big enough for its
- * contents and must know the type of object in the Blob to cast get().
- *
- * If BaseType is specified then only an object that can be
- * static_cast to BaseType may be stored in the Blob.
- */
-template <size_t Size, class BaseType=void>
-class Blob
-{
- boost::aligned_storage<Size> store;
- BaseType* basePtr;
-
- void (*destroy)(void*);
- void (*copy)(void*, const void*);
-
- template <class T>void setType() {
- BOOST_STATIC_ASSERT(sizeof(T) <= Size);
- destroy=&BlobHelper<T>::destroy;
- copy=&BlobHelper<T>::copy;
- // Base pointer may be offeset from store.address()
- basePtr = reinterpret_cast<T*>(store.address());
- }
-
- void initialize() {
- destroy=&BlobHelper<void>::destroy;
- copy=&BlobHelper<void>::copy;
- basePtr=0;
- }
-
- template<class Factory>
- typename EnableInPlace<Factory>::type apply(const Factory& factory)
- {
- typedef typename Factory::value_type T;
- assert(empty());
- factory.apply(store.address());
- setType<T>();
- }
-
- void assign(const Blob& b) {
- assert(empty());
- if (b.empty()) return;
- b.copy(this->store.address(), b.store.address());
- copy = b.copy;
- destroy = b.destroy;
- basePtr = reinterpret_cast<BaseType*>(
- ((char*)this)+ ((const char*)(b.basePtr) - (const char*)(&b)));
- }
-
- public:
- /** Construct an empty Blob. */
- Blob() { initialize(); }
-
- /** Copy a Blob. */
- Blob(const Blob& b) { initialize(); assign(b); }
-
- /** Construct from in_place constructor. */
- template<class InPlace>
- Blob(const InPlace & expr, typename EnableInPlace<InPlace>::type* =0) {
- initialize(); apply(expr);
- }
-
- /** Construct by copying an object constructor. */
- template<class T>
- Blob(const T & t, typename DisableInPlace<T>::type* =0) {
- initialize(); apply(in_place<T>(t));
- }
-
- ~Blob() { clear(); }
-
- /** Assign from another Blob. */
- Blob& operator=(const Blob& b) {
- clear();
- assign(b);
- return *this;
- }
-
- /** Assign from an in_place constructor expression. */
- template<class InPlace>
- typename EnableInPlace<InPlace,Blob&>::type operator=(const InPlace& expr) {
- clear(); apply(expr); return *this;
- }
-
- /** Assign from an object of type T. */
- template <class T>
- typename DisableInPlace<T, Blob&>::type operator=(const T& x) {
- clear(); apply(in_place<T>(x)); return *this;
- }
-
- /** Get pointer to Blob contents, returns 0 if empty. */
- BaseType* get() { return basePtr; }
-
- /** Get pointer to Blob contents, returns 0 if empty. */
- const BaseType* get() const { return basePtr; }
-
- /** Destroy the object in the Blob making it empty. */
- void clear() {
- void (*oldDestroy)(void*) = destroy;
- initialize();
- oldDestroy(store.address());
- }
-
- bool empty() const { return destroy==BlobHelper<void>::destroy; }
-
- static size_t size() { return Size; }
-};
-
-}} // namespace qpid::framing
-
-
-#endif /*!QPID_FRAMING_BLOB_H*/
diff --git a/cpp/src/qpid/sys/Thread.h b/cpp/src/qpid/framing/BodyFactory.h
index 3188e45341..6a8d9b1988 100644
--- a/cpp/src/qpid/sys/Thread.h
+++ b/cpp/src/qpid/framing/BodyFactory.h
@@ -1,5 +1,5 @@
-#ifndef _sys_Thread_h
-#define _sys_Thread_h
+#ifndef QPID_FRAMING_BODYFACTORY_H
+#define QPID_FRAMING_BODYFACTORY_H
/*
*
@@ -21,34 +21,27 @@
* under the License.
*
*/
-#include <boost/shared_ptr.hpp>
-namespace qpid {
-namespace sys {
-
-class Runnable;
-class ThreadPrivate;
+#include <boost/intrusive_ptr.hpp>
-class Thread
-{
- boost::shared_ptr<ThreadPrivate> impl;
+namespace qpid {
+namespace framing {
+/**
+ * Indirect creation of body types to allow centralized changes to
+ * memory management strategy.
+ */
+class BodyFactory {
public:
- Thread();
- explicit Thread(qpid::sys::Runnable*);
- explicit Thread(qpid::sys::Runnable&);
-
- void join();
-
- unsigned long id();
-
- static Thread current();
+ template <class BodyType> static boost::intrusive_ptr<BodyType> create() {
+ return new BodyType;
+ }
- /** ID of current thread for logging.
- * Workaround for broken Thread::current() in APR
- */
- static unsigned long logId() { return current().id(); }
+ template <class BodyType> static boost::intrusive_ptr<BodyType> copy(const BodyType& body) {
+ return new BodyType(body);
+ }
};
-}}
-#endif /*!_sys_Thread_h*/
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_BODYFACTORY_H*/
diff --git a/cpp/src/qpid/framing/BodyHandler.cpp b/cpp/src/qpid/framing/BodyHandler.cpp
index ffbcf33a95..e2128596ed 100644
--- a/cpp/src/qpid/framing/BodyHandler.cpp
+++ b/cpp/src/qpid/framing/BodyHandler.cpp
@@ -18,11 +18,11 @@
* under the License.
*
*/
-#include "BodyHandler.h"
-#include "AMQMethodBody.h"
-#include "AMQHeaderBody.h"
-#include "AMQContentBody.h"
-#include "AMQHeartbeatBody.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 <boost/cast.hpp>
#include "qpid/framing/reply_exceptions.h"
diff --git a/cpp/src/qpid/framing/BodyHolder.cpp b/cpp/src/qpid/framing/BodyHolder.cpp
index 1b2f74de2c..e69de29bb2 100644
--- a/cpp/src/qpid/framing/BodyHolder.cpp
+++ b/cpp/src/qpid/framing/BodyHolder.cpp
@@ -1,76 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "BodyHolder.h"
-#include "AMQMethodBody.h"
-#include "AMQHeaderBody.h"
-#include "AMQContentBody.h"
-#include "AMQHeartbeatBody.h"
-#include "Buffer.h"
-#include "qpid/framing/reply_exceptions.h"
-
-namespace qpid {
-namespace framing {
-
-
-// BodyHolder::operator=(const AMQBody&) is defined
-// in generated file BodyHolder_gen.cpp
-
-
-void BodyHolder::encode(Buffer& b) const {
- const AMQMethodBody* method=getMethod();
- if (method) {
- b.putOctet(method->amqpClassId());
- b.putOctet(method->amqpMethodId());
- method->encode(b);
- }
- else
- get()->encode(b);
-}
-
-void BodyHolder::decode(uint8_t type, Buffer& buffer, uint32_t size) {
- switch(type)
- {
- case 0://CONTROL
- case METHOD_BODY: {
- ClassId c = buffer.getOctet();
- MethodId m = buffer.getOctet();
- setMethod(c, m);
- break;
- }
- case HEADER_BODY: *this=in_place<AMQHeaderBody>(); break;
- case CONTENT_BODY: *this=in_place<AMQContentBody>(); break;
- case HEARTBEAT_BODY: *this=in_place<AMQHeartbeatBody>(); break;
- default:
- throw IllegalArgumentException(QPID_MSG("Invalid frame type " << type));
- }
- get()->decode(buffer, size);
-}
-
-uint32_t BodyHolder::size() const {
- const AMQMethodBody* method=getMethod();
- if (method)
- return sizeof(ClassId)+sizeof(MethodId)+method->size();
- else
- return get()->size();
-}
-
-}} // namespace qpid::framing
-
diff --git a/cpp/src/qpid/framing/BodyHolder.h b/cpp/src/qpid/framing/BodyHolder.h
index f843961a32..e69de29bb2 100644
--- a/cpp/src/qpid/framing/BodyHolder.h
+++ b/cpp/src/qpid/framing/BodyHolder.h
@@ -1,88 +0,0 @@
-#ifndef QPID_FRAMING_BODYHOLDER_H
-#define QPID_FRAMING_BODYHOLDER_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/AMQBody.h"
-#include "qpid/framing/Blob.h"
-#include "qpid/framing/MaxMethodBodySize.h" // Generated file.
-#include "qpid/framing/amqp_types.h"
-#include "qpid/RefCounted.h"
-
-
-namespace qpid {
-namespace framing {
-
-class AMQMethodBody;
-class AMQBody;
-class Buffer;
-
-/**
- * Holder for arbitrary frame body.
- */
-class BodyHolder : public RefCounted
-{
- public:
- // default copy, assign dtor ok.
- BodyHolder() {}
- BodyHolder(const AMQBody& b) { setBody(b); }
- BodyHolder(ClassId c, MethodId m) { setMethod(c,m); }
-
- /** Construct from an in_place constructor expression. */
- template <class InPlace>
- BodyHolder(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0)
- : blob(ip) {}
-
- void setBody(const AMQBody& b);
-
- /** Assign from an in_place constructor expression. */
- template <class InPlace>
- typename EnableInPlace<InPlace,BodyHolder&>::type
- operator=(const InPlace& ip) { blob=ip; return *this; }
-
- /** Assign by copying. */
- template <class T>
- typename DisableInPlace<T,BodyHolder&>::type operator=(const T& x)
- { blob=in_place<T>(x); return *this; }
-
- /** Set to method with ClassId c, MethodId m. */
- void setMethod(ClassId c, MethodId m);
-
- void encode(Buffer&) const;
- void decode(uint8_t frameType, Buffer&, uint32_t=0);
- uint32_t size() const;
-
- /** Return body pointer or 0 if empty. */
- AMQBody* get() { return blob.get(); }
- const AMQBody* get() const { return blob.get(); }
-
- /** Return method pointer or 0 if not a method. */
- AMQMethodBody* getMethod() { return get()->getMethod(); }
- const AMQMethodBody* getMethod() const { return get()->getMethod(); }
-
- private:
- Blob<MAX_METHOD_BODY_SIZE, AMQBody> blob;
-};
-
-}} // namespace qpid::framing
-
-#endif /*!QPID_FRAMING_BODYHOLDER_H*/
diff --git a/cpp/src/qpid/framing/Buffer.cpp b/cpp/src/qpid/framing/Buffer.cpp
index 9c089fd0f8..051e7a2362 100644
--- a/cpp/src/qpid/framing/Buffer.cpp
+++ b/cpp/src/qpid/framing/Buffer.cpp
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "Buffer.h"
-#include "FieldTable.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldTable.h"
#include <string.h>
#include <boost/format.hpp>
namespace qpid {
@@ -51,12 +51,14 @@ void Buffer::reset(){
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){
@@ -65,6 +67,7 @@ void Buffer::putLong(uint32_t i){
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){
@@ -76,6 +79,7 @@ void Buffer::putLongLong(uint64_t i){
void Buffer::putInt8(int8_t i){
data[position++] = (uint8_t) i;
+ assert(position <= size);
}
void Buffer::putInt16(int16_t i){
@@ -110,19 +114,22 @@ void Buffer::putDouble(double f){
putLongLong (val.i);
}
-void Buffer::putBin128(uint8_t* b){
+void Buffer::putBin128(const uint8_t* b){
memcpy (data + position, b, 16);
position += 16;
}
uint8_t Buffer::getOctet(){
- return (uint8_t) data[position++];
+ 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;
}
@@ -131,6 +138,7 @@ uint32_t Buffer::getLong(){
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;
@@ -146,7 +154,9 @@ uint64_t Buffer::getLongLong(){
}
int8_t Buffer::getInt8(){
- return (int8_t) data[position++];
+ int8_t i = static_cast<int8_t>(data[position++]);
+ assert(position <= size);
+ return i;
}
int16_t Buffer::getInt16(){
@@ -220,14 +230,16 @@ void Buffer::putUInt<8>(uint64_t i) {
}
void Buffer::putShortString(const string& s){
- uint8_t len = s.length();
+ size_t slen = s.length();
+ uint8_t len = slen < 0x100 ? (uint8_t) slen : 0xFF;
putOctet(len);
s.copy(data + position, len);
position += len;
}
void Buffer::putMediumString(const string& s){
- uint16_t len = s.length();
+ size_t slen = s.length();
+ uint16_t len = slen < 0x10000 ? (uint16_t) slen : 0xFFFF;
putShort(len);
s.copy(data + position, len);
position += len;
diff --git a/cpp/src/qpid/framing/Buffer.h b/cpp/src/qpid/framing/Buffer.h
deleted file mode 100644
index a27b15cac0..0000000000
--- a/cpp/src/qpid/framing/Buffer.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "amqp_types.h"
-#include "qpid/Exception.h"
-#include <boost/iterator/iterator_facade.hpp>
-
-#ifndef _Buffer_
-#define _Buffer_
-
-namespace qpid {
-namespace framing {
-
-struct OutOfBounds : qpid::Exception {};
-
-class Content;
-class FieldTable;
-
-class Buffer
-{
- uint32_t size;
- char* data;
- uint32_t position;
- uint32_t r_position;
-
- void checkAvailable(uint32_t count) { if (position + count > size) throw OutOfBounds(); }
-
- public:
-
- /** Buffer input/output iterator.
- * Supports using an amqp_0_10::Codec with a framing::Buffer.
- */
- class Iterator : public boost::iterator_facade<
- Iterator, char, boost::random_access_traversal_tag>
- {
- public:
- Iterator(Buffer& b) : buffer(&b) {}
-
- private:
- friend class boost::iterator_core_access;
- char& dereference() const { return buffer->data[buffer->position]; }
- void increment() { ++buffer->position; }
- bool equal(const Iterator& x) const { return buffer == x.buffer; }
-
- Buffer* buffer;
- };
-
- friend class Iterator;
-
- Buffer(char* data=0, uint32_t size=0);
-
- void record();
- void restore(bool reRecord = false);
- void reset();
-
- uint32_t available() { return size - position; }
- uint32_t getSize() { return size; }
- uint32_t getPosition() { return position; }
- Iterator getIterator() { return Iterator(*this); }
-
- void putOctet(uint8_t i);
- void putShort(uint16_t i);
- void putLong(uint32_t i);
- void putLongLong(uint64_t i);
- void putInt8(int8_t i);
- void putInt16(int16_t i);
- void putInt32(int32_t i);
- void putInt64(int64_t i);
- void putFloat(float f);
- void putDouble(double f);
- void putBin128(uint8_t* b);
-
- uint8_t getOctet();
- uint16_t getShort();
- uint32_t getLong();
- uint64_t getLongLong();
- int8_t getInt8();
- int16_t getInt16();
- int32_t getInt32();
- int64_t getInt64();
- float getFloat();
- double getDouble();
-
- template <int n>
- uint64_t getUInt();
-
- template <int n>
- void putUInt(uint64_t);
-
- void putShortString(const string& s);
- void putMediumString(const string& s);
- void putLongString(const string& s);
- void getShortString(string& s);
- void getMediumString(string& s);
- void getLongString(string& s);
- void getBin128(uint8_t* b);
-
- void putRawData(const string& s);
- void getRawData(string& s, uint32_t size);
-
- void putRawData(const uint8_t* data, size_t size);
- void getRawData(uint8_t* data, size_t size);
-
- template <class T> void put(const T& data) { data.encode(*this); }
- template <class T> void get(T& data) { data.decode(*this); }
-
- void dump(std::ostream&) const;
-};
-
-std::ostream& operator<<(std::ostream&, const Buffer&);
-
-}} // namespace qpid::framing
-
-
-#endif
diff --git a/cpp/src/qpid/framing/ChannelHandler.h b/cpp/src/qpid/framing/ChannelHandler.h
index 69aaeac492..ddab204578 100644
--- a/cpp/src/qpid/framing/ChannelHandler.h
+++ b/cpp/src/qpid/framing/ChannelHandler.h
@@ -21,8 +21,8 @@
* under the License.
*
*/
-#include "FrameHandler.h"
-#include "AMQFrame.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/AMQFrame.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/Endian.cpp b/cpp/src/qpid/framing/Endian.cpp
new file mode 100644
index 0000000000..5acc3c459f
--- /dev/null
+++ b/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/cpp/src/qpid/sys/IOHandle.h b/cpp/src/qpid/framing/Endian.h
index d06512da58..077d5a3e9b 100644
--- a/cpp/src/qpid/sys/IOHandle.h
+++ b/cpp/src/qpid/framing/Endian.h
@@ -1,5 +1,5 @@
-#ifndef _sys_IOHandle_h
-#define _sys_IOHandle_h
+#ifndef QPID_FRAMING_ENDIAN_H
+#define QPID_FRAMING_ENDIAN_H
/*
*
@@ -22,24 +22,25 @@
*
*/
+#include "qpid/sys/IntegerTypes.h"
+
namespace qpid {
-namespace sys {
+namespace framing {
/**
- * This is a class intended to abstract the Unix concept of file descriptor or the Windows concept of HANDLE
+ * Conversion utility for little-endian platforms that need to convert
+ * to and from network ordered octet sequences
*/
-class PollerHandle;
-class IOHandlePrivate;
-class IOHandle {
- friend class PollerHandle;
-
-protected:
- IOHandlePrivate* const impl;
-
- IOHandle(IOHandlePrivate*);
- virtual ~IOHandle();
+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 // _sys_IOHandle_h
+#endif /*!QPID_FRAMING_ENDIAN_H*/
diff --git a/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp
index bd20c10c37..e2e91e450a 100644
--- a/cpp/src/qpid/framing/FieldTable.cpp
+++ b/cpp/src/qpid/framing/FieldTable.cpp
@@ -18,9 +18,11 @@
* under the License.
*
*/
-#include "FieldTable.h"
-#include "Buffer.h"
-#include "FieldValue.h"
+#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 <assert.h>
@@ -28,13 +30,25 @@
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::size() const {
+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)->size();
+ len += 1 + (i->first).size() + (i->second)->encodedSize();
}
return len;
}
@@ -69,17 +83,38 @@ void FieldTable::setString(const std::string& name, const std::string& value){
values[name] = ValuePtr(new Str16Value(value));
}
-void FieldTable::setInt(const std::string& name, int value){
+void FieldTable::setInt(const std::string& name, const int value){
values[name] = ValuePtr(new IntegerValue(value));
}
-void FieldTable::setTimestamp(const std::string& name, uint64_t 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::setTable(const std::string& name, const FieldTable& 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
{
@@ -105,24 +140,54 @@ T getValue(const FieldTable::ValuePtr value)
return value->get<T>();
}
-std::string FieldTable::getString(const std::string& name) const {
+std::string FieldTable::getAsString(const std::string& name) const {
return getValue<std::string>(get(name));
}
-int FieldTable::getInt(const std::string& name) const {
+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::getTable(const std::string& name, FieldTable& value) const {
-// value = getValue<FieldTable>(name);
-//}
-void FieldTable::encode(Buffer& buffer) const{
- buffer.putLong(size() - 4);
+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);
@@ -131,11 +196,12 @@ void FieldTable::encode(Buffer& buffer) const{
}
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."));
+ 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--){
@@ -149,7 +215,6 @@ void FieldTable::decode(Buffer& buffer){
}
}
-
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) {
@@ -160,10 +225,22 @@ bool FieldTable::operator==(const FieldTable& x) const {
return true;
}
-//void FieldTable::erase(const std::string& name)
-//{
-// values.erase(values.find(name));
-//}
+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/cpp/src/qpid/framing/FieldTable.h b/cpp/src/qpid/framing/FieldTable.h
deleted file mode 100644
index 3c65d31aee..0000000000
--- a/cpp/src/qpid/framing/FieldTable.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include <iostream>
-#include <vector>
-#include <boost/shared_ptr.hpp>
-#include <map>
-#include "amqp_types.h"
-
-#ifndef _FieldTable_
-#define _FieldTable_
-
-namespace qpid {
- /**
- * The framing namespace contains classes that are used to create,
- * send and receive the basic packets from which AMQP is built.
- */
-namespace framing {
-
-class FieldValue;
-class Buffer;
-
-/**
- * A set of name-value pairs. (See the AMQP spec for more details on
- * AMQP field tables).
- *
- * \ingroup clientapi
- */
-class FieldTable
-{
- public:
- typedef boost::shared_ptr<FieldValue> ValuePtr;
- typedef std::map<std::string, ValuePtr> ValueMap;
- typedef ValueMap::iterator iterator;
-
- ~FieldTable();
- uint32_t size() const;
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer);
-
- int count() const;
- void set(const std::string& name, const ValuePtr& value);
- ValuePtr get(const std::string& name) const;
-
- void setString(const std::string& name, const std::string& value);
- void setInt(const std::string& name, int value);
- void setTimestamp(const std::string& name, uint64_t value);
- void setTable(const std::string& name, const FieldTable& value);
- //void setDecimal(string& name, xxx& value);
-
- std::string getString(const std::string& name) const;
- int getInt(const std::string& name) const;
-// uint64_t getTimestamp(const std::string& name) const;
-// void getTable(const std::string& name, FieldTable& value) const;
-// //void getDecimal(string& name, xxx& value);
-// //void erase(const std::string& name);
-
-
- bool operator==(const FieldTable& other) const;
-
- // Map-like interface.
- // TODO: may need to duplicate into versions that return mutable iterator
- ValueMap::const_iterator begin() const { return values.begin(); }
- ValueMap::const_iterator end() const { return values.end(); }
- ValueMap::const_iterator find(const std::string& s) const { return values.find(s); }
-
- // ### Hack Alert
-
- ValueMap::iterator getValues() { return values.begin(); }
-
- private:
- ValueMap values;
-
- friend std::ostream& operator<<(std::ostream& out, const FieldTable& body);
-};
-
-//class FieldNotFoundException{};
-//class UnknownFieldName : public FieldNotFoundException{};
-//class IncorrectFieldType : public FieldNotFoundException{};
-}
-}
-
-
-#endif
diff --git a/cpp/src/qpid/framing/FieldValue.cpp b/cpp/src/qpid/framing/FieldValue.cpp
index 681f20a793..5bac931b83 100644
--- a/cpp/src/qpid/framing/FieldValue.cpp
+++ b/cpp/src/qpid/framing/FieldValue.cpp
@@ -18,8 +18,11 @@
* under the License.
*
*/
-#include "FieldValue.h"
-#include "Buffer.h"
+#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"
namespace qpid {
@@ -33,53 +36,60 @@ uint8_t FieldValue::getType()
void FieldValue::setType(uint8_t type)
{
typeOctet = type;
-
- 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));
+ 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));
+ }
}
}
@@ -102,10 +112,10 @@ bool FieldValue::operator==(const FieldValue& v) const
*data == *v.data;
}
-StringValue::StringValue(const std::string& v) :
+Str8Value::Str8Value(const std::string& v) :
FieldValue(
- 0xA4,
- new VariableWidthValue<4>(
+ TYPE_CODE_STR8,
+ new VariableWidthValue<1>(
reinterpret_cast<const uint8_t*>(v.data()),
reinterpret_cast<const uint8_t*>(v.data()+v.size())))
{
@@ -129,16 +139,77 @@ Struct32Value::Struct32Value(const std::string& v) :
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))
{
}
-TimeValue::TimeValue(uint64_t v) :
- FieldValue(0x32, 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))
{
}
-FieldTableValue::FieldTableValue(const FieldTable&) : FieldValue()
+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))
+{}
+
+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/cpp/src/qpid/framing/FieldValue.h b/cpp/src/qpid/framing/FieldValue.h
deleted file mode 100644
index a4c20bf415..0000000000
--- a/cpp/src/qpid/framing/FieldValue.h
+++ /dev/null
@@ -1,241 +0,0 @@
-#ifndef _framing_FieldValue_h
-#define _framing_FieldValue_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 "Buffer.h"
-#include "amqp_types.h"
-
-#include "assert.h"
-
-#include <iostream>
-#include <memory>
-#include <vector>
-
-namespace qpid {
-namespace framing {
-
-/**
- * Exception that is the base exception for all field table errors.
- *
- * \ingroup clientapi
- */
-class FieldValueException : public qpid::Exception {};
-
-/**
- * Exception thrown when we can't perform requested conversion
- *
- * \ingroup clientapi
- */
-struct InvalidConversionException : public FieldValueException {
- InvalidConversionException() {}
-};
-
-/**
- * Value that can appear in an AMQP field table
- *
- * \ingroup clientapi
- */
-class FieldValue {
- public:
- /*
- * Abstract type for content of different types
- */
- class Data {
- public:
- virtual ~Data() {};
- virtual uint32_t size() const = 0;
- virtual void encode(Buffer& buffer) = 0;
- virtual void decode(Buffer& buffer) = 0;
- virtual bool operator==(const Data&) const = 0;
-
- virtual bool convertsToInt() const { return false; }
- virtual bool convertsToString() const { return false; }
- virtual int64_t getInt() const { throw InvalidConversionException();}
- virtual std::string getString() const { throw InvalidConversionException(); }
-
- virtual void print(std::ostream& out) const = 0;
- };
-
- FieldValue(): data(0) {};
- // Default assignment operator is fine
- void setType(uint8_t type);
- uint8_t getType();
- Data& getData() { return *data; }
- uint32_t size() const { return 1 + data->size(); };
- bool empty() const { return data.get() == 0; }
- void encode(Buffer& buffer);
- void decode(Buffer& buffer);
- bool operator==(const FieldValue&) const;
- bool operator!=(const FieldValue& v) const { return !(*this == v); }
- void print(std::ostream& out) const { out << "(0x" << std::hex << int(typeOctet) << ")"; data->print(out); }
-
- template <typename T> bool convertsTo() const { return false; }
- template <typename T> T get() const { throw InvalidConversionException(); }
-
- protected:
- FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {}
-
- private:
- uint8_t typeOctet;
- std::auto_ptr<Data> data;
-};
-
-template <>
-inline bool FieldValue::convertsTo<int>() const { return data->convertsToInt(); }
-
-template <>
-inline bool FieldValue::convertsTo<std::string>() const { return data->convertsToString(); }
-
-template <>
-inline int FieldValue::get<int>() const { return data->getInt(); }
-
-template <>
-inline std::string FieldValue::get<std::string>() const { return data->getString(); }
-
-inline std::ostream& operator<<(std::ostream& out, const FieldValue& v) {
- v.print(out);
- return out;
-}
-
-template <int width>
-class FixedWidthValue : public FieldValue::Data {
- uint8_t octets[width];
-
- public:
- FixedWidthValue() {}
- FixedWidthValue(const uint8_t (&data)[width]) : octets(data) {}
- FixedWidthValue(uint64_t v)
- {
- for (int i = width; i > 0; --i) {
- octets[i-1] = (uint8_t) (0xFF & v); v >>= 8;
- }
- octets[0] = (uint8_t) (0xFF & v);
- }
-
- uint32_t size() const { return width; }
- void encode(Buffer& buffer) { buffer.putRawData(octets, width); }
- void decode(Buffer& buffer) { buffer.getRawData(octets, width); }
- bool operator==(const Data& d) const {
- const FixedWidthValue<width>* rhs = dynamic_cast< const FixedWidthValue<width>* >(&d);
- if (rhs == 0) return false;
- else return std::equal(&octets[0], &octets[width], &rhs->octets[0]);
- }
-
- bool convertsToInt() const { return true; }
- int64_t getInt() const
- {
- int64_t v = 0;
- for (int i = 0; i < width-1; ++i) {
- v |= octets[i]; v <<= 8;
- }
- v |= octets[width-1];
- return v;
- }
-
- void print(std::ostream& o) const { o << "F" << width << ":"; };
-};
-
-template <>
-class FixedWidthValue<0> : public FieldValue::Data {
- public:
- // Implicit default constructor is fine
- uint32_t size() const { return 0; }
- void encode(Buffer&) {};
- void decode(Buffer&) {};
- bool operator==(const Data& d) const {
- const FixedWidthValue<0>* rhs = dynamic_cast< const FixedWidthValue<0>* >(&d);
- return rhs != 0;
- }
- void print(std::ostream& o) const { o << "F0"; };
-};
-
-template <int lenwidth>
-class VariableWidthValue : public FieldValue::Data {
- std::vector<uint8_t> octets;
-
- public:
- VariableWidthValue() {}
- VariableWidthValue(const std::vector<uint8_t>& data) : octets(data) {}
- VariableWidthValue(const uint8_t* start, const uint8_t* end) : octets(start, end) {}
- uint32_t size() const { return lenwidth + octets.size(); }
- void encode(Buffer& buffer) {
- buffer.putUInt<lenwidth>(octets.size());
- buffer.putRawData(&octets[0], octets.size());
- };
- void decode(Buffer& buffer) {
- uint32_t len = buffer.getUInt<lenwidth>();
- octets.resize(len);
- buffer.getRawData(&octets[0], len);
- }
- bool operator==(const Data& d) const {
- const VariableWidthValue<lenwidth>* rhs = dynamic_cast< const VariableWidthValue<lenwidth>* >(&d);
- if (rhs == 0) return false;
- else return octets==rhs->octets;
- }
-
- bool convertsToString() const { return true; }
- std::string getString() const { return std::string(octets.begin(), octets.end()); }
-
- void print(std::ostream& o) const { o << "V" << lenwidth << ":" << octets.size() << ":"; };
-};
-
-/*
- * Basic string value encodes as iso-8859-15 with 32 bit length
- */
-class StringValue : public FieldValue {
- public:
- StringValue(const std::string& v);
-};
-
-class Str16Value : public FieldValue {
- public:
- Str16Value(const std::string& v);
-};
-
-class Struct32Value : public FieldValue {
- public:
- Struct32Value(const std::string& v);
-};
-
-
-/*
- * Basic integer value encodes as signed 32 bit
- */
-class IntegerValue : public FieldValue {
- public:
- IntegerValue(int v);
-};
-
-class TimeValue : public FieldValue {
- public:
- TimeValue(uint64_t v);
-};
-
-class FieldTableValue : public FieldValue {
- public:
- FieldTableValue(const FieldTable&);
-};
-
-}} // qpid::framing
-
-#endif
diff --git a/cpp/src/qpid/framing/FrameDecoder.cpp b/cpp/src/qpid/framing/FrameDecoder.cpp
new file mode 100644
index 0000000000..90cbbd84a1
--- /dev/null
+++ b/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/cpp/src/qpid/framing/FrameDecoder.h b/cpp/src/qpid/framing/FrameDecoder.h
new file mode 100644
index 0000000000..26bed6c447
--- /dev/null
+++ b/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/cpp/src/qpid/framing/FrameHandler.h b/cpp/src/qpid/framing/FrameHandler.h
index 457968c35e..fa1fb535ef 100644
--- a/cpp/src/qpid/framing/FrameHandler.h
+++ b/cpp/src/qpid/framing/FrameHandler.h
@@ -20,7 +20,7 @@
* under the License.
*
*/
-#include "Handler.h"
+#include "qpid/framing/Handler.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/FrameSet.cpp b/cpp/src/qpid/framing/FrameSet.cpp
index 5326ab7c14..c03dd39458 100644
--- a/cpp/src/qpid/framing/FrameSet.cpp
+++ b/cpp/src/qpid/framing/FrameSet.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "FrameSet.h"
+#include "qpid/framing/FrameSet.h"
#include "qpid/framing/all_method_bodies.h"
#include "qpid/framing/frame_functors.h"
#include "qpid/framing/MessageProperties.h"
@@ -33,7 +33,7 @@ FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalcula
void FrameSet::append(const AMQFrame& part)
{
parts.push_back(part);
- recalculateSize = true;
+ recalculateSize = true;
}
bool FrameSet::isComplete() const
@@ -52,6 +52,11 @@ 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>();
diff --git a/cpp/src/qpid/framing/FrameSet.h b/cpp/src/qpid/framing/FrameSet.h
index 82987910a7..398a709353 100644
--- a/cpp/src/qpid/framing/FrameSet.h
+++ b/cpp/src/qpid/framing/FrameSet.h
@@ -23,6 +23,7 @@
#include "qpid/framing/amqp_framing.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/SequenceNumber.h"
+#include "qpid/CommonImportExport.h"
#ifndef _FrameSet_
#define _FrameSet_
@@ -44,19 +45,21 @@ class FrameSet
public:
typedef boost::shared_ptr<FrameSet> shared_ptr;
- FrameSet(const SequenceNumber& id);
- void append(const AMQFrame& part);
- bool isComplete() const;
+ QPID_COMMON_EXTERN FrameSet(const SequenceNumber& id);
+ QPID_COMMON_EXTERN void append(const AMQFrame& part);
+ QPID_COMMON_EXTERN bool isComplete() const;
- uint64_t getContentSize() const;
- void getContent(std::string&) const;
- std::string getContent() 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;
- const AMQMethodBody* getMethod() const;
- const AMQHeaderBody* getHeaders() const;
- AMQHeaderBody* getHeaders();
+ 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();
@@ -68,11 +71,19 @@ public:
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) {
diff --git a/cpp/src/qpid/framing/Handler.h b/cpp/src/qpid/framing/Handler.h
index e07a803e17..fa8db36f49 100644
--- a/cpp/src/qpid/framing/Handler.h
+++ b/cpp/src/qpid/framing/Handler.h
@@ -21,7 +21,7 @@
* under the License.
*
*/
-#include "qpid/shared_ptr.h"
+#include <boost/shared_ptr.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <assert.h>
diff --git a/cpp/src/qpid/framing/HeaderProperties.h b/cpp/src/qpid/framing/HeaderProperties.h
index d66c1d00d6..8b1828daec 100644
--- a/cpp/src/qpid/framing/HeaderProperties.h
+++ b/cpp/src/qpid/framing/HeaderProperties.h
@@ -18,8 +18,8 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "Buffer.h"
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Buffer.h"
#ifndef _HeaderProperties_
#define _HeaderProperties_
@@ -33,7 +33,7 @@ namespace framing {
public:
inline virtual ~HeaderProperties(){}
virtual uint8_t classId() const = 0;
- virtual uint32_t size() const = 0;
+ virtual uint32_t encodedSize() const = 0;
virtual void encode(Buffer& buffer) const = 0;
virtual void decode(Buffer& buffer, uint32_t size) = 0;
};
diff --git a/cpp/src/qpid/framing/InitiationHandler.cpp b/cpp/src/qpid/framing/InitiationHandler.cpp
index eceeaf4abc..7ded505a47 100644
--- a/cpp/src/qpid/framing/InitiationHandler.cpp
+++ b/cpp/src/qpid/framing/InitiationHandler.cpp
@@ -19,6 +19,6 @@
*
*/
-#include "InitiationHandler.h"
+#include "qpid/framing/InitiationHandler.h"
qpid::framing::InitiationHandler::~InitiationHandler() {}
diff --git a/cpp/src/qpid/framing/InitiationHandler.h b/cpp/src/qpid/framing/InitiationHandler.h
index 16a6b502e8..5dfcc6b468 100644
--- a/cpp/src/qpid/framing/InitiationHandler.h
+++ b/cpp/src/qpid/framing/InitiationHandler.h
@@ -23,7 +23,7 @@
#ifndef _InitiationHandler_
#define _InitiationHandler_
-#include "ProtocolInitiation.h"
+#include "qpid/framing/ProtocolInitiation.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/InputHandler.h b/cpp/src/qpid/framing/InputHandler.h
index 3a6d786a24..3efb23632a 100644
--- a/cpp/src/qpid/framing/InputHandler.h
+++ b/cpp/src/qpid/framing/InputHandler.h
@@ -21,7 +21,7 @@
*
*/
-#include "FrameHandler.h"
+#include "qpid/framing/FrameHandler.h"
#include <boost/noncopyable.hpp>
namespace qpid {
diff --git a/cpp/src/qpid/framing/IsInSequenceSet.h b/cpp/src/qpid/framing/IsInSequenceSet.h
new file mode 100644
index 0000000000..fe10c1b9fa
--- /dev/null
+++ b/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/cpp/src/qpid/framing/List.cpp b/cpp/src/qpid/framing/List.cpp
new file mode 100644
index 0000000000..bde7dabbac
--- /dev/null
+++ b/cpp/src/qpid/framing/List.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/framing/List.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/reply_exceptions.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/cpp/src/qpid/sys/Monitor.h b/cpp/src/qpid/framing/MethodBodyFactory.h
index 1d9835675c..607ec9d959 100644
--- a/cpp/src/qpid/sys/Monitor.h
+++ b/cpp/src/qpid/framing/MethodBodyFactory.h
@@ -1,5 +1,5 @@
-#ifndef _sys_Monitor_h
-#define _sys_Monitor_h
+#ifndef QPID_FRAMING_METHODBODYFACTORY_H
+#define QPID_FRAMING_METHODBODYFACTORY_H
/*
*
@@ -21,31 +21,24 @@
* under the License.
*
*/
-
-#include <sys/errno.h>
-#include "Condition.h"
+#include "qpid/framing/amqp_types.h"
+#include <boost/intrusive_ptr.hpp>
namespace qpid {
-namespace sys {
+namespace framing {
+
+class AMQMethodBody;
/**
- * A monitor is a condition variable and a mutex
+ * Functions to create instances of AMQMethodBody sub-classes.
+ * Note: MethodBodyFactory.cpp file is generated by rubygen.
*/
-class Monitor : public Mutex, public Condition {
+class MethodBodyFactory
+{
public:
- using Condition::wait;
- inline void wait();
- inline bool wait(const AbsTime& absoluteTime);
+ static boost::intrusive_ptr<AMQMethodBody> create(ClassId c, MethodId m);
};
+}} // namespace qpid::framing
-void Monitor::wait() {
- Condition::wait(*this);
-}
-
-bool Monitor::wait(const AbsTime& absoluteTime) {
- return Condition::wait(*this, absoluteTime);
-}
-
-}}
-#endif /*!_sys_Monitor_h*/
+#endif /*!QPID_FRAMING_METHODBODYFACTORY_H*/
diff --git a/cpp/src/qpid/framing/MethodContent.h b/cpp/src/qpid/framing/MethodContent.h
index 737c0d6b7b..b290a0c140 100644
--- a/cpp/src/qpid/framing/MethodContent.h
+++ b/cpp/src/qpid/framing/MethodContent.h
@@ -22,7 +22,7 @@
#define _MethodContent_
#include <string>
-#include "AMQHeaderBody.h"
+#include "qpid/framing/AMQHeaderBody.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/ModelMethod.h b/cpp/src/qpid/framing/ModelMethod.h
index 8e4361e761..d99bd06cfa 100644
--- a/cpp/src/qpid/framing/ModelMethod.h
+++ b/cpp/src/qpid/framing/ModelMethod.h
@@ -21,7 +21,7 @@
* under the License.
*
*/
-#include "AMQMethodBody.h"
+#include "qpid/framing/AMQMethodBody.h"
#include "qpid/framing/Header.h"
namespace qpid {
@@ -35,7 +35,7 @@ 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.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; }
diff --git a/cpp/src/qpid/framing/OutputHandler.h b/cpp/src/qpid/framing/OutputHandler.h
index 6f4b27fb72..88c95589da 100644
--- a/cpp/src/qpid/framing/OutputHandler.h
+++ b/cpp/src/qpid/framing/OutputHandler.h
@@ -22,7 +22,7 @@
*
*/
#include <boost/noncopyable.hpp>
-#include "FrameHandler.h"
+#include "qpid/framing/FrameHandler.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/ProtocolInitiation.cpp b/cpp/src/qpid/framing/ProtocolInitiation.cpp
index 50617de017..e617015d64 100644
--- a/cpp/src/qpid/framing/ProtocolInitiation.cpp
+++ b/cpp/src/qpid/framing/ProtocolInitiation.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "ProtocolInitiation.h"
+#include "qpid/framing/ProtocolInitiation.h"
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/ProtocolInitiation.h b/cpp/src/qpid/framing/ProtocolInitiation.h
index 43e32da4cf..c519bc2442 100644
--- a/cpp/src/qpid/framing/ProtocolInitiation.h
+++ b/cpp/src/qpid/framing/ProtocolInitiation.h
@@ -18,10 +18,11 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "Buffer.h"
-#include "AMQDataBlock.h"
-#include "ProtocolVersion.h"
+#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_
@@ -35,20 +36,20 @@ private:
ProtocolVersion version;
public:
- ProtocolInitiation();
- ProtocolInitiation(uint8_t major, uint8_t minor);
- ProtocolInitiation(ProtocolVersion p);
- virtual ~ProtocolInitiation();
- virtual void encode(Buffer& buffer) const;
- virtual bool decode(Buffer& buffer);
- inline virtual uint32_t size() const { return 8; }
+ 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(); }
};
-std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi);
+QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi);
}
diff --git a/cpp/src/qpid/framing/ProtocolVersion.cpp b/cpp/src/qpid/framing/ProtocolVersion.cpp
index 7a96bfa925..c63cddb4cc 100644
--- a/cpp/src/qpid/framing/ProtocolVersion.cpp
+++ b/cpp/src/qpid/framing/ProtocolVersion.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "ProtocolVersion.h"
+#include "qpid/framing/ProtocolVersion.h"
#include <sstream>
using namespace qpid::framing;
diff --git a/cpp/src/qpid/framing/ProtocolVersion.h b/cpp/src/qpid/framing/ProtocolVersion.h
deleted file mode 100644
index 9a7ebec491..0000000000
--- a/cpp/src/qpid/framing/ProtocolVersion.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _ProtocolVersion_
-#define _ProtocolVersion_
-
-#include "amqp_types.h"
-
-namespace qpid
-{
-namespace framing
-{
-
-class ProtocolVersion
-{
-private:
- uint8_t major_;
- uint8_t minor_;
-
-public:
- explicit ProtocolVersion(uint8_t _major=0, uint8_t _minor=0)
- : major_(_major), minor_(_minor) {}
-
- uint8_t getMajor() const { return major_; }
- void setMajor(uint8_t major) { major_ = major; }
- uint8_t getMinor() const { return minor_; }
- void setMinor(uint8_t minor) { minor_ = minor; }
- const std::string toString() const;
-
- ProtocolVersion& operator=(ProtocolVersion p);
-
- bool operator==(ProtocolVersion p) const;
- bool operator!=(ProtocolVersion p) const { return ! (*this == p); }
-};
-
-} // namespace framing
-} // namespace qpid
-
-
-#endif // ifndef _ProtocolVersion_
diff --git a/cpp/src/qpid/framing/Proxy.cpp b/cpp/src/qpid/framing/Proxy.cpp
index 6b37fb368d..452fb13b01 100644
--- a/cpp/src/qpid/framing/Proxy.cpp
+++ b/cpp/src/qpid/framing/Proxy.cpp
@@ -16,17 +16,23 @@
*
*/
-#include "Proxy.h"
-#include "AMQFrame.h"
+#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) {}
+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);
}
@@ -39,4 +45,7 @@ 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/cpp/src/qpid/framing/Proxy.h b/cpp/src/qpid/framing/Proxy.h
index 3dc082097a..0884e9cbd2 100644
--- a/cpp/src/qpid/framing/Proxy.h
+++ b/cpp/src/qpid/framing/Proxy.h
@@ -19,8 +19,10 @@
*
*/
-#include "FrameHandler.h"
-#include "ProtocolVersion.h"
+#include "qpid/framing/FrameHandler.h"
+#include "qpid/framing/ProtocolVersion.h"
+
+#include "qpid/CommonImportExport.h"
namespace qpid {
namespace framing {
@@ -33,18 +35,26 @@ class AMQBody;
class Proxy
{
public:
- Proxy(FrameHandler& h);
- virtual ~Proxy();
+ class ScopedSync
+ {
+ Proxy& proxy;
+ public:
+ QPID_COMMON_EXTERN ScopedSync(Proxy& p);
+ QPID_COMMON_EXTERN ~ScopedSync();
+ };
- void send(const AMQBody&);
+ QPID_COMMON_EXTERN Proxy(FrameHandler& h);
+ QPID_COMMON_EXTERN virtual ~Proxy();
- ProtocolVersion getVersion() const;
+ QPID_COMMON_EXTERN void send(const AMQBody&);
- FrameHandler& getHandler();
- void setHandler(FrameHandler&);
+ 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
diff --git a/cpp/src/qpid/framing/SendContent.cpp b/cpp/src/qpid/framing/SendContent.cpp
index a62e4eeb72..a26c19037b 100644
--- a/cpp/src/qpid/framing/SendContent.cpp
+++ b/cpp/src/qpid/framing/SendContent.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "SendContent.h"
+#include "qpid/framing/SendContent.h"
qpid::framing::SendContent::SendContent(FrameHandler& h, uint16_t mfs, uint efc) : handler(h),
maxFrameSize(mfs),
@@ -34,13 +34,13 @@ void qpid::framing::SendContent::operator()(const AMQFrame& f)
real frame size, hence substract -1 from frameOverhead()*/
uint16_t maxContentSize = maxFrameSize - (AMQFrame::frameOverhead() - 1);
const AMQContentBody* body(f.castBody<AMQContentBody>());
- if (body->size() > maxContentSize) {
+ if (body->encodedSize() > maxContentSize) {
uint32_t offset = 0;
- for (int chunk = body->size() / maxContentSize; chunk > 0; chunk--) {
- sendFragment(*body, offset, maxContentSize, first && offset == 0, last && offset + maxContentSize == body->size());
+ 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->size() % maxContentSize;
+ uint32_t remainder = body->encodedSize() % maxContentSize;
if (remainder) {
sendFragment(*body, offset, remainder, first && offset == 0, last);
}
@@ -53,8 +53,7 @@ void qpid::framing::SendContent::operator()(const AMQFrame& f)
void qpid::framing::SendContent::sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const
{
- AMQFrame fragment(in_place<AMQContentBody>(
- body.getData().substr(offset, size)));
+ AMQFrame fragment((AMQContentBody(body.getData().substr(offset, size))));
setFlags(fragment, first, last);
handler.handle(fragment);
}
diff --git a/cpp/src/qpid/framing/SendContent.h b/cpp/src/qpid/framing/SendContent.h
index dcd5202b3e..745c948c9e 100644
--- a/cpp/src/qpid/framing/SendContent.h
+++ b/cpp/src/qpid/framing/SendContent.h
@@ -22,6 +22,7 @@
#include "qpid/framing/amqp_framing.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/FrameHandler.h"
+#include "qpid/CommonImportExport.h"
#ifndef _SendContent_
#define _SendContent_
@@ -44,8 +45,8 @@ class SendContent
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:
- SendContent(FrameHandler& _handler, uint16_t _maxFrameSize, uint frameCount);
- void operator()(const AMQFrame& f);
+ QPID_COMMON_EXTERN SendContent(FrameHandler& _handler, uint16_t _maxFrameSize, uint frameCount);
+ QPID_COMMON_EXTERN void operator()(const AMQFrame& f);
};
}
diff --git a/cpp/src/qpid/framing/SequenceNumber.cpp b/cpp/src/qpid/framing/SequenceNumber.cpp
index 7caaf5440b..41cb236629 100644
--- a/cpp/src/qpid/framing/SequenceNumber.cpp
+++ b/cpp/src/qpid/framing/SequenceNumber.cpp
@@ -19,67 +19,13 @@
*
*/
-#include "SequenceNumber.h"
-#include "Buffer.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/Buffer.h"
#include <ostream>
using qpid::framing::SequenceNumber;
using qpid::framing::Buffer;
-SequenceNumber::SequenceNumber() : value(0) {}
-
-SequenceNumber::SequenceNumber(uint32_t v) : value((int32_t) v) {}
-
-bool SequenceNumber::operator==(const SequenceNumber& other) const
-{
- return value == other.value;
-}
-
-bool SequenceNumber::operator!=(const SequenceNumber& other) const
-{
- return !(value == other.value);
-}
-
-
-SequenceNumber& SequenceNumber::operator++()
-{
- value = value + 1;
- return *this;
-}
-
-const SequenceNumber SequenceNumber::operator++(int)
-{
- SequenceNumber old(value);
- value = value + 1;
- return old;
-}
-
-SequenceNumber& SequenceNumber::operator--()
-{
- value = value - 1;
- return *this;
-}
-
-bool SequenceNumber::operator<(const SequenceNumber& other) const
-{
- return (value - other.value) < 0;
-}
-
-bool SequenceNumber::operator>(const SequenceNumber& other) const
-{
- return other < *this;
-}
-
-bool SequenceNumber::operator<=(const SequenceNumber& other) const
-{
- return *this == other || *this < other;
-}
-
-bool SequenceNumber::operator>=(const SequenceNumber& other) const
-{
- return *this == other || *this > other;
-}
-
void SequenceNumber::encode(Buffer& buffer) const
{
buffer.putLong(value);
@@ -90,19 +36,13 @@ void SequenceNumber::decode(Buffer& buffer)
value = buffer.getLong();
}
-uint32_t SequenceNumber::size() const {
+uint32_t SequenceNumber::encodedSize() const {
return 4;
}
namespace qpid {
namespace framing {
-int32_t operator-(const SequenceNumber& a, const SequenceNumber& b)
-{
- int32_t result = a.value - b.value;
- return result;
-}
-
std::ostream& operator<<(std::ostream& o, const SequenceNumber& n) {
return o << n.getValue();
}
diff --git a/cpp/src/qpid/framing/SequenceNumber.h b/cpp/src/qpid/framing/SequenceNumber.h
deleted file mode 100644
index aacd77501b..0000000000
--- a/cpp/src/qpid/framing/SequenceNumber.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _framing_SequenceNumber_h
-#define _framing_SequenceNumber_h
-
-#include "amqp_types.h"
-#include <iosfwd>
-
-namespace qpid {
-namespace framing {
-
-class Buffer;
-
-/**
- * 4-byte sequence number that 'wraps around'.
- */
-class SequenceNumber
-{
- int32_t value;
-
- public:
- SequenceNumber();
- SequenceNumber(uint32_t v);
-
- SequenceNumber& operator++();//prefix ++
- const SequenceNumber operator++(int);//postfix ++
- SequenceNumber& operator--();//prefix ++
- bool operator==(const SequenceNumber& other) const;
- bool operator!=(const SequenceNumber& other) const;
- bool operator<(const SequenceNumber& other) const;
- bool operator>(const SequenceNumber& other) const;
- bool operator<=(const SequenceNumber& other) const;
- bool operator>=(const SequenceNumber& other) const;
- uint32_t getValue() const { return (uint32_t) value; }
- operator uint32_t() const { return (uint32_t) value; }
-
- friend int32_t operator-(const SequenceNumber& a, const SequenceNumber& b);
-
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer);
- uint32_t size() const;
-
- template <class S> void serialize(S& s) { s(value); }
-};
-
-struct Window
-{
- SequenceNumber hwm;
- SequenceNumber lwm;
-};
-
-std::ostream& operator<<(std::ostream& o, const SequenceNumber& n);
-
-}} // namespace qpid::framing
-
-
-#endif
diff --git a/cpp/src/qpid/framing/SequenceNumberSet.cpp b/cpp/src/qpid/framing/SequenceNumberSet.cpp
index afab9033e5..e9d78f3c17 100644
--- a/cpp/src/qpid/framing/SequenceNumberSet.cpp
+++ b/cpp/src/qpid/framing/SequenceNumberSet.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "SequenceNumberSet.h"
+#include "qpid/framing/SequenceNumberSet.h"
using namespace qpid::framing;
@@ -33,6 +33,7 @@ void SequenceNumberSet::encode(Buffer& buffer) const
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()));
diff --git a/cpp/src/qpid/framing/SequenceNumberSet.h b/cpp/src/qpid/framing/SequenceNumberSet.h
index 666307f9d9..c8356c8163 100644
--- a/cpp/src/qpid/framing/SequenceNumberSet.h
+++ b/cpp/src/qpid/framing/SequenceNumberSet.h
@@ -22,11 +22,12 @@
#define _framing_SequenceNumberSet_h
#include <ostream>
-#include "amqp_types.h"
-#include "Buffer.h"
-#include "SequenceNumber.h"
+#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 {
@@ -41,8 +42,8 @@ public:
void encode(Buffer& buffer) const;
void decode(Buffer& buffer);
uint32_t encodedSize() const;
- SequenceNumberSet condense() const;
- void addRange(const SequenceNumber& start, const SequenceNumber& end);
+ 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
@@ -58,7 +59,7 @@ public:
}
}
- friend std::ostream& operator<<(std::ostream&, const SequenceNumberSet&);
+ friend QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const SequenceNumberSet&);
};
diff --git a/cpp/src/qpid/framing/SequenceSet.cpp b/cpp/src/qpid/framing/SequenceSet.cpp
index 9610b9180c..dcfb4689b6 100644
--- a/cpp/src/qpid/framing/SequenceSet.cpp
+++ b/cpp/src/qpid/framing/SequenceSet.cpp
@@ -19,8 +19,8 @@
*
*/
-#include "SequenceSet.h"
-#include "Buffer.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/Buffer.h"
#include "qpid/framing/reply_exceptions.h"
using namespace qpid::framing;
@@ -46,6 +46,7 @@ void SequenceSet::encode(Buffer& buffer) const
void SequenceSet::decode(Buffer& buffer)
{
+ clear();
uint16_t size = buffer.getShort();
uint16_t count = size / RANGE_SIZE;//number of ranges
if (size % RANGE_SIZE)
@@ -56,7 +57,7 @@ void SequenceSet::decode(Buffer& buffer)
}
}
-uint32_t SequenceSet::size() const {
+uint32_t SequenceSet::encodedSize() const {
return 2 /*size field*/ + (rangesSize() * RANGE_SIZE);
}
diff --git a/cpp/src/qpid/framing/SequenceSet.h b/cpp/src/qpid/framing/SequenceSet.h
deleted file mode 100644
index 99e7cb4b21..0000000000
--- a/cpp/src/qpid/framing/SequenceSet.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _framing_SequenceSet_h
-#define _framing_SequenceSet_h
-
-#include "SequenceNumber.h"
-#include "qpid/RangeSet.h"
-
-namespace qpid {
-namespace framing {
-class Buffer;
-
-class SequenceSet : public RangeSet<SequenceNumber> {
- public:
- SequenceSet() {}
- explicit SequenceSet(const RangeSet<SequenceNumber>& r)
- : RangeSet<SequenceNumber>(r) {}
- explicit SequenceSet(const SequenceNumber& s) { add(s); }
- SequenceSet(const SequenceNumber& start, const SequenceNumber finish) { add(start,finish); }
-
-
- void encode(Buffer& buffer) const;
- void decode(Buffer& buffer);
- uint32_t size() const;
-
- bool contains(const SequenceNumber& s) const;
- void add(const SequenceNumber& s);
- void add(const SequenceNumber& start, const SequenceNumber& finish); // Closed range
- void add(const SequenceSet& set);
- void remove(const SequenceNumber& s);
- void remove(const SequenceNumber& start, const SequenceNumber& finish); // Closed range
- void remove(const SequenceSet& set);
-
- template <class T> void for_each(T& t) const {
- for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++)
- t(i->first(), i->last());
- }
-
- template <class T> void for_each(const T& t) const {
- for (RangeIterator i = rangesBegin(); i != rangesEnd(); i++)
- t(i->first(), i->last());
- }
-
- friend std::ostream& operator<<(std::ostream&, const SequenceSet&);
-};
-
-}} // namespace qpid::framing
-
-
-#endif
diff --git a/cpp/src/qpid/framing/StructHelper.h b/cpp/src/qpid/framing/StructHelper.h
deleted file mode 100644
index e3dce4f5ec..0000000000
--- a/cpp/src/qpid/framing/StructHelper.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#ifndef _StructHelper_
-#define _StructHelper_
-
-#include "qpid/Exception.h"
-#include "Buffer.h"
-
-#include <stdlib.h> // For alloca
-
-namespace qpid {
-namespace framing {
-
-class StructHelper
-{
-public:
-
- template <class T> void encode(const T t, std::string& data) {
- uint32_t size = t.bodySize() + 2/*type*/;
- data.resize(size);
- Buffer wbuffer(const_cast<char*>(data.data()), size);
- wbuffer.putShort(T::TYPE);
- t.encodeStructBody(wbuffer);
- }
-
- template <class T> void decode(T& t, const std::string& data) {
- Buffer rbuffer(const_cast<char*>(data.data()), data.length());
- uint16_t type = rbuffer.getShort();
- if (type == T::TYPE) {
- t.decodeStructBody(rbuffer);
- } else {
- throw Exception("Type code does not match");
- }
- }
-};
-
-}}
-#endif
diff --git a/cpp/src/qpid/framing/TransferContent.cpp b/cpp/src/qpid/framing/TransferContent.cpp
index 42af2027eb..837d7d346a 100644
--- a/cpp/src/qpid/framing/TransferContent.cpp
+++ b/cpp/src/qpid/framing/TransferContent.cpp
@@ -19,20 +19,17 @@
*
*/
-#include "TransferContent.h"
+#include "qpid/framing/TransferContent.h"
namespace qpid {
namespace framing {
-TransferContent::TransferContent(const std::string& data,
- const std::string& routingKey,
- const std::string& exchange)
-{
+TransferContent::TransferContent(const std::string& data, const std::string& key) {
setData(data);
- if (routingKey.size()) getDeliveryProperties().setRoutingKey(routingKey);
- if (exchange.size()) getDeliveryProperties().setExchange(exchange);
+ if (!key.empty()) getDeliveryProperties().setRoutingKey(key);
}
+
AMQHeaderBody TransferContent::getHeader() const
{
return header;
diff --git a/cpp/src/qpid/framing/TransferContent.h b/cpp/src/qpid/framing/TransferContent.h
index 7630421bd4..5fe1a513a9 100644
--- a/cpp/src/qpid/framing/TransferContent.h
+++ b/cpp/src/qpid/framing/TransferContent.h
@@ -21,11 +21,12 @@
#ifndef _TransferContent_
#define _TransferContent_
-#include "FrameSet.h"
-#include "MethodContent.h"
+#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 {
@@ -36,29 +37,27 @@ class TransferContent : public MethodContent
AMQHeaderBody header;
std::string data;
public:
- TransferContent(const std::string& data = std::string(),
- const std::string& routingKey = std::string(),
- const std::string& exchange = std::string());
+ QPID_COMMON_EXTERN TransferContent(const std::string& data = std::string(), const std::string& key=std::string());
///@internal
- AMQHeaderBody getHeader() const;
+ QPID_COMMON_EXTERN AMQHeaderBody getHeader() const;
- void setData(const std::string&);
- const std::string& getData() const;
- std::string& getData();
+ QPID_COMMON_EXTERN void setData(const std::string&);
+ QPID_COMMON_EXTERN const std::string& getData() const;
+ QPID_COMMON_EXTERN std::string& getData();
- void appendData(const std::string&);
+ QPID_COMMON_EXTERN void appendData(const std::string&);
- bool hasMessageProperties() const;
- MessageProperties& getMessageProperties();
- const MessageProperties& getMessageProperties() const;
+ QPID_COMMON_EXTERN bool hasMessageProperties() const;
+ QPID_COMMON_EXTERN MessageProperties& getMessageProperties();
+ QPID_COMMON_EXTERN const MessageProperties& getMessageProperties() const;
- bool hasDeliveryProperties() const;
- DeliveryProperties& getDeliveryProperties();
- const DeliveryProperties& getDeliveryProperties() const;
+ QPID_COMMON_EXTERN bool hasDeliveryProperties() const;
+ QPID_COMMON_EXTERN DeliveryProperties& getDeliveryProperties();
+ QPID_COMMON_EXTERN const DeliveryProperties& getDeliveryProperties() const;
///@internal
- void populate(const FrameSet& frameset);
+ QPID_COMMON_EXTERN void populate(const FrameSet& frameset);
};
}}
diff --git a/cpp/src/qpid/framing/Uuid.cpp b/cpp/src/qpid/framing/Uuid.cpp
index e8d8d517dd..432c7ab94e 100644
--- a/cpp/src/qpid/framing/Uuid.cpp
+++ b/cpp/src/qpid/framing/Uuid.cpp
@@ -16,7 +16,9 @@
*
*/
-#include "Uuid.h"
+#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"
@@ -28,6 +30,37 @@ 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());
}
@@ -52,7 +85,7 @@ istream& operator>>(istream& in, Uuid& uuid) {
return in;
}
-std::string Uuid::str() {
+std::string Uuid::str() const {
std::ostringstream os;
os << *this;
return os.str();
diff --git a/cpp/src/qpid/framing/Uuid.h b/cpp/src/qpid/framing/Uuid.h
deleted file mode 100644
index e3e309a56c..0000000000
--- a/cpp/src/qpid/framing/Uuid.h
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef QPID_FRAMING_UUID_H
-#define QPID_FRAMING_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.
- *
- */
-
-#include <boost/array.hpp>
-
-#include <ostream>
-#include <istream>
-
-#include <uuid/uuid.h>
-
-namespace qpid {
-namespace framing {
-
-class Buffer;
-
-/**
- * A UUID is represented as a boost::array of 16 bytes.
- *
- * Full value semantics, operators ==, < etc. are provided by
- * boost::array so Uuid can be the key type in a map etc.
- */
-struct Uuid : public boost::array<uint8_t, 16> {
- /** If unique is true, generate a unique ID else a null ID. */
- Uuid(bool unique=false) { if (unique) generate(); else clear(); }
-
- /** Copy from 16 bytes of data. */
- Uuid(uint8_t* data) { assign(data); }
-
- /** Copy from 16 bytes of data. */
- void assign(uint8_t* data) {
- uuid_copy(c_array(), data);
- }
-
- /** Set to a new unique identifier. */
- void generate() { uuid_generate(c_array()); }
-
- /** Set to all zeros. */
- void clear() { uuid_clear(c_array()); }
-
- /** Test for null (all zeros). */
- bool isNull() {
- return uuid_is_null(data());
- }
-
- // Default op= and copy ctor are fine.
- // boost::array gives us ==, < etc.
-
- void encode(framing::Buffer& buf) const;
-
- void decode(framing::Buffer& buf);
-
- /** String value in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */
- std::string str();
-
- template <class S> void serialize(S& s) {
- s.raw(begin(), size());
- }
-};
-
-/** Print in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */
-std::ostream& operator<<(std::ostream&, Uuid);
-
-/** Read from format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */
-std::istream& operator>>(std::istream&, Uuid&);
-
-}} // namespace qpid::framing
-
-
-
-#endif /*!QPID_FRAMING_UUID_H*/
diff --git a/cpp/src/qpid/framing/amqp_framing.h b/cpp/src/qpid/framing/amqp_framing.h
index 4e4747c3f4..3a8b39afb5 100644
--- a/cpp/src/qpid/framing/amqp_framing.h
+++ b/cpp/src/qpid/framing/amqp_framing.h
@@ -18,15 +18,15 @@
* under the License.
*
*/
-#include "amqp_types.h"
-#include "AMQFrame.h"
-#include "AMQBody.h"
-#include "BodyHandler.h"
-#include "AMQMethodBody.h"
-#include "AMQHeaderBody.h"
-#include "AMQContentBody.h"
-#include "AMQHeartbeatBody.h"
-#include "InputHandler.h"
-#include "OutputHandler.h"
-#include "ProtocolInitiation.h"
-#include "ProtocolVersion.h"
+#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/cpp/src/qpid/framing/amqp_types.h b/cpp/src/qpid/framing/amqp_types.h
deleted file mode 100644
index 8b10aa3069..0000000000
--- a/cpp/src/qpid/framing/amqp_types.h
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef AMQP_TYPES_H
-#define AMQP_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.
- *
- */
-
-/** \file
- * Definitions and forward declarations of all types used
- * in AMQP messages.
- */
-
-#include <string>
-#ifdef _WINDOWS
-#include "windows.h"
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef unsigned __int64 uint64_t;
-#endif
-#ifndef _WINDOWS
-#include <stdint.h>
-#endif
-
-namespace qpid {
-namespace framing {
-
-using std::string;
-typedef uint8_t FrameType;
-typedef uint16_t ChannelId;
-typedef uint32_t BatchOffset;
-typedef uint8_t ClassId;
-typedef uint8_t MethodId;
-typedef uint16_t ReplyCode;
-
-// Types represented by classes.
-class Content;
-class FieldTable;
-class SequenceNumberSet;
-class Uuid;
-
-// Useful constants
-
-/** Maximum channel ID used by broker. Reserve high bit for internal use.*/
-const ChannelId CHANNEL_MAX=(ChannelId(~1))>>1;
-const ChannelId CHANNEL_HIGH_BIT= ChannelId(~CHANNEL_MAX);
-
-// Forward declare class types
-class FramingContent;
-class FieldTable;
-class SequenceNumberSet;
-class SequenceSet;
-class Uuid;
-
-}} // namespace qpid::framing
-#endif
diff --git a/cpp/src/qpid/framing/frame_functors.h b/cpp/src/qpid/framing/frame_functors.h
index 8ae3d15b4f..d2064d6a57 100644
--- a/cpp/src/qpid/framing/frame_functors.h
+++ b/cpp/src/qpid/framing/frame_functors.h
@@ -36,7 +36,7 @@ class SumFrameSize
uint64_t size;
public:
SumFrameSize() : size(0) {}
- void operator()(const AMQFrame& f) { size += f.size(); }
+ void operator()(const AMQFrame& f) { size += f.encodedSize(); }
uint64_t getSize() { return size; }
};
@@ -45,7 +45,7 @@ class SumBodySize
uint64_t size;
public:
SumBodySize() : size(0) {}
- void operator()(const AMQFrame& f) { size += f.getBody()->size(); }
+ void operator()(const AMQFrame& f) { size += f.getBody()->encodedSize(); }
uint64_t getSize() { return size; }
};
diff --git a/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp
index 54df54bb94..939e2502cc 100644
--- a/cpp/src/qpid/log/Logger.cpp
+++ b/cpp/src/qpid/log/Logger.cpp
@@ -16,20 +16,19 @@
*
*/
-#include "Logger.h"
-#include "Options.h"
+#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 <boost/pool/detail/singleton.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
-#include <boost/scoped_ptr.hpp>
#include <algorithm>
#include <sstream>
-#include <fstream>
#include <iomanip>
#include <stdexcept>
-#include <syslog.h>
#include <time.h>
@@ -44,45 +43,6 @@ inline void Logger::enable_unlocked(Statement* s) {
s->enabled=selector.isEnabled(s->level, s->function);
}
-struct OstreamOutput : public Logger::Output {
- OstreamOutput(std::ostream& o) : out(&o) {}
-
- OstreamOutput(const 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 log(const Statement&, const std::string& m) {
- *out << m << flush;
- }
-
- ostream* out;
- boost::scoped_ptr<ostream> mine;
-};
-
-struct SyslogOutput : public Logger::Output {
- SyslogOutput(const Options& opts)
- : name(opts.syslogName), facility(opts.syslogFacility.value)
- {
- ::openlog(name.c_str(), LOG_PID, facility);
- }
-
- ~SyslogOutput() {
- ::closelog();
- }
-
- void log(const Statement& s, const std::string& m)
- {
- syslog(LevelTraits::priority(s.level), "%s", m.c_str());
- }
-
- std::string name;
- int facility;
-};
-
Logger& Logger::instance() {
return boost::details::pool::singleton_default<Logger>::instance();
}
@@ -114,25 +74,7 @@ void Logger::log(const Statement& s, const std::string& msg) {
if (!prefix.empty())
os << prefix << ": ";
if (flags&TIME)
- {
- const char * month_abbrevs[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
- time_t rawtime;
- struct tm * timeinfo;
-
- time ( & rawtime );
- timeinfo = localtime ( &rawtime );
- char time_string[100];
- sprintf ( time_string,
- "%d-%s-%02d %02d:%02d:%02d",
- 1900 + timeinfo->tm_year,
- month_abbrevs[timeinfo->tm_mon],
- timeinfo->tm_mday,
- timeinfo->tm_hour,
- timeinfo->tm_min,
- timeinfo->tm_sec
- );
- os << time_string << " ";
- }
+ qpid::sys::outputFormattedNow(os);
if (flags&LEVEL)
os << LevelTraits::name(s.level) << " ";
if (flags&THREAD)
@@ -159,25 +101,6 @@ void Logger::output(std::auto_ptr<Output> out) {
outputs.push_back(out.release());
}
-void Logger::output(std::ostream& out) {
- output(make_auto_ptr<Output>(new OstreamOutput(out)));
-}
-
-void Logger::syslog(const Options& opts) {
- output(make_auto_ptr<Output>(new SyslogOutput(opts)));
-}
-
-void Logger::output(const std::string& name, const Options& opts) {
- if (name=="stderr")
- output(clog);
- else if (name=="stdout")
- output(cout);
- else if (name=="syslog")
- syslog(opts);
- else
- output(make_auto_ptr<Output>(new OstreamOutput(name)));
-}
-
void Logger::clear() {
select(Selector()); // locked
format(0); // locked
@@ -212,16 +135,15 @@ void Logger::add(Statement& 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));
- void (Logger::* outputFn)(const std::string&, const Options&) = &Logger::output;
- for_each(o.outputs.begin(), o.outputs.end(),
- boost::bind(outputFn, this, _1, boost::cref(o)));
setPrefix(opts.prefix);
+ options.sinkOptions->setup(this);
}
void Logger::setPrefix(const std::string& p) { prefix = p; }
diff --git a/cpp/src/qpid/log/Logger.h b/cpp/src/qpid/log/Logger.h
deleted file mode 100644
index fa38678bba..0000000000
--- a/cpp/src/qpid/log/Logger.h
+++ /dev/null
@@ -1,117 +0,0 @@
-#ifndef LOGGER_H
-#define LOGGER_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 "Selector.h"
-#include "qpid/sys/Mutex.h"
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/noncopyable.hpp>
-#include <set>
-
-namespace qpid {
-namespace log {
-
-class Options;
-
-/**
- * Central logging agent.
- *
- * Thread safe, singleton.
- */
-class Logger : private boost::noncopyable {
- public:
- /** Flags indicating what to include in the log output */
- enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32};
-
- /** Interface for log output destination.
- *
- * Implementations must be thread safe.
- */
- class Output {
- public:
- Output();
- virtual ~Output();
- /** Receives the statemnt of origin and formatted message to log. */
- virtual void log(const Statement&, const std::string&) =0;
- };
-
- static Logger& instance();
-
- Logger();
- ~Logger();
-
- /** Select the messages to be logged. */
- void select(const Selector& s);
-
- /** Set the formatting flags, bitwise OR of FormatFlag values. */
- void format(int formatFlags);
-
- /** Set format flags from options object.
- *@returns computed flags.
- */
- int format(const Options&);
-
- /** Configure logger from Options */
- void configure(const Options& o);
-
- /** Add a statement. */
- void add(Statement& s);
-
- /** Log a message. */
- void log(const Statement&, const std::string&);
-
- /** Add an ostream to outputs.
- *
- * The ostream must not be destroyed while the Logger might
- * still be using it. This is the case for std streams cout,
- * cerr, clog.
- */
- void output(std::ostream&);
-
- /** Add syslog to outputs. */
- void syslog(const Options&);
-
- /** Add an output.
- *@param name a file name or one of the special tokens:
- *stdout, stderr, syslog.
- */
- void output(const std::string& name, const Options&);
-
- /** Add an output destination for messages */
- void output(std::auto_ptr<Output> out);
-
- /** Set a prefix for all messages */
- void setPrefix(const std::string& prefix);
-
- /** Reset the logger to it's original state. */
- void clear();
-
-
- private:
- typedef boost::ptr_vector<Output> Outputs;
- typedef std::set<Statement*> Statements;
-
- sys::Mutex lock;
- inline void enable_unlocked(Statement* s);
-
- Statements statements;
- Outputs outputs;
- Selector selector;
- int flags;
- std::string prefix;
-};
-
-}} // namespace qpid::log
-
-
-#endif /*!LOGGER_H*/
diff --git a/cpp/src/qpid/log/Options.cpp b/cpp/src/qpid/log/Options.cpp
index e28f82648e..24ef413cbc 100644
--- a/cpp/src/qpid/log/Options.cpp
+++ b/cpp/src/qpid/log/Options.cpp
@@ -16,116 +16,39 @@
*
*/
-#include "Options.h"
-#include "Statement.h"
+#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>
-#include <syslog.h>
namespace qpid {
namespace log {
using namespace std;
-namespace {
-
-class SyslogFacilities {
- public:
- typedef map<string, int> ByName;
- typedef map<int, string> ByValue;
-
- SyslogFacilities() {
- struct NameValue { const char* name; int value; };
- NameValue nameValue[] = {
- { "AUTH", LOG_AUTH },
- { "AUTHPRIV", LOG_AUTHPRIV },
- { "CRON", LOG_CRON },
- { "DAEMON", LOG_DAEMON },
- { "FTP", LOG_FTP },
- { "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);
- 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;
-};
-
-}
-
-ostream& operator<<(ostream& o, const SyslogFacility& f) {
- return o << SyslogFacilities().name(f.value);
-}
-
-istream& operator>>(istream& i, SyslogFacility& f) {
- std::string name;
- i >> name;
- f.value = SyslogFacilities().value(name);
- return i;
-}
-
-namespace {
-std::string basename(const std::string path) {
- size_t i = path.find_last_of('/');
- return path.substr((i == std::string::npos) ? 0 : i+1);
-}
-}
-
-Options::Options(const std::string& argv0, const std::string& name) :
- qpid::Options(name),
- time(true), level(true), thread(false), source(false), function(false), trace(false),
- syslogName(basename(argv0)), syslogFacility(LOG_DAEMON)
+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),
+ trace(false),
+ sinkOptions (SinkOptions::create(argv0_))
{
- outputs.push_back("stderr");
- selectors.push_back("error+");
+ 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()
- ("log-output", optValue(outputs, "FILE"), "Send log output to FILE. "
- "FILE can be a file name or one of the special values:\n"
- "stderr, stdout, syslog")
("trace,t", optValue(trace), "Enables all logging" )
("log-enable", optValue(selectors, "RULE"),
("Enables logging for selected levels and components. "
@@ -143,9 +66,42 @@ Options::Options(const std::string& argv0, const std::string& name) :
("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-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages")
- ("syslog-name", optValue(syslogName, "NAME"), "Name to use in syslog messages")
- ("syslog-facility", optValue(syslogFacility,"LOG_XXX"), "Facility to use in syslog 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),
+ 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;
+ trace = x.trace;
+ prefix = x.prefix;
+ *sinkOptions = *x.sinkOptions;
+ }
+ return *this;
+}
}} // namespace qpid::log
diff --git a/cpp/src/qpid/log/Options.h b/cpp/src/qpid/log/Options.h
deleted file mode 100644
index 13fca38f9d..0000000000
--- a/cpp/src/qpid/log/Options.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef OPTIONS_H
-#define OPTIONS_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/Options.h"
-#include <iosfwd>
-
-namespace qpid {
-namespace log {
-
-/** Provides << and >> operators to convert syslog facility values to/from strings. */
-struct SyslogFacility {
- int value;
- SyslogFacility(int i=0) : value(i) {}
-};
-
-std::ostream& operator<<(std::ostream&, const SyslogFacility&);
-std::istream& operator>>(std::istream&, SyslogFacility&);
-
-/** Logging options for config parser. */
-struct Options : public qpid::Options {
- /** Pass argv[0] for use in syslog output */
- Options(const std::string& argv0,
- const std::string& name="Logging options");
-
- std::vector<std::string> selectors;
- std::vector<std::string> outputs;
- bool time, level, thread, source, function;
- bool trace;
- std::string syslogName;
- SyslogFacility syslogFacility;
- std::string prefix;
-};
-
-
-}} // namespace qpid::log
-
-
-
-#endif /*!OPTIONS_H*/
diff --git a/cpp/src/qpid/framing/amqp_types_full.h b/cpp/src/qpid/log/OstreamOutput.cpp
index d0aaf28cb4..9b6ec1f8aa 100644
--- a/cpp/src/qpid/framing/amqp_types_full.h
+++ b/cpp/src/qpid/log/OstreamOutput.cpp
@@ -1,6 +1,3 @@
-#ifndef _framing_amqp_types_decl_h
-#define _framing_amqp_types_decl_h
-
/*
*
* Copyright (c) 2006 The Apache Software Foundation
@@ -19,20 +16,26 @@
*
*/
-/** \file
- * Definitions and full declarations of all types used
- * in AMQP messages.
- *
- * It's better to include amqp_types.h in another header instead of this file
- * unless the header actually needs the full declarations. Including
- * full declarations when forward declarations would increase compile
- * times.
- */
+#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);
+}
-#include "amqp_types.h"
-#include "Array.h"
-#include "FieldTable.h"
-#include "SequenceSet.h"
-#include "Uuid.h"
+void OstreamOutput::log(const Statement&, const std::string& m) {
+ *out << m << flush;
+}
-#endif /*!_framing_amqp_types_decl_h*/
+}} // namespace qpid::log
diff --git a/cpp/src/qpid/log/OstreamOutput.h b/cpp/src/qpid/log/OstreamOutput.h
new file mode 100644
index 0000000000..12fd4ce425
--- /dev/null
+++ b/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/cpp/src/qpid/log/Selector.cpp b/cpp/src/qpid/log/Selector.cpp
index 994421d0ff..a4bc580470 100644
--- a/cpp/src/qpid/log/Selector.cpp
+++ b/cpp/src/qpid/log/Selector.cpp
@@ -16,10 +16,11 @@
*
*/
-#include "Selector.h"
-#include "Options.h"
+#include "qpid/log/Selector.h"
+#include "qpid/log/Options.h"
#include <boost/bind.hpp>
#include <algorithm>
+#include <string.h>
namespace qpid {
namespace log {
@@ -52,12 +53,13 @@ Selector::Selector(const Options& opt){
boost::bind(&Selector::enable, this, _1));
}
-bool Selector::isEnabled(Level level, const std::string& function) {
+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 (function.find(*i) != std::string::npos)
+ if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd)
return true;
}
return false;
diff --git a/cpp/src/qpid/log/Selector.h b/cpp/src/qpid/log/Selector.h
deleted file mode 100644
index 7c98bc6f8f..0000000000
--- a/cpp/src/qpid/log/Selector.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef SELECTOR_H
-#define SELECTOR_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 "Statement.h"
-#include <vector>
-
-namespace qpid {
-namespace log {
-class Options;
-
-/**
- * A selector identifies the set of log messages to enable.
- *
- * Thread object unsafe, pass-by-value type.
- */
-class Selector {
- public:
- /** Empty selector selects nothing */
- Selector() {}
-
- /** Set selector from Options */
- Selector(const Options&);
-
- /** Equavlient to: Selector s; s.enable(l, s) */
- Selector(Level l, const std::string& s=std::string()) {
- enable(l,s);
- }
-
- Selector(const std::string& enableStr) { enable(enableStr); }
- /**
- * Enable messages with level in levels where the file
- * name contains substring. Empty string matches all.
- */
- void enable(Level level, const std::string& substring=std::string()) {
- substrings[level].push_back(substring);
- }
-
- /** Enable based on a 'level[+]:file' string */
- void enable(const std::string& enableStr);
-
- /** True if level is enabld for file. */
- bool isEnabled(Level level, const std::string& function);
-
- private:
- std::vector<std::string> substrings[LevelTraits::COUNT];
-};
-
-
-}} // namespace qpid::log
-
-
-#endif /*!SELECTOR_H*/
diff --git a/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp
index c2b286f1e7..6a32b50096 100644
--- a/cpp/src/qpid/log/Statement.cpp
+++ b/cpp/src/qpid/log/Statement.cpp
@@ -16,12 +16,11 @@
*
*/
-#include "Statement.h"
-#include "Logger.h"
+#include "qpid/log/Statement.h"
+#include "qpid/log/Logger.h"
#include <boost/bind.hpp>
#include <stdexcept>
#include <algorithm>
-#include <syslog.h>
#include <ctype.h>
namespace qpid {
@@ -67,11 +66,6 @@ const char* names[LevelTraits::COUNT] = {
"trace", "debug", "info", "notice", "warning", "error", "critical"
};
-int priorities[LevelTraits::COUNT] = {
- LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE,
- LOG_WARNING, LOG_ERR, LOG_CRIT
-};
-
} // namespace
Level LevelTraits::level(const char* name) {
@@ -86,8 +80,4 @@ const char* LevelTraits::name(Level l) {
return names[l];
}
-int LevelTraits::priority(Level l) {
- return priorities[l];
-}
-
}} // namespace qpid::log
diff --git a/cpp/src/qpid/log/Statement.h b/cpp/src/qpid/log/Statement.h
deleted file mode 100644
index f765df1cf4..0000000000
--- a/cpp/src/qpid/log/Statement.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#ifndef STATEMENT_H
-#define STATEMENT_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/Msg.h"
-
-#include <boost/current_function.hpp>
-
-namespace qpid {
-namespace log {
-
-/** Debugging severity levels
- * - trace: High-volume debugging messages.
- * - debug: Debugging messages.
- * - info: Informational messages.
- * - notice: Normal but significant condition.
- * - warning: Warn of a possible problem.
- * - error: A definite error has occured.
- * - critical: System in danger of severe failure.
- */
-enum Level { trace, debug, info, notice, warning, error, critical };
-struct LevelTraits {
- static const int COUNT=critical+1;
-
- /** Get level from string name.
- *@exception if name invalid.
- */
- static Level level(const char* name);
-
- /** Get level from string name.
- *@exception if name invalid.
- */
- static Level level(const std::string& name) {
- return level(name.c_str());
- }
-
- /** String name of level */
- static const char* name(Level);
-
- /** Syslog priority of level */
- static int priority(Level);
-};
-
-/** POD struct representing a logging statement in source code. */
-struct Statement {
- bool enabled;
- const char* file;
- int line;
- const char* function;
- Level level;
-
- void log(const std::string& message);
-
- struct Initializer {
- Initializer(Statement& s);
- Statement& statement;
- };
-};
-
-///@internal static initializer for a Statement.
-#define QPID_LOG_STATEMENT_INIT(level) \
- { 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::level) }
-
-/**
- * Like QPID_LOG but computes an additional boolean test expression
- * to determine if the message should be logged. Evaluation of both
- * the test and message expressions occurs only if the requested log level
- * is enabled.
- *@param LEVEL severity Level for message, should be one of:
- * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix.
- *@param TEST message is logged only if expression TEST evaluates to true.
- *@param MESSAGE any object with an @eostream operator<<, or a sequence
- * like of ostreamable objects separated by @e<<.
- */
-#define QPID_LOG_IF(LEVEL, TEST, MESSAGE) \
- do { \
- using ::qpid::log::Statement; \
- static Statement stmt_= QPID_LOG_STATEMENT_INIT(LEVEL); \
- static Statement::Initializer init_(stmt_); \
- if (stmt_.enabled && (TEST)) \
- stmt_.log(::qpid::Msg() << MESSAGE); \
- } while(0)
-
-/**
- * Macro for log statements. Example of use:
- * @code
- * QPID_LOG(debug, "There are " << foocount << " foos in the bar.");
- * QPID_LOG(error, boost::format("Dohickey %s exploded") % dohicky.name());
- * @endcode
- *
- * All code with logging statements should be built with
- * -DQPID_COMPONENT=<component name>
- * where component name is the name of the component this file belongs to.
- *
- * You can subscribe to log messages by level, by component, by filename
- * or a combination @see Configuration.
-
- *@param LEVEL severity Level for message, should be one of:
- * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix.
- *@param MESSAGE any object with an @eostream operator<<, or a sequence
- * like of ostreamable objects separated by @e<<.
- */
-#define QPID_LOG(LEVEL, MESSAGE) QPID_LOG_IF(LEVEL, true, MESSAGE);
-
-}} // namespace qpid::log
-
-
-
-
-#endif /*!STATEMENT_H*/
-
diff --git a/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp
new file mode 100644
index 0000000000..292e9147f6
--- /dev/null
+++ b/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/cpp/src/qpid/log/posix/SinkOptions.h b/cpp/src/qpid/log/posix/SinkOptions.h
new file mode 100644
index 0000000000..d929c29025
--- /dev/null
+++ b/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/cpp/src/qpid/log/windows/SinkOptions.cpp b/cpp/src/qpid/log/windows/SinkOptions.cpp
new file mode 100644
index 0000000000..28f4b267e0
--- /dev/null
+++ b/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/cpp/src/qpid/log/windows/SinkOptions.h b/cpp/src/qpid/log/windows/SinkOptions.h
new file mode 100644
index 0000000000..605822fd46
--- /dev/null
+++ b/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 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/cpp/src/qpid/management/Args.h b/cpp/src/qpid/management/Args.h
deleted file mode 100644
index da1fb033b9..0000000000
--- a/cpp/src/qpid/management/Args.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _Args_
-#define _Args_
-
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT 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 management {
-
-class Args
-{
- public:
-
- virtual ~Args (void) = 0;
-
-};
-
-inline Args::~Args (void) {}
-
-class ArgsNone : public Args
-{
-};
-
-}}
-
-
-#endif /*!_Args_*/
diff --git a/cpp/src/qpid/client/MessageQueue.h b/cpp/src/qpid/management/IdAllocator.h
index ab6d351ba7..6f49d6d13f 100644
--- a/cpp/src/qpid/client/MessageQueue.h
+++ b/cpp/src/qpid/management/IdAllocator.h
@@ -1,3 +1,6 @@
+#ifndef QPID_MANAGEMENT_IDALLOCATOR_H
+#define QPID_MANAGEMENT_IDALLOCATOR_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,32 +22,21 @@
*
*/
-#ifndef _MessageQueue_
-#define _MessageQueue_
-#include <iostream>
-#include "qpid/sys/BlockingQueue.h"
-#include "MessageListener.h"
+#include "qpid/management/Manageable.h"
namespace qpid {
-namespace client {
+namespace management {
/**
- * A MessageListener implementation that queues up
- * messages.
- *
- * \ingroup clientapi
+ * Interface through which plugins etc can control the mgmt object id
+ * allocation for special cases
*/
-class MessageQueue : public sys::BlockingQueue<Message>, public MessageListener
+struct IdAllocator
{
- public:
- void received(Message& msg)
- {
- push(msg);
- }
+ virtual uint64_t getIdFor(Manageable* object) = 0;
+ virtual ~IdAllocator() {}
};
-}
-}
-
+}} // namespace qpid::management
-#endif
+#endif /*!QPID_MANAGEMENT_IDALLOCATOR_H*/
diff --git a/cpp/src/qpid/management/Manageable.cpp b/cpp/src/qpid/management/Manageable.cpp
index 0f3fbab55c..a3593e73e3 100644
--- a/cpp/src/qpid/management/Manageable.cpp
+++ b/cpp/src/qpid/management/Manageable.cpp
@@ -17,26 +17,31 @@
// under the License.
//
-#include "Manageable.h"
+#include "qpid/management/Manageable.h"
using namespace qpid::management;
+using std::string;
-std::string Manageable::StatusText (status_t status)
+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_INVALID_PARAMETER : return "InvalidParameter";
+ 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&)
+Manageable::status_t Manageable::ManagementMethod (uint32_t, Args&, std::string&)
{
return STATUS_UNKNOWN_METHOD;
}
diff --git a/cpp/src/qpid/management/Manageable.h b/cpp/src/qpid/management/Manageable.h
deleted file mode 100644
index e2b8980465..0000000000
--- a/cpp/src/qpid/management/Manageable.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef _Manageable_
-#define _Manageable_
-
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-//
-
-#include "ManagementObject.h"
-#include "Args.h"
-#include <string>
-
-namespace qpid {
-namespace management {
-
-class Manageable
-{
- public:
-
- virtual ~Manageable (void) = 0;
-
- // status_t is a type used to pass completion status from the method handler.
- //
- typedef uint32_t status_t;
- static std::string StatusText (status_t status);
-
- static const status_t STATUS_OK = 0;
- static const status_t STATUS_UNKNOWN_OBJECT = 1;
- static const status_t STATUS_UNKNOWN_METHOD = 2;
- static const status_t STATUS_NOT_IMPLEMENTED = 3;
- static const status_t STATUS_INVALID_PARAMETER = 4;
- static const status_t STATUS_FEATURE_NOT_IMPLEMENTED = 5;
-
- // Every "Manageable" object must hold a reference to exactly one
- // management object. This object is always of a class derived from
- // the pure-virtual "ManagementObject".
- //
- // This accessor function returns a pointer to the management object.
- //
- virtual ManagementObject* GetManagementObject (void) const = 0;
-
- // Every "Manageable" object must implement ManagementMethod. This
- // function is called when a remote management client invokes a method
- // on this object. The input and output arguments are specific to the
- // method being called and must be down-cast to the appropriate sub class
- // before use.
- virtual status_t ManagementMethod (uint32_t methodId, Args& args);
-};
-
-inline Manageable::~Manageable (void) {}
-
-}}
-
-#endif /*!_Manageable_*/
diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp
new file mode 100644
index 0000000000..b5ed4ed405
--- /dev/null
+++ b/cpp/src/qpid/management/ManagementAgent.cpp
@@ -0,0 +1,1295 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/ManagementAgent.h"
+#include "qpid/management/ManagementObject.h"
+#include "qpid/management/IdAllocator.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/broker/ConnectionState.h"
+#include "qpid/broker/AclModule.h"
+#include <list>
+#include <iostream>
+#include <fstream>
+
+using boost::intrusive_ptr;
+using qpid::framing::Uuid;
+using namespace qpid::framing;
+using namespace qpid::management;
+using namespace qpid::broker;
+using namespace qpid::sys;
+using namespace std;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
+ManagementAgent::RemoteAgent::~RemoteAgent ()
+{
+ QPID_LOG(trace, "Remote Agent removed bank=[" << brokerBank << "." << agentBank << "]");
+ if (mgmtObject != 0) {
+ mgmtObject->resourceDestroy();
+ agent.deleteObjectNowLH(mgmtObject->getObjectId());
+ }
+}
+
+ManagementAgent::ManagementAgent () :
+ threadPoolSize(1), interval(10), broker(0), startTime(uint64_t(Duration(now())))
+{
+ nextObjectId = 1;
+ brokerBank = 1;
+ bootSequence = 1;
+ nextRemoteBank = 10;
+ nextRequestSequence = 1;
+ clientWasAdded = false;
+}
+
+ManagementAgent::~ManagementAgent ()
+{
+ {
+ 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 implicitely destroyed (long
+ // after this destructor completes). Those exchanges hold references to management
+ // objects that will be invalid.
+ dExchange.reset();
+ mExchange.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;
+ timer = &_broker->getTimer();
+ threadPoolSize = _threads;
+ ManagementObject::maxThreads = threadPoolSize;
+ timer->add (new Periodic(*this, interval));
+
+ // 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();
+ QPID_LOG (debug, "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::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::registerClass (const string& packageName,
+ const string& className,
+ uint8_t* md5Sum,
+ ManagementObject::writeSchemaCall_t schemaCall)
+{
+ 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)
+{
+ Mutex::ScopedLock lock(userLock);
+ PackageMap::iterator pIter = findOrAddPackageLH(packageName);
+ addClassLH(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall);
+}
+
+ObjectId ManagementAgent::addObject(ManagementObject* object,
+ uint64_t persistId,
+ bool publishNow)
+{
+ Mutex::ScopedLock lock (addLock);
+ uint16_t sequence;
+ uint64_t objectNum;
+
+ if (persistId == 0) {
+ sequence = bootSequence;
+ objectNum = nextObjectId++;
+ } else {
+ sequence = 0;
+ objectNum = persistId;
+ }
+
+ ObjectId objId(0 /*flags*/ , sequence, brokerBank, 0, objectNum);
+
+ object->setObjectId(objId);
+ newManagementObjects[objId] = object;
+
+ if (publishNow) {
+#define IMM_BUFSIZE 65536
+ char rawBuf[IMM_BUFSIZE];
+ Buffer msgBuffer(rawBuf, IMM_BUFSIZE);
+
+ encodeHeader(msgBuffer, 'c');
+ object->writeProperties(msgBuffer);
+ uint32_t contentSize = msgBuffer.getPosition();
+ stringstream key;
+ key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName();
+ msgBuffer.reset();
+ sendBuffer(msgBuffer, contentSize, mExchange, key.str());
+ QPID_LOG(trace, "SEND Immediate ContentInd to=" << key.str());
+ }
+
+ return objId;
+}
+
+void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severity)
+{
+ Mutex::ScopedLock lock (userLock);
+ Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+
+ encodeHeader(outBuffer, 'e');
+ outBuffer.putShortString(event.getPackageName());
+ outBuffer.putShortString(event.getEventName());
+ outBuffer.putBin128(event.getMd5Sum());
+ outBuffer.putLongLong(uint64_t(Duration(now())));
+ outBuffer.putOctet(sev);
+ event.encode(outBuffer);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBuffer(outBuffer, outLen, mExchange,
+ "console.event.1.0." + event.getPackageName() + "." + event.getEventName());
+}
+
+ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds)
+ : TimerTask (qpid::sys::Duration ((_seconds ? _seconds : 1) * qpid::sys::TIME_SEC)), agent(_agent) {}
+
+ManagementAgent::Periodic::~Periodic () {}
+
+void ManagementAgent::Periodic::fire ()
+{
+ agent.timer->add (new Periodic (agent, agent.interval));
+ agent.periodicProcessing ();
+}
+
+void ManagementAgent::clientAdded (const std::string& routingKey)
+{
+ if (routingKey.find("console") != 0)
+ return;
+
+ clientWasAdded = true;
+ for (RemoteAgentMap::iterator aIter = remoteAgents.begin();
+ aIter != remoteAgents.end();
+ aIter++) {
+ char localBuffer[16];
+ Buffer outBuffer(localBuffer, 16);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'x');
+ outLen = outBuffer.getPosition();
+ outBuffer.reset();
+ sendBuffer(outBuffer, outLen, dExchange, aIter->second->routingKey);
+ QPID_LOG(trace, "SEND ConsoleAddedIndication to=" << aIter->second->routingKey);
+ }
+}
+
+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';
+}
+
+void ManagementAgent::sendBuffer(Buffer& buf,
+ uint32_t length,
+ qpid::broker::Exchange::shared_ptr exchange,
+ string routingKey)
+{
+ 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);
+ msg->getFrames().append(content);
+
+ DeliverableMessage deliverable (msg);
+ try {
+ exchange->route(deliverable, routingKey, 0);
+ } catch(exception&) {}
+}
+
+void ManagementAgent::moveNewObjectsLH()
+{
+ Mutex::ScopedLock lock (addLock);
+ for (ManagementObjectMap::iterator iter = newManagementObjects.begin ();
+ iter != newManagementObjects.end ();
+ iter++)
+ managementObjects[iter->first] = iter->second;
+ newManagementObjects.clear();
+}
+
+void ManagementAgent::periodicProcessing (void)
+{
+#define BUFSIZE 65536
+#define HEADROOM 4096
+ Mutex::ScopedLock lock (userLock);
+ char msgChars[BUFSIZE];
+ uint32_t contentSize;
+ string routingKey;
+ list<pair<ObjectId, ManagementObject*> > deleteList;
+
+ uint64_t uptime = uint64_t(Duration(now())) - startTime;
+ 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;
+
+ //
+ // Process the entire object map.
+ //
+ for (ManagementObjectMap::iterator baseIter = managementObjects.begin();
+ baseIter != managementObjects.end();
+ baseIter++) {
+ ManagementObject* baseObject = baseIter->second;
+ uint32_t pcount = 0;
+ uint32_t scount = 0;
+
+ //
+ // Skip until we find a base object requiring a sent message.
+ //
+ if (baseObject->getFlags() == 1 ||
+ (!baseObject->getConfigChanged() &&
+ !baseObject->getInstChanged() &&
+ !baseObject->getForcePublish() &&
+ !baseObject->isDeleted()))
+ continue;
+
+ Buffer msgBuffer(msgChars, BUFSIZE);
+ for (ManagementObjectMap::iterator iter = baseIter;
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
+ object->setFlags(1);
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (object->getConfigChanged() || object->getForcePublish() || object->isDeleted()) {
+ encodeHeader(msgBuffer, 'c');
+ object->writeProperties(msgBuffer);
+ pcount++;
+ }
+
+ if (object->hasInst() && (object->getInstChanged() || object->getForcePublish())) {
+ encodeHeader(msgBuffer, 'i');
+ object->writeStatistics(msgBuffer);
+ scount++;
+ }
+
+ if (object->isDeleted())
+ deleteList.push_back(pair<ObjectId, ManagementObject*>(iter->first, object));
+ object->setForcePublish(false);
+
+ if (msgBuffer.available() < HEADROOM)
+ break;
+ }
+ }
+
+ contentSize = BUFSIZE - msgBuffer.available();
+ if (contentSize > 0) {
+ msgBuffer.reset();
+ stringstream key;
+ key << "console.obj.1.0." << baseObject->getPackageName() << "." << baseObject->getClassName();
+ sendBuffer(msgBuffer, contentSize, mExchange, key.str());
+ QPID_LOG(trace, "SEND Multicast ContentInd to=" << key.str() << " props=" << pcount << " stats=" << scount);
+ }
+ }
+
+ // Delete flagged objects
+ for (list<pair<ObjectId, ManagementObject*> >::reverse_iterator iter = deleteList.rbegin();
+ iter != deleteList.rend();
+ iter++) {
+ delete iter->second;
+ managementObjects.erase(iter->first);
+ }
+
+ if (!deleteList.empty()) {
+ deleteList.clear();
+ deleteOrphanedAgentsLH();
+ }
+
+ {
+ Buffer msgBuffer(msgChars, BUFSIZE);
+ encodeHeader(msgBuffer, 'h');
+ msgBuffer.putLongLong(uint64_t(Duration(now())));
+
+ contentSize = BUFSIZE - msgBuffer.available ();
+ msgBuffer.reset ();
+ routingKey = "console.heartbeat.1.0";
+ sendBuffer (msgBuffer, contentSize, mExchange, routingKey);
+ QPID_LOG(trace, "SEND HeartbeatInd to=" << routingKey);
+ }
+}
+
+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;
+
+#define DNOW_BUFSIZE 2048
+ char msgChars[DNOW_BUFSIZE];
+ uint32_t contentSize;
+ Buffer msgBuffer(msgChars, DNOW_BUFSIZE);
+
+ encodeHeader(msgBuffer, 'c');
+ object->writeProperties(msgBuffer);
+ contentSize = msgBuffer.getPosition();
+ msgBuffer.reset();
+ stringstream key;
+ key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName();
+ sendBuffer(msgBuffer, contentSize, mExchange, key.str());
+ QPID_LOG(trace, "SEND Immediate(delete) ContentInd to=" << key.str());
+
+ managementObjects.erase(oid);
+}
+
+void ManagementAgent::sendCommandComplete (string replyToKey, uint32_t sequence,
+ uint32_t code, 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 ();
+ sendBuffer (outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND CommandCompleteInd code=" << code << " text=" << text << " to=" <<
+ replyToKey << " seq=" << sequence);
+}
+
+bool ManagementAgent::dispatchCommand (Deliverable& deliverable,
+ const string& routingKey,
+ const FieldTable* /*args*/)
+{
+ Mutex::ScopedLock lock (userLock);
+ Message& msg = ((DeliverableMessage&) deliverable).getMessage ();
+
+ // 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;
+ }
+
+ else if (routingKey.compare(0, 9, "agent.1.0") == 0) {
+ dispatchAgentCommandLH(msg);
+ return false;
+ }
+
+ else if (routingKey.compare(0, 8, "agent.1.") == 0) {
+ return authorizeAgentMessageLH(msg);
+ }
+
+ else if (routingKey.compare(0, 7, "schema.") == 0) {
+ dispatchAgentCommandLH(msg);
+ return true;
+ }
+
+ return true;
+}
+
+void ManagementAgent::handleMethodRequestLH (Buffer& inBuffer, string replyToKey,
+ uint32_t sequence, const ConnectionToken* connToken)
+{
+ string methodName;
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+ AclModule* acl = broker->getAcl();
+
+ ObjectId objId(inBuffer);
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+ inBuffer.getShortString(methodName);
+
+ QPID_LOG(trace, "RECV MethodRequest class=" << packageName << ":" << className << "(" << Uuid(hash) << ") method=" <<
+ methodName << " replyTo=" << replyToKey);
+
+ encodeHeader(outBuffer, 'm', sequence);
+
+ DisallowedMethods::const_iterator i = disallowed.find(std::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();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND MethodResponse status=FORBIDDEN text=" << i->second << " seq=" << sequence)
+ return;
+ }
+
+ if (acl != 0) {
+ string userId = ((const qpid::broker::ConnectionState*) connToken)->getUserId();
+ 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();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence)
+ return;
+ }
+ }
+
+ ManagementObjectMap::iterator iter = managementObjects.find(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();
+ Mutex::ScopedUnlock u(userLock);
+ iter->second->doMethod(methodName, inBuffer, outBuffer);
+ } catch(exception& e) {
+ outBuffer.restore();
+ outBuffer.putLong(Manageable::STATUS_EXCEPTION);
+ outBuffer.putMediumString(e.what());
+ }
+ }
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND MethodResponse to=" << replyToKey << " seq=" << sequence);
+}
+
+void ManagementAgent::handleBrokerRequestLH (Buffer&, string replyToKey, uint32_t sequence)
+{
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ QPID_LOG(trace, "RECV BrokerRequest replyTo=" << replyToKey);
+
+ encodeHeader (outBuffer, 'b', sequence);
+ uuid.encode (outBuffer);
+
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBuffer (outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND BrokerResponse to=" << replyToKey);
+}
+
+void ManagementAgent::handlePackageQueryLH (Buffer&, string replyToKey, uint32_t sequence)
+{
+ QPID_LOG(trace, "RECV PackageQuery replyTo=" << replyToKey);
+
+ for (PackageMap::iterator pIter = packages.begin ();
+ pIter != packages.end ();
+ pIter++)
+ {
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader (outBuffer, 'p', sequence);
+ encodePackageIndication (outBuffer, pIter);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBuffer (outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND PackageInd package=" << (*pIter).first << " to=" << replyToKey << " seq=" << sequence);
+ }
+
+ sendCommandComplete (replyToKey, sequence);
+}
+
+void ManagementAgent::handlePackageIndLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
+{
+ string packageName;
+
+ inBuffer.getShortString(packageName);
+
+ QPID_LOG(trace, "RECV PackageInd package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ findOrAddPackageLH(packageName);
+}
+
+void ManagementAgent::handleClassQueryLH(Buffer& inBuffer, string replyToKey, uint32_t sequence)
+{
+ string packageName;
+
+ inBuffer.getShortString(packageName);
+
+ QPID_LOG(trace, "RECV ClassQuery package=" << packageName << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end())
+ {
+ ClassMap cMap = pIter->second;
+ for (ClassMap::iterator cIter = cMap.begin();
+ cIter != cMap.end();
+ cIter++)
+ {
+ if (cIter->second.hasSchema())
+ {
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'q', sequence);
+ encodeClassIndication(outBuffer, pIter, cIter);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND ClassInd class=" << (*pIter).first << ":" << (*cIter).first.name <<
+ "(" << Uuid((*cIter).first.hash) << ") to=" << replyToKey << " seq=" << sequence);
+ }
+ }
+ }
+ sendCommandComplete(replyToKey, sequence);
+}
+
+void ManagementAgent::handleClassIndLH (Buffer& inBuffer, 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(trace, "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++;
+
+ encodeHeader (outBuffer, 'S', sequence);
+ outBuffer.putShortString(packageName);
+ outBuffer.putShortString(key.name);
+ outBuffer.putBin128(key.hash);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBuffer (outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "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)
+ writeSchemaCall(buf);
+ else
+ buf.putRawData(buffer, bufferLen);
+}
+
+void ManagementAgent::handleSchemaRequestLH(Buffer& inBuffer, string replyToKey, uint32_t sequence)
+{
+ string packageName;
+ SchemaClassKey key;
+
+ inBuffer.getShortString (packageName);
+ inBuffer.getShortString (key.name);
+ inBuffer.getBin128 (key.hash);
+
+ QPID_LOG(trace, "RECV SchemaRequest class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) <<
+ "), replyTo=" << replyToKey << " 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();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND SchemaResponse to=" << replyToKey << " seq=" << sequence);
+ }
+ else
+ sendCommandComplete(replyToKey, sequence, 1, "Schema not available");
+ }
+ else
+ sendCommandComplete(replyToKey, sequence, 1, "Class key not found");
+ }
+ else
+ sendCommandComplete(replyToKey, sequence, 1, "Package not found");
+}
+
+void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, string /*replyToKey*/, uint32_t sequence)
+{
+ string packageName;
+ SchemaClassKey key;
+
+ inBuffer.record();
+ inBuffer.getOctet();
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(key.name);
+ inBuffer.getBin128(key.hash);
+ inBuffer.restore();
+
+ QPID_LOG(trace, "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.buffer = (uint8_t*) malloc(length);
+ cIter->second.bufferLen = length;
+ inBuffer.getRawData(cIter->second.buffer, cIter->second.bufferLen);
+
+ // Publish a class-indication message
+ Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ encodeHeader(outBuffer, 'q');
+ encodeClassIndication(outBuffer, pIter, cIter);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ sendBuffer(outBuffer, outLen, mExchange, "schema.class");
+ QPID_LOG(trace, "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()
+{
+ vector<ObjectId> deleteList;
+
+ for (RemoteAgentMap::iterator aIter = remoteAgents.begin(); aIter != remoteAgents.end(); aIter++) {
+ ObjectId connectionRef = aIter->first;
+ bool found = false;
+
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ if (iter->first == connectionRef && !iter->second->isDeleted()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ deleteList.push_back(connectionRef);
+ delete aIter->second;
+ }
+ }
+
+ for (vector<ObjectId>::iterator dIter = deleteList.begin(); dIter != deleteList.end(); dIter++)
+ remoteAgents.erase(*dIter);
+
+ deleteList.clear();
+}
+
+void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, 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.
+ sendCommandComplete(replyToKey, sequence, 1, "Connection already has remote agent");
+ return;
+ }
+
+ inBuffer.getShortString(label);
+ systemId.decode(inBuffer);
+ requestedBrokerBank = inBuffer.getLong();
+ requestedAgentBank = inBuffer.getLong();
+
+ QPID_LOG(trace, "RECV (Agent)AttachRequest label=" << label << " reqBrokerBank=" << requestedBrokerBank <<
+ " reqAgentBank=" << requestedAgentBank << " replyTo=" << replyToKey << " seq=" << sequence);
+
+ assignedBank = assignBankLH(requestedAgentBank);
+
+ RemoteAgent* agent = new RemoteAgent(*this);
+ agent->brokerBank = brokerBank;
+ agent->agentBank = assignedBank;
+ agent->routingKey = replyToKey;
+ agent->connectionRef = connectionRef;
+ agent->mgmtObject = new _qmf::Agent (this, agent);
+ agent->mgmtObject->set_connectionRef(agent->connectionRef);
+ agent->mgmtObject->set_label (label);
+ agent->mgmtObject->set_registeredTo (broker->GetManagementObject()->getObjectId());
+ agent->mgmtObject->set_systemId (systemId);
+ agent->mgmtObject->set_brokerBank (brokerBank);
+ agent->mgmtObject->set_agentBank (assignedBank);
+ addObject (agent->mgmtObject, 0, true);
+
+ remoteAgents[connectionRef] = agent;
+
+ QPID_LOG(trace, "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 ();
+ sendBuffer (outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND AttachResponse brokerBank=" << brokerBank << " agentBank=" << assignedBank <<
+ " to=" << replyToKey << " seq=" << sequence);
+}
+
+void ManagementAgent::handleGetQueryLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
+{
+ FieldTable ft;
+ FieldTable::ValuePtr value;
+
+ moveNewObjectsLH();
+
+ ft.decode(inBuffer);
+
+ QPID_LOG(trace, "RECV GetQuery 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 = managementObjects.find(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()) {
+ encodeHeader(outBuffer, 'g', sequence);
+ object->writeProperties(outBuffer);
+ object->writeStatistics(outBuffer, true);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND GetResponse to=" << replyToKey << " seq=" << sequence);
+ }
+ }
+ sendCommandComplete(replyToKey, sequence);
+ return;
+ }
+
+ string className (value->get<string>());
+
+ for (ManagementObjectMap::iterator iter = managementObjects.begin();
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second;
+ if (object->getClassName () == className) {
+ Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
+ uint32_t outLen;
+
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ if (!object->isDeleted()) {
+ encodeHeader(outBuffer, 'g', sequence);
+ object->writeProperties(outBuffer);
+ object->writeStatistics(outBuffer, true);
+ outLen = MA_BUFFER_SIZE - outBuffer.available ();
+ outBuffer.reset ();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND GetResponse to=" << replyToKey << " seq=" << sequence);
+ }
+ }
+ }
+
+ sendCommandComplete(replyToKey, sequence);
+}
+
+bool ManagementAgent::authorizeAgentMessageLH(Message& msg)
+{
+ Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE);
+ uint8_t opcode;
+ uint32_t sequence;
+ string replyToKey;
+
+ if (msg.encodedSize() > MA_BUFFER_SIZE)
+ return false;
+
+ msg.encodeContent(inBuffer);
+ inBuffer.reset();
+
+ if (!checkHeader(inBuffer, &opcode, &sequence))
+ return false;
+
+ if (opcode == 'M') {
+ // TODO: check method call against ACL list.
+ AclModule* acl = broker->getAcl();
+ if (acl == 0)
+ return true;
+
+ string userId = ((const qpid::broker::ConnectionState*) msg.getPublisher())->getUserId();
+ string packageName;
+ string className;
+ uint8_t hash[16];
+ string methodName;
+
+ map<acl::Property, string> params;
+ ObjectId objId(inBuffer);
+ inBuffer.getShortString(packageName);
+ inBuffer.getShortString(className);
+ inBuffer.getBin128(hash);
+ inBuffer.getShortString(methodName);
+
+ params[acl::PROP_SCHEMAPACKAGE] = packageName;
+ params[acl::PROP_SCHEMACLASS] = className;
+
+ if (acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_METHOD, methodName, &params))
+ return true;
+
+ const framing::MessageProperties* p =
+ msg.getFrames().getHeaders()->get<framing::MessageProperties>();
+ if (p && p->hasReplyTo()) {
+ const framing::ReplyTo& rt = p->getReplyTo();
+ replyToKey = rt.getRoutingKey();
+
+ 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();
+ sendBuffer(outBuffer, outLen, dExchange, replyToKey);
+ QPID_LOG(trace, "SEND MethodResponse status=FORBIDDEN" << " seq=" << sequence)
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+void ManagementAgent::dispatchAgentCommandLH(Message& msg)
+{
+ Buffer inBuffer(inputBuffer, MA_BUFFER_SIZE);
+ uint8_t opcode;
+ uint32_t sequence;
+ string replyToKey;
+
+ const framing::MessageProperties* p =
+ msg.getFrames().getHeaders()->get<framing::MessageProperties>();
+ if (p && p->hasReplyTo()) {
+ const framing::ReplyTo& rt = p->getReplyTo();
+ replyToKey = rt.getRoutingKey();
+ }
+ else
+ return;
+
+ 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();
+
+ while (inBuffer.getPosition() < bufferLen) {
+ if (!checkHeader(inBuffer, &opcode, &sequence))
+ return;
+
+ if (opcode == 'B') handleBrokerRequestLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'P') handlePackageQueryLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'p') handlePackageIndLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'Q') handleClassQueryLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'q') handleClassIndLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'S') handleSchemaRequestLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 's') handleSchemaResponseLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'A') handleAttachRequestLH (inBuffer, replyToKey, sequence, msg.getPublisher());
+ else if (opcode == 'G') handleGetQueryLH (inBuffer, replyToKey, sequence);
+ else if (opcode == 'M') handleMethodRequestLH (inBuffer, replyToKey, 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 ();
+ sendBuffer (outBuffer, outLen, mExchange, "schema.package");
+ QPID_LOG(trace, "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,
+ 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);
+}
+
+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 = 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;
+}
+
+void ManagementAgent::setAllocator(std::auto_ptr<IdAllocator> a)
+{
+ Mutex::ScopedLock lock (addLock);
+ allocator = a;
+}
+
+uint64_t ManagementAgent::allocateId(Manageable* object)
+{
+ Mutex::ScopedLock lock (addLock);
+ if (allocator.get()) return allocator->getIdFor(object);
+ return 0;
+}
+
+void ManagementAgent::disallow(const std::string& className, const std::string& methodName, const std::string& message) {
+ disallowed[std::make_pair(className, methodName)] = message;
+}
diff --git a/cpp/src/qpid/management/ManagementBroker.h b/cpp/src/qpid/management/ManagementAgent.h
index 151926f526..c64c073d8c 100644
--- a/cpp/src/qpid/management/ManagementBroker.h
+++ b/cpp/src/qpid/management/ManagementAgent.h
@@ -1,5 +1,5 @@
-#ifndef _ManagementBroker_
-#define _ManagementBroker_
+#ifndef _ManagementAgent_
+#define _ManagementAgent_
/*
*
@@ -21,62 +21,89 @@
* under the License.
*
*/
+#include "qpid/broker/BrokerImportExport.h"
#include "qpid/Options.h"
#include "qpid/broker/Exchange.h"
-#include "qpid/broker/Timer.h"
#include "qpid/framing/Uuid.h"
#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Timer.h"
#include "qpid/broker/ConnectionToken.h"
-#include "qpid/agent/ManagementAgent.h"
-#include "ManagementObject.h"
-#include "Manageable.h"
-#include "qpid/management/Agent.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/framing/AMQFrame.h>
+#include <memory>
+#include <string>
+#include <map>
namespace qpid {
namespace management {
-class ManagementBroker : public ManagementAgent
+struct IdAllocator;
+
+class ManagementAgent
{
- private:
+private:
int threadPoolSize;
- public:
+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;
+
- ManagementBroker ();
- virtual ~ManagementBroker ();
+ ManagementAgent ();
+ virtual ~ManagementAgent ();
- void configure (std::string dataDir, uint16_t interval, Manageable* broker, int threadPoolSize);
+ void configure (const std::string& dataDir, uint16_t interval,
+ qpid::broker::Broker* broker, int threadPoolSize);
void setInterval (uint16_t _interval) { interval = _interval; }
- void setExchange (broker::Exchange::shared_ptr mgmtExchange,
- broker::Exchange::shared_ptr directExchange);
+ void setExchange (qpid::broker::Exchange::shared_ptr mgmtExchange,
+ qpid::broker::Exchange::shared_ptr directExchange);
int getMaxThreads () { return threadPoolSize; }
- void RegisterClass (std::string packageName,
- std::string className,
- uint8_t* md5Sum,
- ManagementObject::writeSchemaCall_t schemaCall);
- uint64_t addObject (ManagementObject* object,
- uint32_t persistId = 0,
- uint32_t persistBank = 4);
- void clientAdded (void);
- bool dispatchCommand (broker::Deliverable& msg,
+ 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 publishNow = false);
+ QPID_BROKER_EXTERN void raiseEvent(const ManagementEvent& event,
+ severity_t severity = SEV_DEFAULT);
+ QPID_BROKER_EXTERN void clientAdded (const std::string& routingKey);
+
+ bool dispatchCommand (qpid::broker::Deliverable& msg,
const std::string& routingKey,
const framing::FieldTable* args);
- // Stubs for remote management agent calls
- void init (std::string, uint16_t, uint16_t, bool) { assert(0); }
- uint32_t pollCallbacks (uint32_t) { assert(0); return 0; }
- int getSignalFd () { assert(0); return -1; }
+ const framing::Uuid& getUuid() const { return uuid; }
- private:
- friend class ManagementAgent;
+ void setAllocator(std::auto_ptr<IdAllocator> allocator);
+ uint64_t allocateId(Manageable* object);
- struct Periodic : public broker::TimerTask
+ /** 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);
+
+private:
+ struct Periodic : public qpid::sys::TimerTask
{
- ManagementBroker& broker;
+ ManagementAgent& agent;
- Periodic (ManagementBroker& broker, uint32_t seconds);
+ Periodic (ManagementAgent& agent, uint32_t seconds);
virtual ~Periodic ();
void fire ();
};
@@ -86,10 +113,13 @@ class ManagementBroker : public ManagementAgent
//
struct RemoteAgent : public Manageable
{
- uint32_t objIdBank;
+ ManagementAgent& agent;
+ uint32_t brokerBank;
+ uint32_t agentBank;
std::string routingKey;
- uint64_t connectionRef;
- Agent* mgmtObject;
+ ObjectId connectionRef;
+ qmf::org::apache::qpid::broker::Agent* mgmtObject;
+ RemoteAgent(ManagementAgent& _agent) : agent(_agent) {}
ManagementObject* GetManagementObject (void) const { return mgmtObject; }
virtual ~RemoteAgent ();
};
@@ -97,7 +127,7 @@ class ManagementBroker : public ManagementAgent
// TODO: Eventually replace string with entire reply-to structure. reply-to
// currently assumes that the exchange is "amq.direct" even though it could
// in theory be specified differently.
- typedef std::map<uint64_t, RemoteAgent*> RemoteAgentMap;
+ typedef std::map<ObjectId, RemoteAgent*> RemoteAgentMap;
typedef std::vector<std::string> ReplyToVector;
// Storage for known schema classes:
@@ -128,17 +158,21 @@ class ManagementBroker : public ManagementAgent
struct SchemaClass
{
+ uint8_t kind;
ManagementObject::writeSchemaCall_t writeSchemaCall;
uint32_t pendingSequence;
size_t bufferLen;
uint8_t* buffer;
- SchemaClass () : writeSchemaCall(0), pendingSequence(0), bufferLen(0), buffer(0) {}
+ SchemaClass(uint8_t _kind, uint32_t seq) :
+ kind(_kind), writeSchemaCall(0), pendingSequence(seq), bufferLen(0), buffer(0) {}
+ SchemaClass(uint8_t _kind, ManagementObject::writeSchemaCall_t call) :
+ kind(_kind), writeSchemaCall(call), pendingSequence(0), bufferLen(0), buffer(0) {}
bool hasSchema () { return (writeSchemaCall != 0) || (buffer != 0); }
void appendSchema (framing::Buffer& buf);
};
- typedef std::map<SchemaClassKey, SchemaClass*, SchemaClassKeyComp> ClassMap;
+ typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap;
typedef std::map<std::string, ClassMap> PackageMap;
RemoteAgentMap remoteAgents;
@@ -152,43 +186,55 @@ class ManagementBroker : public ManagementAgent
framing::Uuid uuid;
sys::Mutex addLock;
sys::Mutex userLock;
- broker::Timer timer;
- broker::Exchange::shared_ptr mExchange;
- broker::Exchange::shared_ptr dExchange;
+ qpid::broker::Exchange::shared_ptr mExchange;
+ qpid::broker::Exchange::shared_ptr dExchange;
std::string dataDir;
uint16_t interval;
- Manageable* broker;
+ qpid::broker::Broker* broker;
+ qpid::sys::Timer* timer;
uint16_t bootSequence;
- uint32_t localBank;
uint32_t nextObjectId;
+ uint32_t brokerBank;
uint32_t nextRemoteBank;
uint32_t nextRequestSequence;
bool clientWasAdded;
+ const uint64_t startTime;
+
+ std::auto_ptr<IdAllocator> allocator;
+
+ typedef std::pair<std::string,std::string> MethodName;
+ typedef std::map<MethodName, std::string> DisallowedMethods;
+ DisallowedMethods disallowed;
+
# define MA_BUFFER_SIZE 65536
char inputBuffer[MA_BUFFER_SIZE];
char outputBuffer[MA_BUFFER_SIZE];
+ char eventBuffer[MA_BUFFER_SIZE];
void writeData ();
- void PeriodicProcessing (void);
- void EncodeHeader (framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0);
- bool CheckHeader (framing::Buffer& buf, uint8_t *opcode, uint32_t *seq);
- void SendBuffer (framing::Buffer& buf,
+ 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 sendBuffer (framing::Buffer& buf,
uint32_t length,
- broker::Exchange::shared_ptr exchange,
+ qpid::broker::Exchange::shared_ptr exchange,
std::string routingKey);
void moveNewObjectsLH();
- void dispatchAgentCommandLH (broker::Message& msg);
+ bool authorizeAgentMessageLH(qpid::broker::Message& msg);
+ void dispatchAgentCommandLH(qpid::broker::Message& msg);
- PackageMap::iterator FindOrAddPackageLH(std::string name);
- void AddClass(PackageMap::iterator pIter,
- std::string className,
- uint8_t* md5Sum,
- ManagementObject::writeSchemaCall_t schemaCall);
- void EncodePackageIndication (framing::Buffer& buf,
+ 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,
+ void encodeClassIndication (framing::Buffer& buf,
PackageMap::iterator pIter,
ClassMap::iterator cIter);
bool bankInUse (uint32_t bank);
@@ -204,13 +250,15 @@ class ManagementBroker : public ManagementAgent
void handleClassIndLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence);
void handleSchemaRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence);
void handleSchemaResponseLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence);
- void handleAttachRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence, const broker::ConnectionToken* connToken);
+ void handleAttachRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
void handleGetQueryLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence);
- void handleMethodRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence);
+ void handleMethodRequestLH (framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence, const qpid::broker::ConnectionToken* connToken);
- size_t ValidateSchema(framing::Buffer&);
+ size_t validateSchema(framing::Buffer&, uint8_t kind);
+ size_t validateTableSchema(framing::Buffer&);
+ size_t validateEventSchema(framing::Buffer&);
};
}}
-#endif /*!_ManagementBroker_*/
+#endif /*!_ManagementAgent_*/
diff --git a/cpp/src/qpid/management/ManagementBroker.cpp b/cpp/src/qpid/management/ManagementBroker.cpp
deleted file mode 100644
index 1bdd8ab836..0000000000
--- a/cpp/src/qpid/management/ManagementBroker.cpp
+++ /dev/null
@@ -1,933 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "ManagementBroker.h"
-#include "qpid/broker/DeliverableMessage.h"
-#include "qpid/log/Statement.h"
-#include <qpid/broker/Message.h>
-#include <qpid/broker/MessageDelivery.h>
-#include "qpid/framing/MessageTransferBody.h"
-#include "qpid/sys/Time.h"
-#include "qpid/broker/ConnectionState.h"
-#include <list>
-#include <iostream>
-#include <fstream>
-
-using boost::intrusive_ptr;
-using qpid::framing::Uuid;
-using namespace qpid::framing;
-using namespace qpid::management;
-using namespace qpid::broker;
-using namespace qpid::sys;
-using namespace std;
-
-Mutex ManagementAgent::Singleton::lock;
-bool ManagementAgent::Singleton::disabled = false;
-ManagementAgent* ManagementAgent::Singleton::agent = 0;
-int ManagementAgent::Singleton::refCount = 0;
-
-ManagementAgent::Singleton::Singleton(bool disableManagement)
-{
- 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 ManagementBroker();
- refCount++;
-}
-
-ManagementAgent::Singleton::~Singleton()
-{
- Mutex::ScopedLock _lock(lock);
- refCount--;
- if (refCount == 0 && !disabled) {
- delete agent;
- agent = 0;
- }
-}
-
-ManagementAgent* ManagementAgent::Singleton::getInstance()
-{
- return agent;
-}
-
-ManagementBroker::RemoteAgent::~RemoteAgent ()
-{
- if (mgmtObject != 0)
- mgmtObject->resourceDestroy();
-}
-
-ManagementBroker::ManagementBroker () :
- threadPoolSize(1), interval(10), broker(0)
-{
- localBank = 5;
- nextObjectId = 1;
- bootSequence = 1;
- nextRemoteBank = 10;
- nextRequestSequence = 1;
- clientWasAdded = false;
-}
-
-ManagementBroker::~ManagementBroker ()
-{
- timer.stop();
- {
- 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 implicitely destroyed (long
- // after this destructor completes). Those exchanges hold references to management
- // objects that will be invalid.
- dExchange.reset();
- mExchange.reset();
-
- moveNewObjectsLH();
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++) {
- ManagementObject* object = iter->second;
- delete object;
- }
- managementObjects.clear();
- }
-}
-
-void ManagementBroker::configure(string _dataDir, uint16_t _interval, Manageable* _broker, int _threads)
-{
- dataDir = _dataDir;
- interval = _interval;
- broker = _broker;
- threadPoolSize = _threads;
- timer.add (intrusive_ptr<TimerTask> (new Periodic(*this, interval)));
-
- // Get from file or generate and save to file.
- if (dataDir.empty())
- {
- uuid.generate();
- QPID_LOG (info, "ManagementBroker 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();
- QPID_LOG (debug, "ManagementBroker restored broker ID: " << uuid);
-
- bootSequence++;
- writeData();
- }
- else
- {
- uuid.generate();
- QPID_LOG (info, "ManagementBroker generated broker ID: " << uuid);
- writeData();
- }
-
- QPID_LOG (debug, "ManagementBroker boot sequence: " << bootSequence);
- }
-}
-
-void ManagementBroker::writeData ()
-{
- string filename (dataDir + "/.mbrokerdata");
- ofstream outFile (filename.c_str ());
-
- if (outFile.good())
- {
- outFile << uuid << " " << bootSequence << " " << nextRemoteBank << endl;
- outFile.close();
- }
-}
-
-void ManagementBroker::setExchange (broker::Exchange::shared_ptr _mexchange,
- broker::Exchange::shared_ptr _dexchange)
-{
- mExchange = _mexchange;
- dExchange = _dexchange;
-}
-
-void ManagementBroker::RegisterClass (string packageName,
- string className,
- uint8_t* md5Sum,
- ManagementObject::writeSchemaCall_t schemaCall)
-{
- Mutex::ScopedLock lock(userLock);
- PackageMap::iterator pIter = FindOrAddPackageLH(packageName);
- AddClass(pIter, className, md5Sum, schemaCall);
-}
-
-uint64_t ManagementBroker::addObject (ManagementObject* object,
- uint32_t persistId,
- uint32_t persistBank)
-{
- Mutex::ScopedLock lock (addLock);
- uint64_t objectId;
-
- if (persistId == 0)
- {
- objectId = ((uint64_t) bootSequence) << 48 |
- ((uint64_t) localBank) << 24 | nextObjectId++;
- if ((nextObjectId & 0xFF000000) != 0)
- {
- nextObjectId = 1;
- localBank++;
- }
- }
- else
- objectId = ((uint64_t) persistBank) << 24 | persistId;
-
- object->setObjectId (objectId);
- newManagementObjects[objectId] = object;
- return objectId;
-}
-
-ManagementBroker::Periodic::Periodic (ManagementBroker& _broker, uint32_t _seconds)
- : TimerTask (qpid::sys::Duration ((_seconds ? _seconds : 1) * qpid::sys::TIME_SEC)), broker(_broker) {}
-
-ManagementBroker::Periodic::~Periodic () {}
-
-void ManagementBroker::Periodic::fire ()
-{
- broker.timer.add (intrusive_ptr<TimerTask> (new Periodic (broker, broker.interval)));
- broker.PeriodicProcessing ();
-}
-
-void ManagementBroker::clientAdded (void)
-{
- Mutex::ScopedLock lock (userLock);
-
- clientWasAdded = true;
- for (RemoteAgentMap::iterator aIter = remoteAgents.begin();
- aIter != remoteAgents.end();
- aIter++) {
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'x');
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, aIter->second->routingKey);
- }
-}
-
-void ManagementBroker::EncodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq)
-{
- buf.putOctet ('A');
- buf.putOctet ('M');
- buf.putOctet ('1');
- buf.putOctet (opcode);
- buf.putLong (seq);
-}
-
-bool ManagementBroker::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 == '1';
-}
-
-void ManagementBroker::SendBuffer (Buffer& buf,
- uint32_t length,
- broker::Exchange::shared_ptr exchange,
- string routingKey)
-{
- if (exchange.get() == 0)
- return;
-
- intrusive_ptr<Message> msg (new Message ());
- AMQFrame method (in_place<MessageTransferBody>(
- ProtocolVersion(), exchange->getName (), 0, 0));
- AMQFrame header (in_place<AMQHeaderBody>());
- AMQFrame content(in_place<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);
- msg->getFrames().append(content);
-
- DeliverableMessage deliverable (msg);
- exchange->route (deliverable, routingKey, 0);
-}
-
-void ManagementBroker::moveNewObjectsLH()
-{
- Mutex::ScopedLock lock (addLock);
- for (ManagementObjectMap::iterator iter = newManagementObjects.begin ();
- iter != newManagementObjects.end ();
- iter++)
- managementObjects[iter->first] = iter->second;
- newManagementObjects.clear();
-}
-
-void ManagementBroker::PeriodicProcessing (void)
-{
-#define BUFSIZE 65536
- Mutex::ScopedLock lock (userLock);
- char msgChars[BUFSIZE];
- uint32_t contentSize;
- string routingKey;
- std::list<uint64_t> deleteList;
-
- {
- Buffer msgBuffer(msgChars, BUFSIZE);
- EncodeHeader(msgBuffer, 'h');
- msgBuffer.putLongLong(uint64_t(Duration(now())));
-
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + uuid.str() + ".heartbeat";
- SendBuffer (msgBuffer, contentSize, mExchange, routingKey);
- }
-
- moveNewObjectsLH();
-
- if (clientWasAdded)
- {
- clientWasAdded = false;
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++)
- {
- ManagementObject* object = iter->second;
- object->setAllChanged ();
- }
- }
-
- if (managementObjects.empty ())
- return;
-
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++)
- {
- ManagementObject* object = iter->second;
-
- if (object->getConfigChanged () || object->isDeleted ())
- {
- Buffer msgBuffer (msgChars, BUFSIZE);
- EncodeHeader (msgBuffer, 'c');
- object->writeProperties(msgBuffer);
-
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + uuid.str() + ".prop." + object->getClassName ();
- SendBuffer (msgBuffer, contentSize, mExchange, routingKey);
- }
-
- if (object->getInstChanged ())
- {
- Buffer msgBuffer (msgChars, BUFSIZE);
- EncodeHeader (msgBuffer, 'i');
- object->writeStatistics(msgBuffer);
-
- contentSize = BUFSIZE - msgBuffer.available ();
- msgBuffer.reset ();
- routingKey = "mgmt." + uuid.str () + ".stat." + object->getClassName ();
- SendBuffer (msgBuffer, contentSize, mExchange, routingKey);
- }
-
- if (object->isDeleted ())
- deleteList.push_back (iter->first);
- }
-
- // Delete flagged objects
- for (std::list<uint64_t>::reverse_iterator iter = deleteList.rbegin ();
- iter != deleteList.rend ();
- iter++)
- managementObjects.erase (*iter);
-
- if (!deleteList.empty()) {
- deleteList.clear();
- deleteOrphanedAgentsLH();
- }
-}
-
-void ManagementBroker::sendCommandComplete (string replyToKey, uint32_t sequence,
- uint32_t code, 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 ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
-}
-
-bool ManagementBroker::dispatchCommand (Deliverable& deliverable,
- const string& routingKey,
- const FieldTable* /*args*/)
-{
- Mutex::ScopedLock lock (userLock);
- Message& msg = ((DeliverableMessage&) deliverable).getMessage ();
-
- // Parse the routing key. This management broker should act as though it
- // is bound to the exchange to match the following keys:
- //
- // agent.<X>.#
- // broker.#
- //
- // where <X> is any non-negative decimal integer less than the lowest remote
- // object-id bank.
-
- if (routingKey == "broker") {
- dispatchAgentCommandLH (msg);
- return false;
- }
-
- else if (routingKey.compare(0, 6, "agent.") == 0) {
- std::string::size_type delim = routingKey.find('.', 6);
- if (delim == string::npos)
- delim = routingKey.length();
- string bank = routingKey.substr(6, delim - 6);
- if ((uint32_t) atoi(bank.c_str()) <= localBank) {
- dispatchAgentCommandLH (msg);
- return false;
- }
- }
-
- return true;
-}
-
-void ManagementBroker::handleMethodRequestLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
-{
- string methodName;
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- uint64_t objId = inBuffer.getLongLong();
- inBuffer.getShortString(methodName);
-
- EncodeHeader(outBuffer, 'm', sequence);
-
- ManagementObjectMap::iterator iter = managementObjects.find(objId);
- if (iter == managementObjects.end() || iter->second->isDeleted()) {
- outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT);
- outBuffer.putShortString (Manageable::StatusText (Manageable::STATUS_UNKNOWN_OBJECT));
- } else {
- iter->second->doMethod(methodName, inBuffer, outBuffer);
- }
-
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- SendBuffer(outBuffer, outLen, dExchange, replyToKey);
-}
-
-void ManagementBroker::handleBrokerRequestLH (Buffer&, string replyToKey, uint32_t sequence)
-{
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'b', sequence);
- uuid.encode (outBuffer);
-
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
-}
-
-void ManagementBroker::handlePackageQueryLH (Buffer&, string replyToKey, uint32_t sequence)
-{
- for (PackageMap::iterator pIter = packages.begin ();
- pIter != packages.end ();
- pIter++)
- {
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'p', sequence);
- EncodePackageIndication (outBuffer, pIter);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
- }
-
- sendCommandComplete (replyToKey, sequence);
-}
-
-void ManagementBroker::handlePackageIndLH (Buffer& inBuffer, string /*replyToKey*/, uint32_t /*sequence*/)
-{
- std::string packageName;
-
- inBuffer.getShortString(packageName);
- FindOrAddPackageLH(packageName);
-}
-
-void ManagementBroker::handleClassQueryLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
-{
- std::string packageName;
-
- inBuffer.getShortString (packageName);
- PackageMap::iterator pIter = packages.find (packageName);
- if (pIter != packages.end ())
- {
- ClassMap cMap = pIter->second;
- for (ClassMap::iterator cIter = cMap.begin ();
- cIter != cMap.end ();
- cIter++)
- {
- if (cIter->second->hasSchema ())
- {
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'q', sequence);
- EncodeClassIndication (outBuffer, pIter, cIter);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
- }
- }
- }
-
- sendCommandComplete (replyToKey, sequence);
-}
-
-void ManagementBroker::handleClassIndLH (Buffer& inBuffer, string replyToKey, uint32_t)
-{
- std::string packageName;
- SchemaClassKey key;
-
- inBuffer.getShortString(packageName);
- inBuffer.getShortString(key.name);
- inBuffer.getBin128(key.hash);
-
- PackageMap::iterator pIter = FindOrAddPackageLH(packageName);
- ClassMap::iterator cIter = pIter->second.find(key);
- if (cIter == pIter->second.end()) {
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
- uint32_t sequence = nextRequestSequence++;
-
- EncodeHeader (outBuffer, 'S', sequence);
- outBuffer.putShortString(packageName);
- outBuffer.putShortString(key.name);
- outBuffer.putBin128(key.hash);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
-
- SchemaClass* newSchema = new SchemaClass;
- newSchema->pendingSequence = sequence;
- pIter->second[key] = newSchema;
- }
-}
-
-void ManagementBroker::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)
- writeSchemaCall(buf);
- else
- buf.putRawData(buffer, bufferLen);
-}
-
-void ManagementBroker::handleSchemaRequestLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
-{
- string packageName;
- SchemaClassKey key;
-
- inBuffer.getShortString (packageName);
- inBuffer.getShortString (key.name);
- inBuffer.getBin128 (key.hash);
-
- 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();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
- }
- else
- sendCommandComplete (replyToKey, sequence, 1, "Schema not available");
- }
- else
- sendCommandComplete (replyToKey, sequence, 1, "Class key not found");
- }
- else
- sendCommandComplete (replyToKey, sequence, 1, "Package not found");
-}
-
-void ManagementBroker::handleSchemaResponseLH (Buffer& inBuffer, string /*replyToKey*/, uint32_t sequence)
-{
- string packageName;
- SchemaClassKey key;
-
- inBuffer.record();
- inBuffer.getShortString (packageName);
- inBuffer.getShortString (key.name);
- inBuffer.getBin128 (key.hash);
- inBuffer.restore();
-
- 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);
- if (length == 0)
- cMap.erase(key);
- else {
- cIter->second->buffer = (uint8_t*) malloc(length);
- cIter->second->bufferLen = length;
- inBuffer.getRawData(cIter->second->buffer, cIter->second->bufferLen);
-
- // Publish a class-indication message
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'q');
- EncodeClassIndication (outBuffer, pIter, cIter);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, mExchange, "mgmt." + uuid.str() + ".schema");
- }
- }
- }
-}
-
-bool ManagementBroker::bankInUse (uint32_t bank)
-{
- for (RemoteAgentMap::iterator aIter = remoteAgents.begin();
- aIter != remoteAgents.end();
- aIter++)
- if (aIter->second->objIdBank == bank)
- return true;
- return false;
-}
-
-uint32_t ManagementBroker::allocateNewBank ()
-{
- while (bankInUse (nextRemoteBank))
- nextRemoteBank++;
-
- uint32_t allocated = nextRemoteBank++;
- writeData ();
- return allocated;
-}
-
-uint32_t ManagementBroker::assignBankLH (uint32_t requestedBank)
-{
- if (requestedBank == 0 || bankInUse (requestedBank))
- return allocateNewBank ();
- return requestedBank;
-}
-
-void ManagementBroker::deleteOrphanedAgentsLH()
-{
- vector<uint64_t> deleteList;
-
- for (RemoteAgentMap::iterator aIter = remoteAgents.begin(); aIter != remoteAgents.end(); aIter++) {
- uint64_t connectionRef = aIter->first;
- bool found = false;
-
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++) {
- if (iter->first == connectionRef && !iter->second->isDeleted()) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- deleteList.push_back(connectionRef);
- delete aIter->second;
- }
- }
-
- for (vector<uint64_t>::iterator dIter = deleteList.begin(); dIter != deleteList.end(); dIter++) {
-
- remoteAgents.erase(*dIter);
- }
-
- deleteList.clear();
-}
-
-void ManagementBroker::handleAttachRequestLH (Buffer& inBuffer, string replyToKey, uint32_t sequence, const ConnectionToken* connToken)
-{
- string label;
- uint32_t requestedBank;
- uint32_t assignedBank;
- uint64_t 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.
- sendCommandComplete (replyToKey, sequence, 1, "Connection already has remote agent");
- return;
- }
-
- inBuffer.getShortString (label);
- systemId.decode (inBuffer);
- requestedBank = inBuffer.getLong ();
- assignedBank = assignBankLH (requestedBank);
-
- RemoteAgent* agent = new RemoteAgent;
- agent->objIdBank = assignedBank;
- agent->routingKey = replyToKey;
- agent->connectionRef = connectionRef;
- agent->mgmtObject = new management::Agent (this, agent);
- agent->mgmtObject->set_connectionRef(agent->connectionRef);
- agent->mgmtObject->set_label (label);
- agent->mgmtObject->set_registeredTo (broker->GetManagementObject()->getObjectId());
- agent->mgmtObject->set_systemId (systemId);
- agent->mgmtObject->set_objectIdBank (assignedBank);
- addObject (agent->mgmtObject);
-
- remoteAgents[connectionRef] = agent;
-
- // Send an Attach Response
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'a', sequence);
- outBuffer.putLong (assignedBank);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
-}
-
-void ManagementBroker::handleGetQueryLH (Buffer& inBuffer, string replyToKey, uint32_t sequence)
-{
- FieldTable ft;
- FieldTable::ValuePtr value;
-
- moveNewObjectsLH();
-
- ft.decode(inBuffer);
- value = ft.get("_class");
- if (value.get() == 0 || !value->convertsTo<string>())
- {
- // TODO: Send completion with an error code
- return;
- }
-
- string className (value->get<string>());
-
- for (ManagementObjectMap::iterator iter = managementObjects.begin ();
- iter != managementObjects.end ();
- iter++)
- {
- ManagementObject* object = iter->second;
- if (object->getClassName () == className)
- {
- Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- EncodeHeader (outBuffer, 'g', sequence);
- object->writeProperties(outBuffer);
- object->writeStatistics(outBuffer, true);
- outLen = MA_BUFFER_SIZE - outBuffer.available ();
- outBuffer.reset ();
- SendBuffer (outBuffer, outLen, dExchange, replyToKey);
- }
- }
-
- sendCommandComplete (replyToKey, sequence);
-}
-
-void ManagementBroker::dispatchAgentCommandLH (Message& msg)
-{
- Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE);
- uint8_t opcode;
- uint32_t sequence;
- string replyToKey;
-
- const framing::MessageProperties* p =
- msg.getFrames().getHeaders()->get<framing::MessageProperties>();
- if (p && p->hasReplyTo()) {
- const framing::ReplyTo& rt = p->getReplyTo();
- replyToKey = rt.getRoutingKey();
- }
- else
- return;
-
- if (msg.encodedSize() > MA_BUFFER_SIZE) {
- QPID_LOG(debug, "ManagementBroker::dispatchAgentCommandLH: Message too large: " <<
- msg.encodedSize());
- return;
- }
-
- msg.encodeContent(inBuffer);
- inBuffer.reset();
-
- if (!CheckHeader(inBuffer, &opcode, &sequence))
- return;
-
- if (opcode == 'B') handleBrokerRequestLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'P') handlePackageQueryLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'p') handlePackageIndLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'Q') handleClassQueryLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'q') handleClassIndLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'S') handleSchemaRequestLH (inBuffer, replyToKey, sequence);
- else if (opcode == 's') handleSchemaResponseLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'A') handleAttachRequestLH (inBuffer, replyToKey, sequence, msg.getPublisher());
- else if (opcode == 'G') handleGetQueryLH (inBuffer, replyToKey, sequence);
- else if (opcode == 'M') handleMethodRequestLH (inBuffer, replyToKey, sequence);
-}
-
-ManagementBroker::PackageMap::iterator ManagementBroker::FindOrAddPackageLH(std::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, "ManagementBroker 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 ();
- SendBuffer (outBuffer, outLen, mExchange, "mgmt." + uuid.str() + ".schema.package");
-
- return result.first;
-}
-
-void ManagementBroker::AddClass(PackageMap::iterator pIter,
- 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, "ManagementBroker added class " << pIter->first << "." <<
- key.name);
- SchemaClass* classInfo = new SchemaClass;
-
- classInfo->writeSchemaCall = schemaCall;
- cMap[key] = classInfo;
- cIter = cMap.find (key);
-}
-
-void ManagementBroker::EncodePackageIndication (Buffer& buf,
- PackageMap::iterator pIter)
-{
- buf.putShortString ((*pIter).first);
-}
-
-void ManagementBroker::EncodeClassIndication (Buffer& buf,
- PackageMap::iterator pIter,
- ClassMap::iterator cIter)
-{
- SchemaClassKey key = (*cIter).first;
-
- buf.putShortString ((*pIter).first);
- buf.putShortString (key.name);
- buf.putBin128 (key.hash);
-}
-
-size_t ManagementBroker::ValidateSchema(Buffer& inBuffer)
-{
- uint32_t start = inBuffer.getPosition();
- uint32_t end;
- string text;
- uint8_t hash[16];
-
- inBuffer.record();
- inBuffer.getShortString(text);
- inBuffer.getShortString(text);
- inBuffer.getBin128(hash);
-
- uint16_t propCount = inBuffer.getShort();
- uint16_t statCount = inBuffer.getShort();
- uint16_t methCount = inBuffer.getShort();
- uint16_t evntCount = inBuffer.getShort();
-
- 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);
- int argCount = ft.getInt("argCount");
- for (int mIdx = 0; mIdx < argCount; mIdx++) {
- FieldTable aft;
- aft.decode(inBuffer);
- }
- }
-
- if (evntCount != 0)
- return 0;
-
- end = inBuffer.getPosition();
- inBuffer.restore(); // restore original position
- return end - start;
-}
diff --git a/cpp/src/qpid/management/ManagementExchange.cpp b/cpp/src/qpid/management/ManagementExchange.cpp
index 4ccf8e68c9..b90bcd87d8 100644
--- a/cpp/src/qpid/management/ManagementExchange.cpp
+++ b/cpp/src/qpid/management/ManagementExchange.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "ManagementExchange.h"
+#include "qpid/management/ManagementExchange.h"
#include "qpid/log/Statement.h"
using namespace qpid::management;
@@ -27,14 +27,14 @@ using namespace qpid::broker;
using namespace qpid::framing;
using namespace qpid::sys;
-ManagementExchange::ManagementExchange (const string& _name, Manageable* _parent) :
- Exchange (_name, _parent), TopicExchange(_name, _parent) {}
+ManagementExchange::ManagementExchange (const string& _name, Manageable* _parent, Broker* b) :
+ Exchange (_name, _parent, b), TopicExchange(_name, _parent, b) {}
ManagementExchange::ManagementExchange (const std::string& _name,
bool _durable,
const FieldTable& _args,
- Manageable* _parent) :
- Exchange (_name, _durable, _args, _parent),
- TopicExchange(_name, _durable, _args, _parent) {}
+ Manageable* _parent, Broker* b) :
+ Exchange (_name, _durable, _args, _parent, b),
+ TopicExchange(_name, _durable, _args, _parent, b) {}
void ManagementExchange::route (Deliverable& msg,
const string& routingKey,
@@ -56,11 +56,11 @@ bool ManagementExchange::bind (Queue::shared_ptr queue,
const string& routingKey,
const qpid::framing::FieldTable* args)
{
- managementAgent->clientAdded ();
- return TopicExchange::bind (queue, routingKey, args);
+ managementAgent->clientAdded(routingKey);
+ return TopicExchange::bind(queue, routingKey, args);
}
-void ManagementExchange::setManagmentAgent (ManagementBroker* agent)
+void ManagementExchange::setManagmentAgent (ManagementAgent* agent)
{
managementAgent = agent;
}
diff --git a/cpp/src/qpid/management/ManagementExchange.h b/cpp/src/qpid/management/ManagementExchange.h
index d54db1a74e..3fa4039af7 100644
--- a/cpp/src/qpid/management/ManagementExchange.h
+++ b/cpp/src/qpid/management/ManagementExchange.h
@@ -22,7 +22,7 @@
#define _ManagementExchange_
#include "qpid/broker/TopicExchange.h"
-#include "ManagementBroker.h"
+#include "qpid/management/ManagementAgent.h"
namespace qpid {
namespace broker {
@@ -30,15 +30,15 @@ namespace broker {
class ManagementExchange : public virtual TopicExchange
{
private:
- management::ManagementBroker* managementAgent;
+ management::ManagementAgent* managementAgent;
public:
static const std::string typeName;
- ManagementExchange (const string& name, Manageable* _parent = 0);
+ ManagementExchange (const string& name, Manageable* _parent = 0, Broker* broker = 0);
ManagementExchange (const string& _name, bool _durable,
const qpid::framing::FieldTable& _args,
- Manageable* _parent = 0);
+ Manageable* _parent = 0, Broker* broker = 0);
virtual std::string getType() const { return typeName; }
@@ -50,7 +50,7 @@ class ManagementExchange : public virtual TopicExchange
const string& routingKey,
const qpid::framing::FieldTable* args);
- void setManagmentAgent (management::ManagementBroker* agent);
+ void setManagmentAgent (management::ManagementAgent* agent);
virtual ~ManagementExchange();
};
diff --git a/cpp/src/qpid/management/ManagementObject.cpp b/cpp/src/qpid/management/ManagementObject.cpp
index 74d9571d10..bcb6159663 100644
--- a/cpp/src/qpid/management/ManagementObject.cpp
+++ b/cpp/src/qpid/management/ManagementObject.cpp
@@ -19,38 +19,165 @@
*
*/
-#include "Manageable.h"
-#include "ManagementObject.h"
-#include "qpid/agent/ManagementAgent.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementObject.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/Thread.h"
-using namespace qpid::framing;
+#include <stdlib.h>
+
+using namespace qpid;
using namespace qpid::management;
-using namespace qpid::sys;
+void AgentAttachment::setBanks(uint32_t broker, uint32_t bank)
+{
+ first =
+ ((uint64_t) (broker & 0x000fffff)) << 28 |
+ ((uint64_t) (bank & 0x0fffffff));
+}
+
+ObjectId::ObjectId(uint8_t flags, uint16_t seq, uint32_t broker, uint32_t bank, uint64_t object)
+ : agent(0)
+{
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48 |
+ ((uint64_t) (broker & 0x000fffff)) << 28 |
+ ((uint64_t) (bank & 0x0fffffff));
+ second = object;
+}
+
+ObjectId::ObjectId(AgentAttachment* _agent, uint8_t flags, uint16_t seq, uint64_t object)
+ : agent(_agent)
+{
+ first =
+ ((uint64_t) (flags & 0x0f)) << 60 |
+ ((uint64_t) (seq & 0x0fff)) << 48;
+ second = object;
+}
+
+ObjectId::ObjectId(std::istream& in) : agent(0)
+{
+ std::string text;
+ in >> text;
+ fromString(text);
+}
+
+ObjectId::ObjectId(const std::string& text) : agent(0)
+{
+ fromString(text);
+}
+
+void ObjectId::fromString(const std::string& text)
+{
+#define FIELDS 5
+#if defined (_WIN32) && !defined (atoll)
+# define atoll(X) _atoi64(X)
+#endif
+
+ std::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");
+
+ first = (atoll(field[0]) << 60) +
+ (atoll(field[1]) << 48) +
+ (atoll(field[2]) << 28) +
+ atoll(field[3]);
+ second = atoll(field[4]);
+}
+
+
+bool ObjectId::operator==(const ObjectId &other) const
+{
+ uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL;
+
+ return first == otherFirst && second == other.second;
+}
+
+bool ObjectId::operator<(const ObjectId &other) const
+{
+ uint64_t otherFirst = agent == 0 ? other.first : other.first & 0xffff000000000000LL;
+
+ return (first < otherFirst) || ((first == otherFirst) && (second < other.second));
+}
+
+void ObjectId::encode(framing::Buffer& buffer)
+{
+ if (agent == 0)
+ buffer.putLongLong(first);
+ else
+ buffer.putLongLong(first | agent->first);
+ buffer.putLongLong(second);
+}
+
+void ObjectId::decode(framing::Buffer& buffer)
+{
+ first = buffer.getLongLong();
+ second = buffer.getLongLong();
+}
+
+namespace qpid {
+namespace management {
+
+std::ostream& operator<<(std::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) <<
+ "-" << (virtFirst & 0x000000000FFFFFFFLL) <<
+ "-" << i.second;
+ return out;
+}
+
+}}
+
+int ManagementObject::maxThreads = 1;
int ManagementObject::nextThreadIndex = 0;
-void ManagementObject::writeTimestamps (Buffer& buf)
+void ManagementObject::writeTimestamps (framing::Buffer& buf)
{
buf.putShortString (getPackageName ());
buf.putShortString (getClassName ());
buf.putBin128 (getMd5Sum ());
- buf.putLongLong (uint64_t (Duration (now ())));
+ buf.putLongLong (updateTime);
buf.putLongLong (createTime);
buf.putLongLong (destroyTime);
- buf.putLongLong (objectId);
+ objectId.encode(buf);
}
-void ManagementObject::setReference(uint64_t) {}
+void ManagementObject::setReference(ObjectId) {}
int ManagementObject::getThreadIndex() {
- static __thread int thisIndex = -1;
+ static QPID_TSS int thisIndex = -1;
if (thisIndex == -1) {
sys::Mutex::ScopedLock mutex(accessLock);
thisIndex = nextThreadIndex;
- if (nextThreadIndex < agent->getMaxThreads() - 1)
+ if (nextThreadIndex < maxThreads - 1)
nextThreadIndex++;
}
return thisIndex;
}
-
diff --git a/cpp/src/qpid/management/ManagementObject.h b/cpp/src/qpid/management/ManagementObject.h
deleted file mode 100644
index 78d065aac2..0000000000
--- a/cpp/src/qpid/management/ManagementObject.h
+++ /dev/null
@@ -1,132 +0,0 @@
-#ifndef _ManagementObject_
-#define _ManagementObject_
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Mutex.h"
-#include <qpid/framing/Buffer.h>
-#include <map>
-
-namespace qpid {
-namespace management {
-
-class Manageable;
-class ManagementAgent;
-
-class ManagementObject
-{
- protected:
-
- uint64_t createTime;
- uint64_t destroyTime;
- uint64_t objectId;
- bool configChanged;
- bool instChanged;
- bool deleted;
- Manageable* coreObject;
- sys::Mutex accessLock;
- ManagementAgent* agent;
- int maxThreads;
-
- static const uint8_t TYPE_U8 = 1;
- static const uint8_t TYPE_U16 = 2;
- static const uint8_t TYPE_U32 = 3;
- static const uint8_t TYPE_U64 = 4;
- static const uint8_t TYPE_SSTR = 6;
- static const uint8_t TYPE_LSTR = 7;
- static const uint8_t TYPE_ABSTIME = 8;
- static const uint8_t TYPE_DELTATIME = 9;
- static const uint8_t TYPE_REF = 10;
- static const uint8_t TYPE_BOOL = 11;
- static const uint8_t TYPE_FLOAT = 12;
- static const uint8_t TYPE_DOUBLE = 13;
- static const uint8_t TYPE_UUID = 14;
- static const uint8_t TYPE_FTABLE = 15;
- static const uint8_t TYPE_S8 = 16;
- static const uint8_t TYPE_S16 = 17;
- static const uint8_t TYPE_S32 = 18;
- static const uint8_t TYPE_S64 = 19;
-
- static const uint8_t ACCESS_RC = 1;
- static const uint8_t ACCESS_RW = 2;
- static const uint8_t ACCESS_RO = 3;
-
- static const uint8_t DIR_I = 1;
- static const uint8_t DIR_O = 2;
- static const uint8_t DIR_IO = 3;
-
- static const uint8_t FLAG_CONFIG = 0x01;
- static const uint8_t FLAG_INDEX = 0x02;
- static const uint8_t FLAG_END = 0x80;
-
- static int nextThreadIndex;
-
- int getThreadIndex();
- void writeTimestamps (qpid::framing::Buffer& buf);
-
- public:
- typedef void (*writeSchemaCall_t) (qpid::framing::Buffer&);
-
- ManagementObject (ManagementAgent* _agent, Manageable* _core) :
- destroyTime(0), objectId (0), configChanged(true),
- instChanged(true), deleted(false), coreObject(_core), agent(_agent)
- { createTime = uint64_t (qpid::sys::Duration (qpid::sys::now ())); }
- virtual ~ManagementObject () {}
-
- virtual writeSchemaCall_t getWriteSchemaCall (void) = 0;
- virtual void writeProperties(qpid::framing::Buffer& buf) = 0;
- virtual void writeStatistics(qpid::framing::Buffer& buf,
- bool skipHeaders = false) = 0;
- virtual void doMethod (std::string methodName,
- qpid::framing::Buffer& inBuf,
- qpid::framing::Buffer& outBuf) = 0;
- virtual void setReference (uint64_t objectId);
-
- virtual std::string& getClassName (void) = 0;
- virtual std::string& getPackageName (void) = 0;
- virtual uint8_t* getMd5Sum (void) = 0;
-
- void setObjectId (uint64_t oid) { objectId = oid; }
- uint64_t getObjectId (void) { return objectId; }
- inline bool getConfigChanged (void) { return configChanged; }
- virtual bool getInstChanged (void) { return instChanged; }
- inline void setAllChanged (void) {
- configChanged = true;
- instChanged = true;
- }
-
- inline void resourceDestroy (void) {
- destroyTime = uint64_t (qpid::sys::Duration (qpid::sys::now ()));
- deleted = true;
- }
- inline bool isDeleted (void) { return deleted; }
- inline sys::Mutex& getLock() { return accessLock; }
-};
-
-typedef std::map<uint64_t,ManagementObject*> ManagementObjectMap;
-
-}}
-
-
-
-#endif /*!_ManagementObject_*/
diff --git a/cpp/src/qpid/messaging/Address.cpp b/cpp/src/qpid/messaging/Address.cpp
new file mode 100644
index 0000000000..ff72f62705
--- /dev/null
+++ b/cpp/src/qpid/messaging/Address.cpp
@@ -0,0 +1,362 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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 {
+
+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::toStr() 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; }
+bool Address::hasSubject() const { return !(impl->subject.empty()); }
+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-properties";
+}
+
+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 = getOption(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;
+}
+
+const Variant& Address::getOption(const std::string& key) const
+{
+ return find(impl->options, key);
+}
+
+std::ostream& operator<<(std::ostream& out, const Address& address)
+{
+ out << address.toStr();
+ return out;
+}
+
+InvalidAddress::InvalidAddress(const std::string& msg) : Exception(msg) {}
+
+MalformedAddress::MalformedAddress(const std::string& msg) : Exception(msg) {}
+
+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;
+ if (readSubject(subject)) {
+ address.setSubject(subject);
+ } else {
+ return error("Expected subject after /");
+ }
+ }
+ 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::readList(Variant& value)
+{
+ if (readChar('[')) {
+ value = Variant::List();
+ Variant item;
+ while (readValue(item)) {
+ value.asList().push_back(item);
+ if (!readChar(',')) break;
+ }
+ return readChar(']') || error("Unmatched '['!");
+ } else {
+ return false;
+ }
+}
+
+bool AddressParser::readMap(Variant& value)
+{
+ if (readChar('{')) {
+ value = Variant::Map();
+ while (readKeyValuePair(value.asMap()) && readChar(',')) {}
+ return readChar('}') || error("Unmatched '{'!");
+ } else {
+ return false;
+ }
+}
+
+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);
+}
+
+bool AddressParser::readValue(Variant& value)
+{
+ return readSimpleValue(value) || readQuotedValue(value) ||
+ readMap(value) || readList(value) || error("Expected 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 = s;
+ try { value = value.asInt64(); return true; } catch (const InvalidConversion&) {}
+ try { value = value.asDouble(); return true; } catch (const InvalidConversion&) {}
+ 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/cpp/src/qpid/messaging/Connection.cpp b/cpp/src/qpid/messaging/Connection.cpp
new file mode 100644
index 0000000000..64ca962317
--- /dev/null
+++ b/cpp/src/qpid/messaging/Connection.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 "qpid/messaging/Connection.h"
+#include "qpid/messaging/ConnectionImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/SessionImpl.h"
+#include "qpid/client/PrivateImplRef.h"
+#include "qpid/client/amqp0_10/ConnectionImpl.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Connection> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Connection Connection::open(const std::string& url, const Variant::Map& options)
+{
+ //only support amqp 0-10 at present
+ Connection connection(new qpid::client::amqp0_10::ConnectionImpl(url, options));
+ return connection;
+}
+
+Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); }
+Connection::Connection(const Connection& c) : qpid::client::Handle<ConnectionImpl>() { PI::copy(*this, c); }
+Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); }
+Connection::~Connection() { PI::dtor(*this); }
+
+void Connection::close() { impl->close(); }
+Session Connection::newSession(const char* name) { return impl->newSession(false, name); }
+Session Connection::newSession(const std::string& name) { return impl->newSession(false, name); }
+Session Connection::newSession(bool transactional, const std::string& name)
+{
+ return impl->newSession(transactional, name);
+}
+Session Connection::getSession(const std::string& name) const { return impl->getSession(name); }
+
+InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {}
+
+void parseKeyValuePair(const std::string& in, Variant::Map& out)
+{
+ std::string::size_type i = in.find('=');
+ if (i == std::string::npos || i == in.size() || in.find('=', i+1) != std::string::npos) {
+ throw InvalidOptionString(QPID_MSG("Cannot parse name-value pair from " << in));
+ } else {
+ out[in.substr(0, i)] = in.substr(i+1);
+ }
+}
+
+void parseOptionString(const std::string& in, Variant::Map& out)
+{
+ std::string::size_type start = 0;
+ std::string::size_type i = in.find('&');
+ while (i != std::string::npos) {
+ parseKeyValuePair(in.substr(start, i-start), out);
+ if (i < in.size()) {
+ start = i+1;
+ i = in.find('&', start);
+ } else {
+ i = std::string::npos;
+ }
+ }
+ parseKeyValuePair(in.substr(start), out);
+}
+
+Variant::Map parseOptionString(const std::string& in)
+{
+ Variant::Map map;
+ parseOptionString(in, map);
+ return map;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/ConnectionImpl.h b/cpp/src/qpid/messaging/ConnectionImpl.h
new file mode 100644
index 0000000000..4eff68ff9d
--- /dev/null
+++ b/cpp/src/qpid/messaging/ConnectionImpl.h
@@ -0,0 +1,46 @@
+#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 client {
+}
+
+namespace messaging {
+
+class Session;
+
+class ConnectionImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ConnectionImpl() {}
+ virtual void close() = 0;
+ virtual Session newSession(bool transactional, const std::string& name) = 0;
+ virtual Session getSession(const std::string& name) const = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_CONNECTIONIMPL_H*/
diff --git a/cpp/src/qpid/messaging/ListContent.cpp b/cpp/src/qpid/messaging/ListContent.cpp
new file mode 100644
index 0000000000..0c3ca5fc62
--- /dev/null
+++ b/cpp/src/qpid/messaging/ListContent.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/messaging/ListContent.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+
+namespace qpid {
+namespace messaging {
+
+class ListContentImpl : public Variant
+{
+ Message* msg;
+ public:
+ ListContentImpl(Message& m) : Variant(Variant::List()), msg(&m)
+ {
+ if (msg->getContent().size()) {
+ qpid::client::amqp0_10::ListCodec codec;
+ codec.decode(msg->getContent(), *this);
+ }
+ }
+
+ void encode()
+ {
+ qpid::client::amqp0_10::ListCodec codec;
+ codec.encode(*this, msg->getContent());
+ }
+};
+
+ListContent::ListContent(Message& m) : impl(new ListContentImpl(m)) {}
+ListContent::~ListContent() { delete impl; }
+ListContent& ListContent::operator=(const ListContent& l) { *impl = *l.impl; return *this; }
+
+ListContent::const_iterator ListContent::begin() const { return impl->asList().begin(); }
+ListContent::const_iterator ListContent::end() const { return impl->asList().end(); }
+ListContent::const_reverse_iterator ListContent::rbegin() const { return impl->asList().rbegin(); }
+ListContent::const_reverse_iterator ListContent::rend() const { return impl->asList().rend(); }
+
+ListContent::iterator ListContent::begin() { return impl->asList().begin(); }
+ListContent::iterator ListContent::end() { return impl->asList().end(); }
+ListContent::reverse_iterator ListContent::rbegin() { return impl->asList().rbegin(); }
+ListContent::reverse_iterator ListContent::rend() { return impl->asList().rend(); }
+
+bool ListContent::empty() const { return impl->asList().empty(); }
+size_t ListContent::size() const { return impl->asList().size(); }
+
+const Variant& ListContent::front() const { return impl->asList().front(); }
+Variant& ListContent::front() { return impl->asList().front(); }
+const Variant& ListContent::back() const { return impl->asList().back(); }
+Variant& ListContent::back() { return impl->asList().back(); }
+
+void ListContent::push_front(const Variant& v) { impl->asList().push_front(v); }
+void ListContent::push_back(const Variant& v) { impl->asList().push_back(v); }
+
+void ListContent::pop_front() { impl->asList().pop_front(); }
+void ListContent::pop_back() { impl->asList().pop_back(); }
+
+ListContent::iterator ListContent::insert(iterator position, const Variant& v)
+{
+ return impl->asList().insert(position, v);
+}
+void ListContent::insert(iterator position, size_t n, const Variant& v)
+{
+ impl->asList().insert(position, n, v);
+}
+ListContent::iterator ListContent::erase(iterator position) { return impl->asList().erase(position); }
+ListContent::iterator ListContent::erase(iterator first, iterator last) { return impl->asList().erase(first, last); }
+void ListContent::clear() { impl->asList().clear(); }
+
+void ListContent::encode() { impl->encode(); }
+
+const Variant::List& ListContent::asList() const { return impl->asList(); }
+Variant::List& ListContent::asList() { return impl->asList(); }
+
+std::ostream& operator<<(std::ostream& out, const ListContent& m)
+{
+ out << m.asList();
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/ListView.cpp b/cpp/src/qpid/messaging/ListView.cpp
new file mode 100644
index 0000000000..b717d157fa
--- /dev/null
+++ b/cpp/src/qpid/messaging/ListView.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/messaging/ListView.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+
+namespace qpid {
+namespace messaging {
+
+class ListViewImpl : public Variant
+{
+ public:
+ ListViewImpl(const Message& msg) : Variant(Variant::List())
+ {
+ if (msg.getContent().size()) {
+ qpid::client::amqp0_10::ListCodec codec;
+ codec.decode(msg.getContent(), *this);
+ }
+ }
+};
+
+ListView::ListView(const Message& m) :impl(new ListViewImpl(m)) {}
+ListView::~ListView() { delete impl; }
+ListView& ListView::operator=(const ListView& l) { *impl = *l.impl; return *this; }
+
+ListView::const_iterator ListView::begin() const { return impl->asList().begin(); }
+ListView::const_iterator ListView::end() const { return impl->asList().end(); }
+ListView::const_reverse_iterator ListView::rbegin() const { return impl->asList().rbegin(); }
+ListView::const_reverse_iterator ListView::rend() const { return impl->asList().rend(); }
+
+bool ListView::empty() const { return impl->asList().empty(); }
+size_t ListView::size() const { return impl->asList().size(); }
+
+const Variant& ListView::front() const { return impl->asList().front(); }
+const Variant& ListView::back() const { return impl->asList().back(); }
+
+const Variant::List& ListView::asList() const { return impl->asList(); }
+
+std::ostream& operator<<(std::ostream& out, const ListView& m)
+{
+ out << m.asList();
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/MapContent.cpp b/cpp/src/qpid/messaging/MapContent.cpp
new file mode 100644
index 0000000000..6dba22be99
--- /dev/null
+++ b/cpp/src/qpid/messaging/MapContent.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/messaging/MapContent.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+
+namespace qpid {
+namespace messaging {
+
+class MapContentImpl : public Variant
+{
+ Message* msg;
+ public:
+ MapContentImpl(Message& m) : Variant(Variant::Map()), msg(&m)
+ {
+ if (msg->getContent().size()) {
+ qpid::client::amqp0_10::MapCodec codec;
+ codec.decode(msg->getContent(), *this);
+ }
+ }
+
+ void encode()
+ {
+ qpid::client::amqp0_10::MapCodec codec;
+ codec.encode(*this, msg->getContent());
+ msg->setContentType(qpid::client::amqp0_10::MapCodec::contentType);
+ }
+};
+
+MapContent::MapContent(Message& m) : impl(new MapContentImpl(m)) {}
+MapContent::~MapContent() { delete impl; }
+MapContent& MapContent::operator=(const MapContent& m) { *impl = *m.impl; return *this; }
+
+MapContent::const_iterator MapContent::begin() const { return impl->asMap().begin(); }
+MapContent::const_iterator MapContent::end() const { return impl->asMap().end(); }
+MapContent::const_reverse_iterator MapContent::rbegin() const { return impl->asMap().rbegin(); }
+MapContent::const_reverse_iterator MapContent::rend() const { return impl->asMap().rend(); }
+MapContent::iterator MapContent::begin() { return impl->asMap().begin(); }
+MapContent::iterator MapContent::end() { return impl->asMap().end(); }
+MapContent::reverse_iterator MapContent::rbegin() { return impl->asMap().rbegin(); }
+MapContent::reverse_iterator MapContent::rend() { return impl->asMap().rend(); }
+
+bool MapContent::empty() const { return impl->asMap().empty(); }
+size_t MapContent::size() const { return impl->asMap().size(); }
+
+MapContent::const_iterator MapContent::find(const key_type& key) const { return impl->asMap().find(key); }
+MapContent::iterator MapContent::find(const key_type& key) { return impl->asMap().find(key); }
+const Variant& MapContent::operator[](const key_type& key) const { return impl->asMap()[key]; }
+Variant& MapContent::operator[](const key_type& key) { return impl->asMap()[key]; }
+
+std::pair<MapContent::iterator,bool> MapContent::insert(const value_type& item) { return impl->asMap().insert(item); }
+MapContent::iterator MapContent::insert(iterator position, const value_type& item) { return impl->asMap().insert(position, item); }
+void MapContent::erase(iterator position) { impl->asMap().erase(position); }
+void MapContent::erase(iterator first, iterator last) { impl->asMap().erase(first, last); }
+size_t MapContent::erase(const key_type& key) { return impl->asMap().erase(key); }
+void MapContent::clear() { impl->asMap().clear(); }
+
+void MapContent::encode() { impl->encode(); }
+
+const std::map<MapContent::key_type, Variant>& MapContent::asMap() const { return impl->asMap(); }
+std::map<MapContent::key_type, Variant>& MapContent::asMap() { return impl->asMap(); }
+
+
+std::ostream& operator<<(std::ostream& out, const MapContent& m)
+{
+ out << m.asMap();
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/MapView.cpp b/cpp/src/qpid/messaging/MapView.cpp
new file mode 100644
index 0000000000..ffa6e91a16
--- /dev/null
+++ b/cpp/src/qpid/messaging/MapView.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/messaging/MapView.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/client/amqp0_10/Codecs.h"
+
+namespace qpid {
+namespace messaging {
+
+class MapViewImpl : public Variant
+{
+ public:
+ MapViewImpl(const Message& msg) : Variant(Variant::Map())
+ {
+ if (msg.getContent().size()) {
+ qpid::client::amqp0_10::MapCodec codec;
+ codec.decode(msg.getContent(), *this);
+ }
+ }
+};
+
+MapView::MapView(const Message& m) : impl(new MapViewImpl(m)) {}
+MapView::~MapView() { delete impl; }
+MapView& MapView::operator=(const MapView& m) { *impl = *m.impl; return *this; }
+
+MapView::const_iterator MapView::begin() const { return impl->asMap().begin(); }
+MapView::const_iterator MapView::end() const { return impl->asMap().end(); }
+MapView::const_reverse_iterator MapView::rbegin() const { return impl->asMap().rbegin(); }
+MapView::const_reverse_iterator MapView::rend() const { return impl->asMap().rend(); }
+
+bool MapView::empty() const { return impl->asMap().empty(); }
+size_t MapView::size() const { return impl->asMap().size(); }
+
+MapView::const_iterator MapView::find(const key_type& key) const { return impl->asMap().find(key); }
+const Variant& MapView::operator[](const key_type& key) const { return impl->asMap()[key]; }
+
+const std::map<MapView::key_type, Variant>& MapView::asMap() const { return impl->asMap(); }
+
+std::ostream& operator<<(std::ostream& out, const MapView& m)
+{
+ out << m.asMap();
+ return out;
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/Message.cpp b/cpp/src/qpid/messaging/Message.cpp
new file mode 100644
index 0000000000..deb40b6aa3
--- /dev/null
+++ b/cpp/src/qpid/messaging/Message.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/Message.h"
+#include "qpid/messaging/MessageImpl.h"
+
+namespace qpid {
+namespace messaging {
+
+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(); }
+
+const VariantMap& Message::getHeaders() const { return impl->getHeaders(); }
+VariantMap& Message::getHeaders() { return impl->getHeaders(); }
+
+void Message::setContent(const std::string& c) { impl->setBytes(c); }
+void Message::setContent(const char* chars, size_t count) { impl->setBytes(chars, count); }
+const std::string& Message::getContent() const { return impl->getBytes(); }
+std::string& Message::getContent() { return impl->getBytes(); }
+
+void Message::getContent(std::pair<const char*, size_t>& content) const
+{
+ content.first = impl->getBytes().data();
+ content.second = impl->getBytes().size();
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/MessageImpl.cpp b/cpp/src/qpid/messaging/MessageImpl.cpp
new file mode 100644
index 0000000000..e17fccd64f
--- /dev/null
+++ b/cpp/src/qpid/messaging/MessageImpl.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 "MessageImpl.h"
+#include "qpid/messaging/Message.h"
+
+namespace qpid {
+namespace messaging {
+
+namespace {
+const std::string EMPTY_STRING = "";
+}
+
+MessageImpl::MessageImpl(const std::string& c) : bytes(c), internalId(0) {}
+MessageImpl::MessageImpl(const char* chars, size_t count) : 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 VariantMap& MessageImpl::getHeaders() const { return headers; }
+VariantMap& MessageImpl::getHeaders() { return headers; }
+
+//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/cpp/src/qpid/messaging/MessageImpl.h b/cpp/src/qpid/messaging/MessageImpl.h
new file mode 100644
index 0000000000..4939cdc5cc
--- /dev/null
+++ b/cpp/src/qpid/messaging/MessageImpl.h
@@ -0,0 +1,82 @@
+#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/messaging/Variant.h"
+#include "qpid/framing/SequenceNumber.h"
+
+namespace qpid {
+namespace messaging {
+
+struct MessageImpl
+{
+ Address replyTo;
+ std::string subject;
+ std::string contentType;
+ 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 Variant::Map& getHeaders() const;
+ Variant::Map& getHeaders();
+
+ 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/cpp/src/qpid/messaging/Receiver.cpp b/cpp/src/qpid/messaging/Receiver.cpp
new file mode 100644
index 0000000000..bf9c056db8
--- /dev/null
+++ b/cpp/src/qpid/messaging/Receiver.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/messaging/Receiver.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/ReceiverImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Receiver> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); }
+Receiver::Receiver(const Receiver& s) : qpid::client::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, qpid::sys::Duration timeout) { return impl->get(message, timeout); }
+Message Receiver::get(qpid::sys::Duration timeout) { return impl->get(timeout); }
+bool Receiver::fetch(Message& message, qpid::sys::Duration timeout) { return impl->fetch(message, timeout); }
+Message Receiver::fetch(qpid::sys::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::available() { return impl->available(); }
+uint32_t Receiver::pendingAck() { return impl->pendingAck(); }
+void Receiver::cancel() { impl->cancel(); }
+const std::string& Receiver::getName() const { return impl->getName(); }
+Session Receiver::getSession() const { return impl->getSession(); }
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/ReceiverImpl.h b/cpp/src/qpid/messaging/ReceiverImpl.h
new file mode 100644
index 0000000000..447a505518
--- /dev/null
+++ b/cpp/src/qpid/messaging/ReceiverImpl.h
@@ -0,0 +1,55 @@
+#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"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace client {
+}
+
+namespace messaging {
+
+class Message;
+class MessageListener;
+class Session;
+
+class ReceiverImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~ReceiverImpl() {}
+ virtual bool get(Message& message, qpid::sys::Duration timeout) = 0;
+ virtual Message get(qpid::sys::Duration timeout) = 0;
+ virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0;
+ virtual Message fetch(qpid::sys::Duration timeout) = 0;
+ virtual void setCapacity(uint32_t) = 0;
+ virtual uint32_t getCapacity() = 0;
+ virtual uint32_t available() = 0;
+ virtual uint32_t pendingAck() = 0;
+ virtual void cancel() = 0;
+ virtual const std::string& getName() const = 0;
+ virtual Session getSession() const = 0;
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_RECEIVERIMPL_H*/
diff --git a/cpp/src/qpid/messaging/Sender.cpp b/cpp/src/qpid/messaging/Sender.cpp
new file mode 100644
index 0000000000..f2303f4126
--- /dev/null
+++ b/cpp/src/qpid/messaging/Sender.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/messaging/Sender.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/SenderImpl.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Sender> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); }
+Sender::Sender(const Sender& s) : qpid::client::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) { impl->send(message); }
+void Sender::cancel() { impl->cancel(); }
+void Sender::setCapacity(uint32_t c) { impl->setCapacity(c); }
+uint32_t Sender::getCapacity() { return impl->getCapacity(); }
+uint32_t Sender::pending() { return impl->pending(); }
+const std::string& Sender::getName() const { return impl->getName(); }
+Session Sender::getSession() const { return impl->getSession(); }
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/client/MessageListener.h b/cpp/src/qpid/messaging/SenderImpl.h
index 76e1d17445..5f30417f6a 100644
--- a/cpp/src/qpid/client/MessageListener.h
+++ b/cpp/src/qpid/messaging/SenderImpl.h
@@ -1,3 +1,6 @@
+#ifndef QPID_MESSAGING_SENDERIMPL_H
+#define QPID_MESSAGING_SENDERIMPL_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,36 +21,30 @@
* under the License.
*
*/
-#include <string>
-
-#ifndef _MessageListener_
-#define _MessageListener_
-
-#include "Message.h"
+#include "qpid/RefCounted.h"
namespace qpid {
namespace client {
-
- /**
- * Implement a subclass of MessageListener and subscribe it using
- * the SubscriptionManager to receive messages.
- *
- * Another way to receive messages is by using a LocalQueue.
- *
- * \ingroup clientapi
- */
- class MessageListener{
- public:
- virtual ~MessageListener();
-
- /** Called for each message arriving from the broker. Override
- * in your own subclass to process messages.
- */
- virtual void received(Message& msg) = 0;
- };
-
}
-}
-
-#endif
+namespace messaging {
+
+class Message;
+class Session;
+
+class SenderImpl : public virtual qpid::RefCounted
+{
+ public:
+ virtual ~SenderImpl() {}
+ virtual void send(const Message& message) = 0;
+ virtual void cancel() = 0;
+ virtual void setCapacity(uint32_t) = 0;
+ virtual uint32_t getCapacity() = 0;
+ virtual uint32_t pending() = 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/cpp/src/qpid/messaging/Session.cpp b/cpp/src/qpid/messaging/Session.cpp
new file mode 100644
index 0000000000..99896caad4
--- /dev/null
+++ b/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/client/PrivateImplRef.h"
+
+namespace qpid {
+namespace client {
+
+typedef PrivateImplRef<qpid::messaging::Session> PI;
+
+}
+
+namespace messaging {
+
+using qpid::client::PI;
+
+Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); }
+Session::Session(const Session& s) : qpid::client::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() { impl->acknowledge(); }
+void Session::reject(Message& m) { impl->reject(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()
+{
+ impl->sync();
+}
+
+void Session::flush()
+{
+ impl->flush();
+}
+
+bool Session::nextReceiver(Receiver& receiver, qpid::sys::Duration timeout)
+{
+ return impl->nextReceiver(receiver, timeout);
+}
+
+
+Receiver Session::nextReceiver(qpid::sys::Duration timeout)
+{
+ return impl->nextReceiver(timeout);
+}
+
+uint32_t Session::available() { return impl->available(); }
+uint32_t Session::pendingAck() { return impl->pendingAck(); }
+
+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();
+}
+
+KeyError::KeyError(const std::string& msg) : Exception(msg) {}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/messaging/SessionImpl.h b/cpp/src/qpid/messaging/SessionImpl.h
new file mode 100644
index 0000000000..164a6f6bc9
--- /dev/null
+++ b/cpp/src/qpid/messaging/SessionImpl.h
@@ -0,0 +1,64 @@
+#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/sys/Time.h"
+
+namespace qpid {
+namespace client {
+}
+
+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() = 0;
+ virtual void reject(Message&) = 0;
+ virtual void close() = 0;
+ virtual void sync() = 0;
+ virtual void flush() = 0;
+ virtual Sender createSender(const Address& address) = 0;
+ virtual Receiver createReceiver(const Address& address) = 0;
+ virtual bool nextReceiver(Receiver& receiver, qpid::sys::Duration timeout) = 0;
+ virtual Receiver nextReceiver(qpid::sys::Duration timeout) = 0;
+ virtual uint32_t available() = 0;
+ virtual uint32_t pendingAck() = 0;
+ virtual Sender getSender(const std::string& name) const = 0;
+ virtual Receiver getReceiver(const std::string& name) const = 0;
+ virtual Connection getConnection() const = 0;
+ private:
+};
+}} // namespace qpid::messaging
+
+#endif /*!QPID_MESSAGING_SESSIONIMPL_H*/
diff --git a/cpp/src/qpid/messaging/Variant.cpp b/cpp/src/qpid/messaging/Variant.cpp
new file mode 100644
index 0000000000..9c2f92f47a
--- /dev/null
+++ b/cpp/src/qpid/messaging/Variant.cpp
@@ -0,0 +1,659 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/Variant.h"
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <algorithm>
+#include <sstream>
+
+namespace qpid {
+namespace messaging {
+
+InvalidConversion::InvalidConversion(const std::string& msg) : Exception(msg) {}
+
+
+namespace {
+std::string EMPTY;
+}
+
+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&);
+ VariantImpl(const Variant::Map&);
+ VariantImpl(const Variant::List&);
+ ~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;
+
+ 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
+
+ std::string getTypeName(VariantType type) const;
+ template<class T> T convertFromString() const
+ {
+ std::string* s = reinterpret_cast<std::string*>(value.v);
+ try {
+ return boost::lexical_cast<T>(*s);
+ } catch(const boost::bad_lexical_cast&) {
+ 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) : type(VAR_STRING) { 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() {
+ 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;
+ 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_STRING: return convertFromString<uint8_t>();
+ default: 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_STRING: return convertFromString<uint16_t>();
+ default: 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_STRING: return convertFromString<uint32_t>();
+ default: 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_STRING: return convertFromString<uint64_t>();
+ default: 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_STRING: return convertFromString<int8_t>();
+ default: 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_STRING: return convertFromString<int16_t>();
+ default: 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_STRING: return convertFromString<int32_t>();
+ default: 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_STRING: return convertFromString<int64_t>();
+ default: 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_LIST: return toString(asList());
+ case VAR_MAP: return toString(asMap());
+ default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_STRING)));
+ }
+}
+
+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_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 VariantImpl::getTypeName(VariantType type) const
+{
+ 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";
+ }
+ 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());
+ case VAR_MAP: return new VariantImpl(v.asMap());
+ case VAR_LIST: return new VariantImpl(v.asList());
+ default: return new VariantImpl();
+ }
+}
+
+Variant::Variant() : impl(new VariantImpl()) {}
+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() { if (impl) delete impl; }
+
+void Variant::reset()
+{
+ if (impl) delete impl;
+ impl = new VariantImpl();
+}
+
+
+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 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;
+}
+
+VariantType Variant::getType() const { return impl->getType(); }
+bool Variant::isVoid() const { return impl->getType() == VAR_VOID; }
+bool Variant::asBool() const { return impl->asBool(); }
+uint8_t Variant::asUint8() const { return impl->asUint8(); }
+uint16_t Variant::asUint16() const { return impl->asUint16(); }
+uint32_t Variant::asUint32() const { return impl->asUint32(); }
+uint64_t Variant::asUint64() const { return impl->asUint64(); }
+int8_t Variant::asInt8() const { return impl->asInt8(); }
+int16_t Variant::asInt16() const { return impl->asInt16(); }
+int32_t Variant::asInt32() const { return impl->asInt32(); }
+int64_t Variant::asInt64() const { return impl->asInt64(); }
+float Variant::asFloat() const { return impl->asFloat(); }
+double Variant::asDouble() const { return impl->asDouble(); }
+std::string Variant::asString() const { return impl->asString(); }
+const Variant::Map& Variant::asMap() const { return impl->asMap(); }
+Variant::Map& Variant::asMap() { return impl->asMap(); }
+const Variant::List& Variant::asList() const { return impl->asList(); }
+Variant::List& Variant::asList() { return impl->asList(); }
+const std::string& Variant::getString() const { return impl->getString(); }
+std::string& Variant::getString() { return impl->getString(); }
+void Variant::setEncoding(const std::string& s) { impl->setEncoding(s); }
+const std::string& Variant::getEncoding() const { return impl->getEncoding(); }
+
+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 const char*() const { return asString().c_str(); }
+
+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->isEqualTo(*other.impl);
+}
+
+}} // namespace qpid::messaging
diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/cpp/src/qpid/replication/ReplicatingEventListener.cpp
new file mode 100644
index 0000000000..b7d52372f4
--- /dev/null
+++ b/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/cpp/src/qpid/replication/ReplicatingEventListener.h b/cpp/src/qpid/replication/ReplicatingEventListener.h
new file mode 100644
index 0000000000..74418d00e6
--- /dev/null
+++ b/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/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp
new file mode 100644
index 0000000000..b5911bb71e
--- /dev/null
+++ b/cpp/src/qpid/replication/ReplicationExchange.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 "qpid/replication/ReplicationExchange.h"
+#include "qpid/replication/constants.h"
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.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/cpp/src/qpid/replication/ReplicationExchange.h b/cpp/src/qpid/replication/ReplicationExchange.h
new file mode 100644
index 0000000000..f0252448f9
--- /dev/null
+++ b/cpp/src/qpid/replication/ReplicationExchange.h
@@ -0,0 +1,67 @@
+#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 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(qpid::broker::Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ bool unbind(qpid::broker::Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args);
+ bool isBound(qpid::broker::Queue::shared_ptr 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/cpp/src/qpid/replication/constants.h b/cpp/src/qpid/replication/constants.h
new file mode 100644
index 0000000000..c5ba7d3d6a
--- /dev/null
+++ b/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/cpp/src/qpid/shared_ptr.h b/cpp/src/qpid/shared_ptr.h
deleted file mode 100644
index 0c933ea6a6..0000000000
--- a/cpp/src/qpid/shared_ptr.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef _common_shared_ptr_h
-#define _common_shared_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 <boost/shared_ptr.hpp>
-#include <boost/cast.hpp>
-
-namespace qpid {
-
-// Import shared_ptr definitions into qpid namespace and define some
-// useful shared_ptr templates for convenience.
-
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using boost::static_pointer_cast;
-using boost::const_pointer_cast;
-using boost::shared_polymorphic_downcast;
-
-template <class T> shared_ptr<T> make_shared_ptr(T* ptr) {
- return shared_ptr<T>(ptr);
-}
-
-template <class T, class D>
-shared_ptr<T> make_shared_ptr(T* ptr, D deleter) {
- return shared_ptr<T>(ptr, deleter);
-}
-
-inline void nullDeleter(void const *) {}
-
-} // namespace qpid
-
-
-
-#endif /*!_common_shared_ptr_h*/
diff --git a/cpp/src/qpid/store/CMakeLists.txt b/cpp/src/qpid/store/CMakeLists.txt
new file mode 100644
index 0000000000..0d25923175
--- /dev/null
+++ b/cpp/src/qpid/store/CMakeLists.txt
@@ -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.
+#
+
+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/State.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)
diff --git a/cpp/src/qpid/store/MessageStorePlugin.cpp b/cpp/src/qpid/store/MessageStorePlugin.cpp
new file mode 100644
index 0000000000..05b6ef4465
--- /dev/null
+++ b/cpp/src/qpid/store/MessageStorePlugin.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 "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* broker =
+ dynamic_cast<qpid::broker::Broker*>(&target);
+ if (0 == broker)
+ return; // Only listen to Broker targets
+
+ // 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;
+
+ provider->second->recoverConfigs(recoverer);
+ provider->second->recoverExchanges(recoverer, exchanges);
+ provider->second->recoverQueues(recoverer, queues);
+ provider->second->recoverBindings(recoverer, exchanges, queues);
+ provider->second->recoverMessages(recoverer, messages, messageQueueMap);
+ // 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<uint64_t>::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);
+ if (iQ == queues.end()) {
+ std::ostringstream oss;
+ oss << "No matching queue trying to re-enqueue message "
+ << " on queue Id " << *j;
+ THROW_STORE_EXCEPTION(oss.str());
+ }
+ iQ->second->recover(msg);
+ }
+ }
+
+ // recoverTransactions() and apply correctly while re-enqueuing
+}
+
+}} // namespace qpid::store
diff --git a/cpp/src/qpid/store/MessageStorePlugin.h b/cpp/src/qpid/store/MessageStorePlugin.h
new file mode 100644
index 0000000000..529a59401e
--- /dev/null
+++ b/cpp/src/qpid/store/MessageStorePlugin.h
@@ -0,0 +1,284 @@
+#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() {}
+
+ /**
+ * @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; }
+
+ 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;
+
+}; // class MessageStoreImpl
+
+} // namespace msgstore
+} // namespace mrg
+
+#endif /* QPID_SERIALIZER_H */
diff --git a/cpp/src/qpid/store/StorageProvider.h b/cpp/src/qpid/store/StorageProvider.h
new file mode 100644
index 0000000000..1f257e7416
--- /dev/null
+++ b/cpp/src/qpid/store/StorageProvider.h
@@ -0,0 +1,324 @@
+#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 Ids where message is queued
+typedef std::map<uint64_t, std::vector<uint64_t> > MessageQueueMap;
+
+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;
+ //@}
+};
+
+}} // namespace qpid::store
+
+#endif /* QPID_STORE_STORAGEPROVIDER_H */
diff --git a/cpp/src/qpid/store/StoreException.h b/cpp/src/qpid/store/StoreException.h
new file mode 100644
index 0000000000..1dc7f670ec
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp b/cpp/src/qpid/store/ms-sql/AmqpTransaction.cpp
new file mode 100644
index 0000000000..0ecfacfb4b
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/AmqpTransaction.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 "AmqpTransaction.h"
+#include "DatabaseConnection.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+AmqpTransaction::AmqpTransaction(std::auto_ptr<DatabaseConnection>& _db)
+ : db(_db), transDepth(0)
+{
+}
+
+AmqpTransaction::~AmqpTransaction()
+{
+ if (transDepth > 0)
+ this->abort();
+}
+
+void
+AmqpTransaction::begin()
+{
+ _bstr_t beginCmd("BEGIN TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(beginCmd, NULL, adExecuteNoRecords);
+ ++transDepth;
+}
+
+void
+AmqpTransaction::commit()
+{
+ if (transDepth > 0) {
+ _bstr_t commitCmd("COMMIT TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(commitCmd, NULL, adExecuteNoRecords);
+ --transDepth;
+ }
+}
+
+void
+AmqpTransaction::abort()
+{
+ if (transDepth > 0) {
+ _bstr_t rollbackCmd("ROLLBACK TRANSACTION");
+ _ConnectionPtr c = *db;
+ c->Execute(rollbackCmd, NULL, adExecuteNoRecords);
+ transDepth = 0;
+ }
+}
+
+AmqpTPCTransaction::AmqpTPCTransaction(std::auto_ptr<DatabaseConnection>& _db,
+ const std::string& _xid)
+ : AmqpTransaction(_db), xid(_xid)
+{
+}
+
+AmqpTPCTransaction::~AmqpTPCTransaction()
+{
+}
+
+void
+AmqpTPCTransaction::prepare()
+{
+ // Intermediate transactions should have already assured integrity of
+ // the content in the database; just waiting to pull the trigger on the
+ // outermost transaction.
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/cpp/src/qpid/store/ms-sql/AmqpTransaction.h b/cpp/src/qpid/store/ms-sql/AmqpTransaction.h
new file mode 100644
index 0000000000..9b87d0ae15
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/AmqpTransaction.h
@@ -0,0 +1,84 @@
+#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 <string>
+#include <memory>
+
+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 {
+
+ std::auto_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:
+ AmqpTransaction(std::auto_ptr<DatabaseConnection>& _db);
+ virtual ~AmqpTransaction();
+
+ DatabaseConnection *dbConn() { return db.get(); }
+
+ void begin();
+ void commit();
+ void abort();
+};
+
+/**
+ * @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 {
+ std::string xid;
+
+public:
+ AmqpTPCTransaction(std::auto_ptr<DatabaseConnection>& _db,
+ const std::string& _xid);
+ virtual ~AmqpTPCTransaction();
+
+ void prepare();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_AMQPTRANSACTION_H */
diff --git a/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp b/cpp/src/qpid/store/ms-sql/BindingRecordset.cpp
new file mode 100644
index 0000000000..1dc4370312
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BindingRecordset.h b/cpp/src/qpid/store/ms-sql/BindingRecordset.h
new file mode 100644
index 0000000000..3cb732de75
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp b/cpp/src/qpid/store/ms-sql/BlobAdapter.cpp
new file mode 100644
index 0000000000..1889f34e41
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobAdapter.h b/cpp/src/qpid/store/ms-sql/BlobAdapter.h
new file mode 100644
index 0000000000..1c666392bc
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp b/cpp/src/qpid/store/ms-sql/BlobEncoder.cpp
new file mode 100644
index 0000000000..75d3dc2d86
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobEncoder.h b/cpp/src/qpid/store/ms-sql/BlobEncoder.h
new file mode 100644
index 0000000000..d2b56223c1
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp b/cpp/src/qpid/store/ms-sql/BlobRecordset.cpp
new file mode 100644
index 0000000000..ef1757dbad
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/BlobRecordset.h b/cpp/src/qpid/store/ms-sql/BlobRecordset.h
new file mode 100644
index 0000000000..4d1c338746
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp b/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp
new file mode 100644
index 0000000000..34edec8acd
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/DatabaseConnection.cpp
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#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;
+}
+
+}}} // namespace qpid::store::ms_sql
diff --git a/cpp/src/qpid/store/ms-sql/DatabaseConnection.h b/cpp/src/qpid/store/ms-sql/DatabaseConnection.h
new file mode 100644
index 0000000000..2b8bbffa90
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/DatabaseConnection.h
@@ -0,0 +1,62 @@
+#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(); }
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_DATABASECONNECTION_H */
diff --git a/cpp/src/qpid/store/ms-sql/Exception.h b/cpp/src/qpid/store/ms-sql/Exception.h
new file mode 100644
index 0000000000..0da4b24210
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/Exception.h
@@ -0,0 +1,62 @@
+#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)
+ : 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();
+ }
+ }
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_EXCEPTION_H */
diff --git a/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp b/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
new file mode 100644
index 0000000000..a26df59df7
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/MSSqlProvider.cpp
@@ -0,0 +1,1061 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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 "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");
+}
+
+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);
+
+ // @TODO This maybe should not be in TransactionalStore
+ 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);
+
+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(_ConnectionPtr conn, 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(conn, 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error creating queue " + queue.getName(), e);
+ }
+}
+
+/**
+ * Destroy a durable queue
+ */
+void
+MSSqlProvider::destroy(PersistableQueue& queue)
+{
+ // MessageDeleter class for use with for_each, below.
+ class MessageDeleter {
+ BlobRecordset& msgs;
+ public:
+ explicit MessageDeleter(BlobRecordset& _msgs) : msgs(_msgs) {}
+ void operator()(uint64_t msgId) { msgs.remove(msgId); }
+ };
+
+ 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, those messages can
+ // also be deleted. Lastly, the queue record can be removed.
+ rsBindings.removeForQueue(queue.getPersistenceId());
+ std::vector<uint64_t> orphans;
+ rsMessageMaps.removeForQueue(queue.getPersistenceId(), orphans);
+ std::for_each(orphans.begin(), orphans.end(),
+ MessageDeleter(rsMessages));
+ rsQueues.remove(queue);
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ db->rollbackTransaction();
+ throw ADOException("Error deleting queue " + queue.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error creating exchange " + exchange.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error deleting exchange " + exchange.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error binding exchange " + exchange.getName() +
+ " to queue " + queue.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error unbinding exchange " + exchange.getName() +
+ " from queue " + queue.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error creating config " + config.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error deleting config " + config.getName(), e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error staging message", e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error deleting message", e);
+ }
+}
+
+/**
+ * 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) {
+ db->rollbackTransaction();
+ throw ADOException("Error appending to message", e);
+ }
+}
+
+/**
+ * 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) {
+ throw ADOException("Error loading message content", e);
+ }
+}
+
+/**
+ * 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;
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (ctxt);
+ if (atxn == 0) {
+ db = initConnection();
+ db->beginTransaction();
+ }
+ else {
+ (void)initState(); // Ensure this thread is initialized
+ db = atxn->dbConn();
+ try {
+ atxn->begin();
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error queuing message", e);
+ }
+ }
+
+ 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());
+ if (atxn)
+ atxn->commit();
+ else
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ if (atxn)
+ atxn->abort();
+ else
+ db->rollbackTransaction();
+ throw ADOException("Error queuing message", e);
+ }
+ 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;
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (ctxt);
+ if (atxn == 0) {
+ db = initConnection();
+ db->beginTransaction();
+ }
+ else {
+ (void)initState(); // Ensure this thread is initialized
+ db = atxn->dbConn();
+ try {
+ atxn->begin();
+ }
+ catch(_com_error &e) {
+ throw ADOException("Error queuing message", e);
+ }
+ }
+
+ MessageMapRecordset rsMap;
+ MessageRecordset rsMessages;
+ try {
+ rsMap.open(db, TblMessageMap);
+ bool more = rsMap.remove(msg->getPersistenceId(),
+ queue.getPersistenceId());
+ if (!more) {
+ rsMessages.open(db, TblMessage);
+ rsMessages.remove(msg);
+ }
+ if (atxn)
+ atxn->commit();
+ else
+ db->commitTransaction();
+ }
+ catch(_com_error &e) {
+ if (atxn)
+ atxn->abort();
+ else
+ db->rollbackTransaction();
+ throw ADOException("Error dequeuing message", e);
+ }
+ 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.
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ std::auto_ptr<AmqpTransaction> tx(new AmqpTransaction(db));
+ tx->begin();
+ 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
+ std::auto_ptr<DatabaseConnection> db(new DatabaseConnection);
+ db->open(options.connectString, options.catalogName);
+ std::auto_ptr<AmqpTPCTransaction> tx(new AmqpTPCTransaction(db, xid));
+ tx->begin();
+ std::auto_ptr<qpid::broker::TPCTransactionContext> tc(tx);
+ return tc;
+}
+
+void
+MSSqlProvider::prepare(qpid::broker::TPCTransactionContext& txn)
+{
+ // The inner transactions used for the components of the TPC are done;
+ // nothing else to do but wait for the commit.
+}
+
+void
+MSSqlProvider::commit(qpid::broker::TransactionContext& txn)
+{
+ (void)initState(); // Ensure this thread is initialized
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (&txn);
+ if (atxn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ atxn->commit();
+}
+
+void
+MSSqlProvider::abort(qpid::broker::TransactionContext& txn)
+{
+ (void)initState(); // Ensure this thread is initialized
+ AmqpTransaction *atxn = dynamic_cast<AmqpTransaction*> (&txn);
+ if (atxn == 0)
+ throw qpid::broker::InvalidTransactionContextException();
+ atxn->abort();
+}
+
+// @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 = 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
+MSSqlProvider::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
+MSSqlProvider::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
+MSSqlProvider::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
+MSSqlProvider::recoverMessages(qpid::broker::RecoveryManager& recoverer,
+ MessageMap& messageMap,
+ MessageQueueMap& messageQueueMap)
+{
+ DatabaseConnection *db = initConnection();
+ MessageRecordset rsMessages;
+ rsMessages.open(db, TblMessage);
+ rsMessages.recover(recoverer, messageMap);
+
+ MessageMapRecordset rsMessageMaps;
+ rsMessageMaps.open(db, TblMessageMap);
+ rsMessageMaps.recover(messageQueueMap);
+}
+
+////////////// 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(_ConnectionPtr conn, 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)";
+ _variant_t unused;
+ _bstr_t dbStr = dbCmd.c_str();
+ 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 + TblMessageMap + messageMapSpecs;
+ makeTableStr = makeTable.c_str();
+ conn->Execute(makeTableStr, &unused, adExecuteNoRecords);
+ }
+ catch(_com_error &e) {
+ throw ADOException("MSSQL can't create " + name, e);
+ }
+}
+
+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/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp b/cpp/src/qpid/store/ms-sql/MessageMapRecordset.cpp
new file mode 100644
index 0000000000..68e174a2b0
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/MessageMapRecordset.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/Exception.h>
+#include <qpid/log/Statement.h>
+#include <qpid/store/StorageProvider.h>
+
+#include "MessageMapRecordset.h"
+#include "VariantHelper.h"
+
+namespace qpid {
+namespace store {
+namespace ms_sql {
+
+void
+MessageMapRecordset::add(uint64_t messageId, uint64_t queueId)
+{
+ rs->AddNew();
+ rs->Fields->GetItem("messageId")->Value = messageId;
+ rs->Fields->GetItem("queueId")->Value = queueId;
+ rs->Update();
+}
+
+bool
+MessageMapRecordset::remove(uint64_t messageId, uint64_t queueId)
+{
+ // Look up all mappings for the specified message. Then scan
+ // for the specified queue and keep track of whether or not the
+ // message exists on any queue we are not looking for a well.
+ std::ostringstream filter;
+ filter << "messageId = " << messageId << std::ends;
+ rs->PutFilter (VariantHelper<std::string>(filter.str()));
+ if (rs->RecordCount == 0)
+ return false;
+ MessageMap m;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&m);
+ bool moreEntries = false, deleted = false;
+ rs->MoveFirst();
+ // If the desired mapping gets deleted, and we already know there are
+ // other mappings for the message, don't bother finishing the scan.
+ while (!rs->EndOfFile && !(deleted && moreEntries)) {
+ if (m.queueId == queueId) {
+ rs->Delete(adAffectCurrent);
+ rs->Update();
+ deleted = true;
+ }
+ else {
+ moreEntries = true;
+ }
+ rs->MoveNext();
+ }
+ piAdoRecordBinding->Release();
+ rs->Filter = "";
+ return moreEntries;
+}
+
+void
+MessageMapRecordset::removeForQueue(uint64_t queueId,
+ std::vector<uint64_t>& orphaned)
+{
+ // Read all the messages queued on queueId and add them to the orphaned
+ // list. Then remove each one and learn if there are references to it
+ // from other queues. The ones without references are left in the
+ // orphaned list, others are removed.
+ std::ostringstream filter;
+ filter << "queueId = " << queueId << std::ends;
+ rs->PutFilter (VariantHelper<std::string>(filter.str()));
+ MessageMap m;
+ IADORecordBinding *piAdoRecordBinding;
+ rs->QueryInterface(__uuidof(IADORecordBinding),
+ (LPVOID *)&piAdoRecordBinding);
+ piAdoRecordBinding->BindToRecordset(&m);
+ while (!rs->EndOfFile) {
+ orphaned.push_back(m.messageId);
+ rs->MoveNext();
+ }
+ piAdoRecordBinding->Release();
+ rs->Filter = ""; // Remove filter on queueId
+ rs->Requery(adOptionUnspecified); // Get the entire map again
+
+ // Now delete all the messages on this queue; any message that still has
+ // references from other queue(s) is removed from orphaned.
+ for (std::vector<uint64_t>::iterator i = orphaned.begin();
+ i != orphaned.end();
+ ) {
+ if (remove(*i, queueId))
+ i = orphaned.erase(i); // There are other refs to message *i
+ else
+ ++i;
+ }
+}
+
+void
+MessageMapRecordset::recover(MessageQueueMap& msgMap)
+{
+ 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) {
+ msgMap[b.messageId].push_back(b.queueId);
+ rs->MoveNext();
+ }
+
+ piAdoRecordBinding->Release();
+}
+
+void
+MessageMapRecordset::dump()
+{
+ 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/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h b/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h
new file mode 100644
index 0000000000..475137b18b
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/MessageMapRecordset.h
@@ -0,0 +1,77 @@
+#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 <vector>
+#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 {
+
+ class MessageMap : public CADORecordBinding {
+ BEGIN_ADO_BINDING(MessageMap)
+ ADO_FIXED_LENGTH_ENTRY2(1, adBigInt, messageId, FALSE)
+ ADO_FIXED_LENGTH_ENTRY2(2, adBigInt, queueId, FALSE)
+ END_ADO_BINDING()
+
+ public:
+ uint64_t messageId;
+ uint64_t queueId;
+ };
+
+public:
+ // Add a new mapping
+ void add(uint64_t messageId, uint64_t queueId);
+
+ // Remove a specific mapping. Returns true if the message is still
+ // enqueued on at least one other queue; false if the message no longer
+ // exists on any other queues.
+ bool remove(uint64_t messageId, uint64_t queueId);
+
+ // Remove mappings for all messages on a specified queue. If there are
+ // messages that were only on the specified queue and are, therefore,
+ // orphaned now, return them in the orphaned vector. The orphaned
+ // messages can be deleted permanently as they are not referenced on
+ // any other queues.
+ void removeForQueue(uint64_t queueId, std::vector<uint64_t>& orphaned);
+
+ // 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/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp b/cpp/src/qpid/store/ms-sql/MessageRecordset.cpp
new file mode 100644
index 0000000000..b62a333df6
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/MessageRecordset.h b/cpp/src/qpid/store/ms-sql/MessageRecordset.h
new file mode 100644
index 0000000000..698b2561fe
--- /dev/null
+++ b/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/cpp/src/qpid/store/ms-sql/Recordset.cpp b/cpp/src/qpid/store/ms-sql/Recordset.cpp
new file mode 100644
index 0000000000..e1a5158c87
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/Recordset.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/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 {
+
+#if 0
+Recordset::Iterator::Iterator(Recordset& _rs) : rs(_rs)
+{
+ rs->MoveFirst();
+ setCurrent();
+}
+
+std::pair<uint64_t, BlobAdapter>&
+Recordset::Iterator::dereference() const
+{
+ return const_cast<std::pair<uint64_t, BlobAdapter> >(current);
+}
+
+void
+Recordset::Iterator::increment()
+{
+ rs->MoveNext();
+ setCurrent();
+}
+
+bool
+Recordset::Iterator::equal(const Iterator& x) const
+{
+ return current.first == x.current.first;
+}
+
+void
+Recordset::Iterator::setCurrent()
+{
+ if (!rs->EndOfFile) {
+ uint64_t id = rs->Fields->Item["persistenceId"]->Value;
+ long blobSize = rs->Fields->Item["fieldTableBlob"]->ActualSize;
+ BlobAdapter blob(blobSize);
+ blob = rs->Fields->Item["fieldTableBlob"]->GetChunk(blobSize);
+ current = std::make_pair(id, blob);
+ }
+ else {
+ current.first = 0;
+ }
+}
+#endif
+
+void
+Recordset::open(DatabaseConnection* conn, const std::string& table)
+{
+ _ConnectionPtr p = *conn;
+ TESTHR(rs.CreateInstance(__uuidof(::Recordset)));
+ // 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;
+ rs->Open(table.c_str(),
+ _variant_t((IDispatch *)p, true),
+ adOpenStatic,
+ adLockOptimistic,
+ adCmdTable);
+ tableName = table;
+}
+
+void
+Recordset::close()
+{
+ if (rs && rs->State == adStateOpen)
+ rs->Close();
+ rs = 0;
+}
+
+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/cpp/src/qpid/store/ms-sql/Recordset.h b/cpp/src/qpid/store/ms-sql/Recordset.h
new file mode 100644
index 0000000000..3631838aa8
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/Recordset.h
@@ -0,0 +1,101 @@
+#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;
+
+public:
+
+#if 0
+ /**
+ * Iterator support for walking through the recordset.
+ * If I need to try this again, I'd look at Recordset cloning.
+ */
+ class Iterator : public boost::iterator_facade<
+ Iterator, std::pair<uint64_t, BlobAdapter>, boost::random_access_traversal_tag>
+ {
+ public:
+ Iterator() : rs(0) { }
+ Iterator(Recordset& _rs);
+
+ private:
+ friend class boost::iterator_core_access;
+
+ std::pair<uint64_t, BlobAdapter>& dereference() const;
+ void increment();
+ bool equal(const Iterator& x) const;
+
+ _RecordsetPtr rs;
+ std::pair<uint64_t, BlobAdapter> current;
+
+ void setCurrent();
+ };
+
+ friend class Iterator;
+#endif
+
+ Recordset() : rs(0) {}
+ virtual ~Recordset() { close(); }
+ void open(DatabaseConnection* conn, const std::string& table);
+ void close();
+ void requery();
+ operator _RecordsetPtr () { return rs; }
+#if 0
+ Iterator begin() { Iterator iter(*this); return iter; }
+ Iterator end() { Iterator iter; return iter; }
+#endif
+
+ // Dump table contents; useful for debugging.
+ void dump();
+};
+
+}}} // namespace qpid::store::ms_sql
+
+#endif /* QPID_STORE_MSSQL_RECORDSET_H */
diff --git a/cpp/src/qpid/store/ms-sql/State.cpp b/cpp/src/qpid/store/ms-sql/State.cpp
new file mode 100644
index 0000000000..720603dd11
--- /dev/null
+++ b/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/cpp/src/qpid/client/AckMode.h b/cpp/src/qpid/store/ms-sql/State.h
index b411c322d8..6350bc5bd2 100644
--- a/cpp/src/qpid/client/AckMode.h
+++ b/cpp/src/qpid/store/ms-sql/State.h
@@ -1,6 +1,5 @@
-#ifndef _client_AckMode_h
-#define _client_AckMode_h
-
+#ifndef QPID_STORE_MSSQL_STATE_H
+#define QPID_STORE_MSSQL_STATE_H
/*
*
@@ -24,30 +23,30 @@
*/
namespace qpid {
-namespace client {
+namespace store {
+namespace ms_sql {
+
+class DatabaseConnection;
/**
- * DEPRECATED
- *
- * The available acknowledgement modes for Channel (now also deprecated).
+ * @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.
*/
-enum AckMode {
- /** No acknowledgement will be sent, broker can
- discard messages as soon as they are delivered
- to a consumer using this mode. **/
- NO_ACK = 0,
- /** Each message will be automatically
- acknowledged as soon as it is delivered to the
- application. **/
- AUTO_ACK = 1,
- /** Acknowledgements will be sent automatically,
- but not for each message. **/
- LAZY_ACK = 2,
- /** The application is responsible for explicitly
- acknowledging messages. **/
- CLIENT_ACK = 3
+struct State {
+ State();
+ ~State();
+ DatabaseConnection *dbConn;
};
-}} // namespace qpid::client
+}}} // namespace qpid::store::ms_sql
-#endif
+#endif /* QPID_STORE_MSSQL_STATE_H */
diff --git a/cpp/src/qpid/store/ms-sql/VariantHelper.cpp b/cpp/src/qpid/store/ms-sql/VariantHelper.cpp
new file mode 100644
index 0000000000..786724f031
--- /dev/null
+++ b/cpp/src/qpid/store/ms-sql/VariantHelper.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 <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)
+{
+ var.SetString(init.c_str());
+}
+
+VariantHelper<std::string>&
+VariantHelper<std::string>::operator=(const std::string &rhs)
+{
+ 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/cpp/src/qpid/store/ms-sql/VariantHelper.h b/cpp/src/qpid/store/ms-sql/VariantHelper.h
new file mode 100644
index 0000000000..723dbc3b76
--- /dev/null
+++ b/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/cpp/src/qpid/sys/AggregateOutput.cpp b/cpp/src/qpid/sys/AggregateOutput.cpp
index 2fad28c381..709d3bc640 100644
--- a/cpp/src/qpid/sys/AggregateOutput.cpp
+++ b/cpp/src/qpid/sys/AggregateOutput.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -25,44 +25,67 @@
namespace qpid {
namespace sys {
-
-void AggregateOutput::activateOutput()
-{
- control.activateOutput();
-}
+
+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); }
bool AggregateOutput::hasOutput() {
- for (TaskList::const_iterator i = tasks.begin(); i != tasks.end(); ++i)
- if ((*i)->hasOutput()) return true;
- return false;
+ Mutex::ScopedLock l(lock);
+ return !tasks.empty();
}
-bool AggregateOutput::doOutput()
-{
- bool result = false;
- if (!tasks.empty()) {
- if (next >= tasks.size()) next = next % tasks.size();
-
- size_t start = next;
- //loop until a task generated some output
- while (!result) {
- result = tasks[next++]->doOutput();
- if (next >= tasks.size()) next = next % tasks.size();
- if (start == next) break;
+// 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 result;
+ return false;
+}
+
+void AggregateOutput::addOutputTask(OutputTask* task) {
+ Mutex::ScopedLock l(lock);
+ tasks.push_back(task);
}
-void AggregateOutput::addOutputTask(OutputTask* t)
-{
- tasks.push_back(t);
+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::removeOutputTask(OutputTask* t)
+
+void AggregateOutput::removeAll()
{
- TaskList::iterator i = std::find(tasks.begin(), tasks.end(), t);
- if (i != tasks.end()) tasks.erase(i);
+ Mutex::ScopedLock l(lock);
+ while (busy) lock.wait();
+ tasks.clear();
}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/AggregateOutput.h b/cpp/src/qpid/sys/AggregateOutput.h
index 02a53ed50b..71ad713eb7 100644
--- a/cpp/src/qpid/sys/AggregateOutput.h
+++ b/cpp/src/qpid/sys/AggregateOutput.h
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,35 +21,58 @@
#ifndef _AggregateOutput_
#define _AggregateOutput_
-#include <vector>
-#include "Mutex.h"
-#include "OutputControl.h"
-#include "OutputTask.h"
+#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 {
- class AggregateOutput : public OutputTask, public OutputControl
- {
- typedef std::vector<OutputTask*> TaskList;
-
- TaskList tasks;
- size_t next;
- OutputControl& control;
-
- public:
- AggregateOutput(OutputControl& c) : next(0), control(c) {};
- //this may be called on any thread
- void activateOutput();
- //all the following will be called on the same thread
- bool doOutput();
- bool hasOutput();
- void addOutputTask(OutputTask* t);
- void removeOutputTask(OutputTask* t);
- };
-
-}
-}
+/**
+ * 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 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 bool hasOutput();
+ 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/cpp/src/qpid/sys/AsynchIO.h b/cpp/src/qpid/sys/AsynchIO.h
index ff7823e00d..2a41f0a7d1 100644
--- a/cpp/src/qpid/sys/AsynchIO.h
+++ b/cpp/src/qpid/sys/AsynchIO.h
@@ -21,15 +21,16 @@
*
*/
-#include "Dispatcher.h"
-
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
#include <boost/function.hpp>
-#include <deque>
+#include <boost/shared_ptr.hpp>
namespace qpid {
namespace sys {
class Socket;
+class Poller;
/*
* Asynchronous acceptor: accepts connections then does a callback with the
@@ -39,44 +40,36 @@ class AsynchAcceptor {
public:
typedef boost::function1<void, const Socket&> Callback;
-private:
- Callback acceptedCallback;
- DispatchHandle handle;
- const Socket& socket;
-
-public:
- AsynchAcceptor(const Socket& s, Callback callback);
- void start(Poller::shared_ptr poller);
-
-private:
- void readable(DispatchHandle& handle);
+ 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 : private DispatchHandle {
+class AsynchConnector {
public:
typedef boost::function1<void, const Socket&> ConnectedCallback;
- typedef boost::function2<void, int, std::string> FailedCallback;
-
-private:
- ConnectedCallback connCallback;
- FailedCallback failCallback;
- const Socket& socket;
-
-public:
- AsynchConnector(const Socket& socket,
- Poller::shared_ptr poller,
- std::string hostname,
- uint16_t port,
- ConnectedCallback connCb,
- FailedCallback failCb = 0);
-
-private:
- void connComplete(DispatchHandle& handle);
- void failure(int, std::string);
+ 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,
+ boost::shared_ptr<Poller> poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb);
+
+protected:
+ AsynchConnector() {}
+ virtual ~AsynchConnector() {}
};
struct AsynchIOBufferBase {
@@ -99,16 +92,14 @@ struct AsynchIOBufferBase {
/*
* 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.
+ * 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
+ * a callback for when writing is "idle" (ie fd is writable, but nothing
+ * to write).
*/
-class AsynchIO : private DispatchHandle {
+class AsynchIO {
public:
typedef AsynchIOBufferBase BufferBase;
@@ -118,47 +109,40 @@ public:
typedef boost::function2<void, AsynchIO&, const Socket&> ClosedCallback;
typedef boost::function1<void, AsynchIO&> BuffersEmptyCallback;
typedef boost::function1<void, AsynchIO&> IdleCallback;
-
-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;
-
+ 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:
- AsynchIO(const Socket& s,
- ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb,
- ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0);
- void queueForDeletion();
-
- void start(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();
-
-private:
- ~AsynchIO();
- void readable(DispatchHandle& handle);
- void writeable(DispatchHandle& handle);
- void disconnected(DispatchHandle& handle);
- void close(DispatchHandle& handle);
+ 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() {}
};
}}
diff --git a/cpp/src/qpid/sys/AsynchIOHandler.cpp b/cpp/src/qpid/sys/AsynchIOHandler.cpp
index 886dbc8f43..f658b7d50f 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.cpp
+++ b/cpp/src/qpid/sys/AsynchIOHandler.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,13 +19,15 @@
*
*/
-#include "AsynchIOHandler.h"
+#include "qpid/sys/AsynchIOHandler.h"
#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/Socket.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 {
@@ -44,7 +46,8 @@ AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) :
factory(f),
codec(0),
readError(false),
- isClient(false)
+ isClient(false),
+ readCredit(InfiniteCredit)
{}
AsynchIOHandler::~AsynchIOHandler() {
@@ -70,19 +73,61 @@ void AsynchIOHandler::write(const framing::ProtocolInitiation& data)
buff = new Buff;
framing::Buffer out(buff->bytes, buff->byteCount);
data.encode(out);
- buff->dataCount = data.size();
+ 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 {
@@ -99,13 +144,13 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
decoded = in.getPosition();
QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")");
try {
- codec = factory->create(protocolInit.getVersion(), *this, identifier);
+ codec = factory->create(protocolInit.getVersion(), *this, identifier, 0);
if (!codec) {
//TODO: may still want to revise this...
//send valid version header & close connection.
write(framing::ProtocolInitiation(framing::highestProtocolVersion));
readError = true;
- aio->queueWriteClose();
+ aio->queueWriteClose();
}
} catch (const std::exception& e) {
QPID_LOG(error, e.what());
@@ -130,11 +175,12 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
void AsynchIOHandler::eof(AsynchIO&) {
QPID_LOG(debug, "DISCONNECTED [" << identifier << "]");
if (codec) codec->closed();
+ readError = true;
aio->queueWriteClose();
}
void AsynchIOHandler::closedSocket(AsynchIO&, const Socket& s) {
- // If we closed with data still to send log a warning
+ // 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)");
}
@@ -154,21 +200,29 @@ void AsynchIOHandler::nobuffs(AsynchIO&) {
void AsynchIOHandler::idle(AsynchIO&){
if (isClient && codec == 0) {
- codec = factory->create(*this, identifier);
+ codec = factory->create(*this, identifier, 0);
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
- 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())
+ 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/cpp/src/qpid/sys/AsynchIOHandler.h b/cpp/src/qpid/sys/AsynchIOHandler.h
index 26e2cf4c5c..e1885bac79 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.h
+++ b/cpp/src/qpid/sys/AsynchIOHandler.h
@@ -9,9 +9,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,8 +21,11 @@
*
*/
-#include "OutputControl.h"
-#include "ConnectionCodec.h"
+#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 {
@@ -33,7 +36,7 @@ namespace framing {
namespace sys {
class AsynchIO;
-class AsynchIOBufferBase;
+struct AsynchIOBufferBase;
class Socket;
class AsynchIOHandler : public OutputControl {
@@ -43,29 +46,33 @@ class AsynchIOHandler : public OutputControl {
ConnectionCodec* codec;
bool readError;
bool isClient;
+ AtomicValue<int32_t> readCredit;
+ static const int32_t InfiniteCredit = -1;
+ Mutex creditLock;
void write(const framing::ProtocolInitiation&);
public:
- AsynchIOHandler(std::string id, ConnectionCodec::Factory* f);
- ~AsynchIOHandler();
- void init(AsynchIO* a, int numBuffs);
+ QPID_COMMON_EXTERN AsynchIOHandler(std::string id, ConnectionCodec::Factory* f);
+ QPID_COMMON_EXTERN ~AsynchIOHandler();
+ QPID_COMMON_EXTERN void init(AsynchIO* a, int numBuffs);
- void setClient() { isClient = true; }
+ QPID_COMMON_EXTERN void setClient() { isClient = true; }
// Output side
- void close();
- void activateOutput();
+ QPID_COMMON_EXTERN void abort();
+ QPID_COMMON_EXTERN void activateOutput();
+ QPID_COMMON_EXTERN void giveReadCredit(int32_t credit);
// Input side
- void readbuff(AsynchIO& aio, AsynchIOBufferBase* buff);
- void eof(AsynchIO& aio);
- void disconnect(AsynchIO& aio);
-
+ QPID_COMMON_EXTERN void readbuff(AsynchIO& aio, AsynchIOBufferBase* buff);
+ QPID_COMMON_EXTERN void eof(AsynchIO& aio);
+ QPID_COMMON_EXTERN void disconnect(AsynchIO& aio);
+
// Notifications
- void nobuffs(AsynchIO& aio);
- void idle(AsynchIO& aio);
- void closedSocket(AsynchIO& aio, const Socket& s);
+ 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
diff --git a/cpp/src/qpid/sys/AtomicCount.h b/cpp/src/qpid/sys/AtomicCount.h
index d598b49427..94580c61f3 100644
--- a/cpp/src/qpid/sys/AtomicCount.h
+++ b/cpp/src/qpid/sys/AtomicCount.h
@@ -20,7 +20,7 @@
*/
#include <boost/detail/atomic_count.hpp>
-#include "ScopedIncrement.h"
+#include "qpid/sys/ScopedIncrement.h"
namespace qpid {
namespace sys {
diff --git a/cpp/src/qpid/sys/AtomicValue_gcc.h b/cpp/src/qpid/sys/AtomicValue_gcc.h
index da60edad65..d022b07c1d 100644
--- a/cpp/src/qpid/sys/AtomicValue_gcc.h
+++ b/cpp/src/qpid/sys/AtomicValue_gcc.h
@@ -57,7 +57,7 @@ class AtomicValue
/** 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(0); }
+ T get() const { return const_cast<AtomicValue<T>*>(this)->fetchAndAdd(static_cast<T>(0)); }
private:
T value;
diff --git a/cpp/src/qpid/sys/AtomicValue_mutex.h b/cpp/src/qpid/sys/AtomicValue_mutex.h
index 8871addbda..e4d433e7f5 100644
--- a/cpp/src/qpid/sys/AtomicValue_mutex.h
+++ b/cpp/src/qpid/sys/AtomicValue_mutex.h
@@ -53,6 +53,8 @@ class AtomicValue
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);
diff --git a/cpp/src/qpid/sys/BlockingQueue.h b/cpp/src/qpid/sys/BlockingQueue.h
index c6c6291b97..210cb4ad82 100644
--- a/cpp/src/qpid/sys/BlockingQueue.h
+++ b/cpp/src/qpid/sys/BlockingQueue.h
@@ -22,7 +22,7 @@
*
*/
-#include "Waitable.h"
+#include "qpid/sys/Waitable.h"
#include <queue>
@@ -66,14 +66,17 @@ public:
return true;
}
- T pop() {
+ T pop(Duration timeout=TIME_INFINITE) {
T result;
- bool ok = pop(result);
- assert(ok); (void) ok; // Infinite wait.
+ bool ok = pop(result, timeout);
+ if (!ok)
+ throw Exception("Timed out waiting on a blocking queue");
return result;
}
- /** Push a value onto the queue */
+ /** 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);
diff --git a/cpp/src/qpid/sys/Codec.h b/cpp/src/qpid/sys/Codec.h
new file mode 100644
index 0000000000..ace721fbcc
--- /dev/null
+++ b/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/cpp/src/qpid/sys/ConnectionCodec.h b/cpp/src/qpid/sys/ConnectionCodec.h
index efc6839b60..7231b1daa6 100644
--- a/cpp/src/qpid/sys/ConnectionCodec.h
+++ b/cpp/src/qpid/sys/ConnectionCodec.h
@@ -21,54 +21,55 @@
* under the License.
*
*/
+#include "qpid/sys/Codec.h"
#include "qpid/framing/ProtocolVersion.h"
-#include "OutputControl.h"
-#include <memory>
-#include <map>
namespace qpid {
namespace sys {
+class InputHandlerFactory;
+class OutputControl;
+
/**
* Interface of coder/decoder for a connection of a specific protocol
* version.
*/
-class ConnectionCodec {
+class ConnectionCodec : public Codec {
public:
virtual ~ConnectionCodec() {}
- /** 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 size_t decode(const char* buffer, size_t size) = 0;
-
-
- /** Encode into buffer, return number of bytes encoded */
- virtual size_t encode(const char* buffer, size_t size) = 0;
-
- /** Return true if we have data to encode */
- virtual bool canEncode() = 0;
-
/** 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() {}
+ /** Security Strength Factor - indicates the level of security provided
+ * by the underlying transport. If zero, the transport provides no
+ * security (e.g. TCP). If non-zero, the transport provides some level
+ * of security (e.g. SSL). The values for SSF can be interpreted as:
+ *
+ * 0 = No protection.
+ * 1 = Integrity checking only.
+ * >1 = Supports authentication, integrity and confidentiality.
+ * The number represents the encryption key length.
+ */
+
/** Return 0 if version unknown */
virtual ConnectionCodec* create(
- framing::ProtocolVersion, OutputControl&, const std::string& id
+ framing::ProtocolVersion, OutputControl&, const std::string& id,
+ unsigned int conn_ssf
) = 0;
/** Return "preferred" codec for outbound connections. */
virtual ConnectionCodec* create(
- OutputControl&, const std::string& id
+ OutputControl&, const std::string& id,
+ unsigned int conn_ssf
) = 0;
};
};
diff --git a/cpp/src/qpid/sys/ConnectionInputHandler.h b/cpp/src/qpid/sys/ConnectionInputHandler.h
index a2c18d6d9a..9e85c66924 100644
--- a/cpp/src/qpid/sys/ConnectionInputHandler.h
+++ b/cpp/src/qpid/sys/ConnectionInputHandler.h
@@ -22,8 +22,8 @@
#define _ConnectionInputHandler_
#include "qpid/framing/InputHandler.h"
-#include "OutputTask.h"
-#include "TimeoutHandler.h"
+#include "qpid/sys/OutputTask.h"
+#include "qpid/sys/TimeoutHandler.h"
namespace qpid {
namespace sys {
@@ -33,6 +33,7 @@ namespace sys {
public TimeoutHandler, public OutputTask
{
public:
+
virtual void closed() = 0;
};
diff --git a/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h b/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h
index 2b309b5758..9bb7e13686 100644
--- a/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h
+++ b/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h
@@ -42,7 +42,8 @@ class ConnectionInputHandlerFactory : private boost::noncopyable
*@param id identify the connection for management purposes.
*/
virtual ConnectionInputHandler* create(ConnectionOutputHandler* out,
- const std::string& id) = 0;
+ const std::string& id,
+ bool isClient) = 0;
virtual ~ConnectionInputHandlerFactory(){}
};
diff --git a/cpp/src/qpid/sys/ConnectionOutputHandler.h b/cpp/src/qpid/sys/ConnectionOutputHandler.h
index 5a60ae4998..421dd7c269 100644
--- a/cpp/src/qpid/sys/ConnectionOutputHandler.h
+++ b/cpp/src/qpid/sys/ConnectionOutputHandler.h
@@ -22,7 +22,7 @@
#define _ConnectionOutputHandler_
#include "qpid/framing/OutputHandler.h"
-#include "OutputControl.h"
+#include "qpid/sys/OutputControl.h"
namespace qpid {
namespace sys {
@@ -34,6 +34,7 @@ class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler, pub
{
public:
virtual void close() = 0;
+ virtual size_t getBuffered() const { return 0; }
};
}}
diff --git a/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h b/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h
new file mode 100644
index 0000000000..95a08d15ae
--- /dev/null
+++ b/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/cpp/src/qpid/sys/CopyOnWriteArray.h b/cpp/src/qpid/sys/CopyOnWriteArray.h
new file mode 100644
index 0000000000..e4ae3a6094
--- /dev/null
+++ b/cpp/src/qpid/sys/CopyOnWriteArray.h
@@ -0,0 +1,138 @@
+#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 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/cpp/src/qpid/sys/DeletionManager.h b/cpp/src/qpid/sys/DeletionManager.h
index 43154eb98e..c1fea19f30 100644
--- a/cpp/src/qpid/sys/DeletionManager.h
+++ b/cpp/src/qpid/sys/DeletionManager.h
@@ -54,6 +54,8 @@ struct deleter
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
@@ -65,6 +67,24 @@ public:
// 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
@@ -75,14 +95,9 @@ public:
allThreadsStatuses.addThreadStatus(threadStatus);
}
- 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();
+ return threadStatus;
}
-private:
typedef boost::shared_ptr<H> shared_ptr;
// In theory we know that we never need more handles than the number of
@@ -125,6 +140,15 @@ private:
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));
diff --git a/cpp/src/qpid/sys/DispatchHandle.cpp b/cpp/src/qpid/sys/DispatchHandle.cpp
new file mode 100644
index 0000000000..605edabc64
--- /dev/null
+++ b/cpp/src/qpid/sys/DispatchHandle.cpp
@@ -0,0 +1,337 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/DispatchHandle.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.
+ 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 could only be interrupted if we also had a callback to do
+ assert(callbacks.size() > 0);
+ // 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) {
+ Callback cb = callbacks.front();
+ assert(cb);
+ cb(*this);
+ callbacks.pop();
+ }
+
+ {
+ 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/cpp/src/qpid/sys/DispatchHandle.h b/cpp/src/qpid/sys/DispatchHandle.h
new file mode 100644
index 0000000000..115a3c44f7
--- /dev/null
+++ b/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/cpp/src/qpid/sys/Dispatcher.cpp b/cpp/src/qpid/sys/Dispatcher.cpp
index 02a62c8e66..5f52dcd990 100644
--- a/cpp/src/qpid/sys/Dispatcher.cpp
+++ b/cpp/src/qpid/sys/Dispatcher.cpp
@@ -19,9 +19,7 @@
*
*/
-#include "Dispatcher.h"
-
-#include <boost/cast.hpp>
+#include "qpid/sys/Dispatcher.h"
#include <assert.h>
@@ -36,404 +34,7 @@ Dispatcher::~Dispatcher() {
}
void Dispatcher::run() {
- do {
- Poller::Event event = poller->wait();
-
- // If can read/write then dispatch appropriate callbacks
- if (event.handle) {
- event.process();
- } else {
- // Handle shutdown
- switch (event.type) {
- case Poller::SHUTDOWN:
- goto dispatcher_shutdown;
- default:
- // This should be impossible
- assert(false);
- }
- }
- } while (true);
-
-dispatcher_shutdown:
- ;
-}
-
-DispatchHandle::~DispatchHandle() {
- stopWatch();
-}
-
-void DispatchHandle::startWatch(Poller::shared_ptr poller0) {
- bool r = readableCallback;
- bool w = writableCallback;
-
- ScopedLock<Mutex> lock(stateLock);
- assert(state == IDLE);
-
- // If no callbacks set then do nothing (that is what we were asked to do!)
- // TODO: Maybe this should be an assert instead
- if (!r && !w) {
- state = INACTIVE;
- return;
- }
-
- Poller::Direction d = r ?
- (w ? Poller::INOUT : Poller::IN) :
- Poller::OUT;
-
- poller = poller0;
- poller->addFd(*this, d);
-
- state = r ?
- (w ? ACTIVE_RW : ACTIVE_R) :
- ACTIVE_W;
-}
-
-void DispatchHandle::rewatch() {
- bool r = readableCallback;
- bool w = writableCallback;
-
- ScopedLock<Mutex> lock(stateLock);
- switch(state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_R:
- case DELAYED_W:
- case DELAYED_INACTIVE:
- state = r ?
- (w ? DELAYED_RW : DELAYED_R) :
- DELAYED_W;
- break;
- case DELAYED_DELETE:
- break;
- case INACTIVE:
- case ACTIVE_R:
- case ACTIVE_W: {
- assert(poller);
- Poller::Direction d = r ?
- (w ? Poller::INOUT : Poller::IN) :
- Poller::OUT;
- poller->modFd(*this, d);
- state = r ?
- (w ? ACTIVE_RW : ACTIVE_R) :
- ACTIVE_W;
- break;
- }
- case DELAYED_RW:
- case ACTIVE_RW:
- // Don't need to do anything already waiting for readable/writable
- break;
- }
-}
-
-void DispatchHandle::rewatchRead() {
- if (!readableCallback) {
- return;
- }
-
- ScopedLock<Mutex> lock(stateLock);
- switch(state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_R:
- case DELAYED_RW:
- case DELAYED_DELETE:
- break;
- case DELAYED_W:
- state = DELAYED_RW;
- break;
- case DELAYED_INACTIVE:
- state = DELAYED_R;
- break;
- case ACTIVE_R:
- case ACTIVE_RW:
- // Nothing to do: already waiting for readable
- break;
- case INACTIVE:
- assert(poller);
- poller->modFd(*this, Poller::IN);
- state = ACTIVE_R;
- break;
- case ACTIVE_W:
- assert(poller);
- poller->modFd(*this, Poller::INOUT);
- state = ACTIVE_RW;
- break;
- }
-}
-
-void DispatchHandle::rewatchWrite() {
- if (!writableCallback) {
- return;
- }
-
- ScopedLock<Mutex> lock(stateLock);
- switch(state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_W:
- case DELAYED_RW:
- case DELAYED_DELETE:
- break;
- case DELAYED_R:
- state = DELAYED_RW;
- break;
- case DELAYED_INACTIVE:
- state = DELAYED_W;
- break;
- case INACTIVE:
- assert(poller);
- poller->modFd(*this, Poller::OUT);
- state = ACTIVE_W;
- break;
- case ACTIVE_R:
- assert(poller);
- poller->modFd(*this, Poller::INOUT);
- state = ACTIVE_RW;
- break;
- case ACTIVE_W:
- case ACTIVE_RW:
- // Nothing to do: already waiting for writable
- break;
- }
-}
-
-void DispatchHandle::unwatchRead() {
- if (!readableCallback) {
- return;
- }
-
- ScopedLock<Mutex> lock(stateLock);
- switch(state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_R:
- state = DELAYED_INACTIVE;
- break;
- case DELAYED_RW:
- state = DELAYED_W;
- break;
- case DELAYED_W:
- case DELAYED_INACTIVE:
- case DELAYED_DELETE:
- break;
- case ACTIVE_R:
- assert(poller);
- poller->modFd(*this, Poller::NONE);
- state = INACTIVE;
- break;
- case ACTIVE_RW:
- assert(poller);
- poller->modFd(*this, Poller::OUT);
- state = ACTIVE_W;
- break;
- case ACTIVE_W:
- case INACTIVE:
- break;
- }
-}
-
-void DispatchHandle::unwatchWrite() {
- if (!writableCallback) {
- return;
- }
-
- ScopedLock<Mutex> lock(stateLock);
- switch(state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_W:
- state = DELAYED_INACTIVE;
- break;
- case DELAYED_RW:
- state = DELAYED_R;
- break;
- case DELAYED_R:
- case DELAYED_INACTIVE:
- case DELAYED_DELETE:
- break;
- case ACTIVE_W:
- assert(poller);
- poller->modFd(*this, Poller::NONE);
- state = INACTIVE;
- break;
- case ACTIVE_RW:
- assert(poller);
- poller->modFd(*this, Poller::IN);
- state = ACTIVE_R;
- break;
- case ACTIVE_R:
- case INACTIVE:
- break;
- }
-}
-
-void DispatchHandle::unwatch() {
- ScopedLock<Mutex> lock(stateLock);
- switch (state) {
- case IDLE:
- case DELAYED_IDLE:
- break;
- case DELAYED_R:
- case DELAYED_W:
- case DELAYED_RW:
- case DELAYED_INACTIVE:
- state = DELAYED_INACTIVE;
- break;
- case DELAYED_DELETE:
- break;
- default:
- assert(poller);
- poller->modFd(*this, Poller::NONE);
- state = INACTIVE;
- break;
- }
-}
-
-void DispatchHandle::stopWatch() {
- ScopedLock<Mutex> lock(stateLock);
- switch (state) {
- case IDLE:
- case DELAYED_IDLE:
- case DELAYED_DELETE:
- return;
- case DELAYED_R:
- case DELAYED_W:
- case DELAYED_RW:
- case DELAYED_INACTIVE:
- state = DELAYED_IDLE;
- break;
- default:
- state = IDLE;
- break;
- }
- assert(poller);
- poller->delFd(*this);
- poller.reset();
-}
-
-// The slightly strange switch structure
-// is to ensure that the lock is released before
-// we do the delete
-void DispatchHandle::doDelete() {
- // Ensure that we're no longer watching anything
- stopWatch();
-
- // If we're in the middle of a callback defer the delete
- {
- ScopedLock<Mutex> lock(stateLock);
- switch (state) {
- case DELAYED_IDLE:
- case DELAYED_DELETE:
- state = DELAYED_DELETE;
- return;
- case IDLE:
- break;
- default:
- // Can only get out of stopWatch() in DELAYED_IDLE/DELAYED_DELETE/IDLE states
- assert(false);
- }
- }
- // If we're not then do it right away
- delete this;
-}
-
-void DispatchHandle::processEvent(Poller::EventType type) {
- // Note that we are now doing the callbacks
- {
- ScopedLock<Mutex> lock(stateLock);
-
- // Set up to wait for same events next time unless reset
- switch(state) {
- case ACTIVE_R:
- state = DELAYED_R;
- break;
- case ACTIVE_W:
- state = DELAYED_W;
- break;
- case ACTIVE_RW:
- state = DELAYED_RW;
- break;
- // Can only get here in a DELAYED_* state in the rare case
- // that we're already here for reading and we get activated for
- // writing and we can write (it might be possible the other way
- // round too). In this case we're already processing the handle
- // in a different thread in this function so return right away
- case DELAYED_R:
- case DELAYED_W:
- case DELAYED_RW:
- case DELAYED_INACTIVE:
- case DELAYED_IDLE:
- case DELAYED_DELETE:
- return;
- default:
- assert(false);
- }
- }
-
- // 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.
- 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:
- {
- ScopedLock<Mutex> lock(stateLock);
- state = DELAYED_INACTIVE;
- }
- if (disconnectedCallback) {
- disconnectedCallback(*this);
- }
- break;
- default:
- assert(false);
- }
-
- // If any of the callbacks re-enabled reading/writing then actually
- // do it now
- {
- ScopedLock<Mutex> lock(stateLock);
- switch (state) {
- case DELAYED_R:
- poller->modFd(*this, Poller::IN);
- state = ACTIVE_R;
- return;
- case DELAYED_W:
- poller->modFd(*this, Poller::OUT);
- state = ACTIVE_W;
- return;
- case DELAYED_RW:
- poller->modFd(*this, Poller::INOUT);
- state = ACTIVE_RW;
- return;
- case DELAYED_INACTIVE:
- state = INACTIVE;
- return;
- case DELAYED_IDLE:
- state = IDLE;
- return;
- default:
- // This should be impossible
- assert(false);
- return;
- case DELAYED_DELETE:
- break;
- }
- }
- delete this;
+ poller->run();
}
}}
diff --git a/cpp/src/qpid/sys/Dispatcher.h b/cpp/src/qpid/sys/Dispatcher.h
index 8e34354f9e..e8213d0579 100644
--- a/cpp/src/qpid/sys/Dispatcher.h
+++ b/cpp/src/qpid/sys/Dispatcher.h
@@ -22,139 +22,21 @@
*
*/
-#include "Poller.h"
-#include "Runnable.h"
-#include "Mutex.h"
-
-#include <memory>
-#include <queue>
-#include <boost/function.hpp>
-
-#include <assert.h>
-
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/CommonImportExport.h"
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;
-
-private:
- Callback readableCallback;
- Callback writableCallback;
- Callback disconnectedCallback;
- Poller::shared_ptr poller;
- Mutex stateLock;
- enum {
- IDLE, INACTIVE, ACTIVE_R, ACTIVE_W, ACTIVE_RW,
- DELAYED_IDLE, DELAYED_INACTIVE, DELAYED_R, DELAYED_W, DELAYED_RW,
- DELAYED_DELETE
- } 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.
- */
- DispatchHandle(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) :
- PollerHandle(h),
- readableCallback(rCb),
- writableCallback(wCb),
- disconnectedCallback(dCb),
- state(IDLE)
- {}
-
- ~DispatchHandle();
-
- /** Add this DispatchHandle to the poller to be watched. */
- void startWatch(Poller::shared_ptr poller);
-
- /** Resume watchingn for all non-0 callbacks. */
- void rewatch();
- /** Resume watchingn for read only. */
- void rewatchRead();
-
- /** Resume watchingn for write only. */
- void rewatchWrite();
-
- /** Stop watching temporarily. The DispatchHandle remains
- associated with the poller and can be re-activated using
- rewatch. */
- void unwatch();
- /** Stop watching for read */
- void unwatchRead();
- /** Stop watching for write */
- void unwatchWrite();
-
- /** Stop watching permanently. Disassociates from the poller. */
- void stopWatch();
-
-protected:
- /** Override to get extra processing done when the DispatchHandle is deleted. */
- void doDelete();
-
-private:
- 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(); }
-};
-
-
class Dispatcher : public Runnable {
const Poller::shared_ptr poller;
public:
- Dispatcher(Poller::shared_ptr poller);
- ~Dispatcher();
+ QPID_COMMON_EXTERN Dispatcher(Poller::shared_ptr poller);
+ QPID_COMMON_EXTERN ~Dispatcher();
- void run();
+ QPID_COMMON_EXTERN void run();
};
}}
diff --git a/cpp/src/qpid/sys/ExceptionHolder.h b/cpp/src/qpid/sys/ExceptionHolder.h
deleted file mode 100644
index cfb971411e..0000000000
--- a/cpp/src/qpid/sys/ExceptionHolder.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef QPID_EXCEPTIONHOLDER_H
-#define QPID_EXCEPTIONHOLDER_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/memory.h"
-#include <boost/shared_ptr.hpp>
-
-
-namespace qpid {
-namespace sys {
-
-struct Raisable {
- virtual ~Raisable() {};
- virtual void raise() const=0;
- virtual std::string what() const=0;
-};
-
-/**
- * Holder for exceptions. Allows the thread that notices an error condition to
- * create an exception and store it to be thrown by another thread.
- */
-class ExceptionHolder : public Raisable {
- public:
- ExceptionHolder() {}
- // Use default copy & assign.
-
- /** Take ownership of ex */
- template <class Ex> ExceptionHolder(Ex* ex) { wrap(ex); }
- template <class Ex> ExceptionHolder(const boost::shared_ptr<Ex>& ex) { wrap(ex.release()); }
-
- template <class Ex> ExceptionHolder& operator=(Ex* ex) { wrap(ex); return *this; }
- template <class Ex> ExceptionHolder& operator=(boost::shared_ptr<Ex> ex) { wrap(ex.release()); return *this; }
-
- void raise() const { if (wrapper.get()) wrapper->raise() ; }
- std::string what() const { return wrapper->what(); }
- bool empty() const { return !wrapper.get(); }
- operator bool() const { return !empty(); }
- void reset() { wrapper.reset(); }
-
- private:
- template <class Ex> struct Wrapper : public Raisable {
- Wrapper(Ex* ptr) : exception(ptr) {}
- void raise() const { throw *exception; }
- std::string what() const { return exception->what(); }
- boost::shared_ptr<Ex> exception;
- };
- template <class Ex> void wrap(Ex* ex) { wrapper.reset(new Wrapper<Ex>(ex)); }
- boost::shared_ptr<Raisable> wrapper;
-};
-
-
-}} // namespace qpid::sys
-
-
-#endif /*!QPID_EXCEPTIONHOLDER_H*/
diff --git a/cpp/src/qpid/sys/FileSysDir.h b/cpp/src/qpid/sys/FileSysDir.h
new file mode 100755
index 0000000000..ffe7823f0a
--- /dev/null
+++ b/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/cpp/src/qpid/sys/LockFile.h b/cpp/src/qpid/sys/LockFile.h
index f06cd6a47d..14a76cbf3e 100644
--- a/cpp/src/qpid/sys/LockFile.h
+++ b/cpp/src/qpid/sys/LockFile.h
@@ -19,7 +19,44 @@
*
*/
-#include "posix/LockFile.h"
+#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/cpp/src/qpid/sys/LockPtr.h b/cpp/src/qpid/sys/LockPtr.h
new file mode 100644
index 0000000000..738a864317
--- /dev/null
+++ b/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/cpp/src/qpid/sys/Mutex.h b/cpp/src/qpid/sys/Mutex.h
deleted file mode 100644
index b4bd3a9b4a..0000000000
--- a/cpp/src/qpid/sys/Mutex.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef _sys_Mutex_h
-#define _sys_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.
- *
- */
-
-namespace qpid {
-namespace sys {
-
-/**
- * Scoped lock template: calls lock() in ctor, unlock() in dtor.
- * L can be any class with lock() and unlock() functions.
- */
-template <class L>
-class ScopedLock
-{
- public:
- ScopedLock(L& l) : mutex(l) { l.lock(); }
- ~ScopedLock() { mutex.unlock(); }
- private:
- L& mutex;
-};
-
-template <class L>
-class ScopedUnlock
-{
- public:
- ScopedUnlock(L& l) : mutex(l) { l.unlock(); }
- ~ScopedUnlock() { mutex.lock(); }
- private:
- L& mutex;
-};
-
-template <class L>
-class ScopedRlock
-{
- public:
- ScopedRlock(L& l) : mutex(l) { l.rlock(); }
- ~ScopedRlock() { mutex.unlock(); }
- private:
- L& mutex;
-};
-
-template <class L>
-class ScopedWlock
-{
- public:
- ScopedWlock(L& l) : mutex(l) { l.wlock(); }
- ~ScopedWlock() { mutex.unlock(); }
- private:
- L& mutex;
-};
-
-template <class L>
-class ConditionalScopedLock
-{
- public:
- ConditionalScopedLock(L& l) : mutex(l) { acquired = l.trylock(); }
- ~ConditionalScopedLock() { if (acquired) mutex.unlock(); }
- bool lockAcquired() { return acquired; }
- private:
- L& mutex;
- bool acquired;
-};
-
-}}
-
-#ifdef USE_APR_PLATFORM
-#include "apr/Mutex.h"
-#else
-#include "posix/Mutex.h"
-#endif
-
-#endif /*!_sys_Mutex_h*/
diff --git a/cpp/src/qpid/sys/OutputControl.h b/cpp/src/qpid/sys/OutputControl.h
index d922a0d85c..eae99beb0f 100644
--- a/cpp/src/qpid/sys/OutputControl.h
+++ b/cpp/src/qpid/sys/OutputControl.h
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,17 +18,22 @@
* under the License.
*
*/
+
+#include "qpid/sys/IntegerTypes.h"
+
#ifndef _OutputControl_
#define _OutputControl_
namespace qpid {
namespace sys {
- class OutputControl
+ class OutputControl
{
public:
virtual ~OutputControl() {}
+ virtual void abort() = 0;
virtual void activateOutput() = 0;
+ virtual void giveReadCredit(int32_t credit) = 0;
};
}
diff --git a/cpp/src/qpid/sys/PipeHandle.h b/cpp/src/qpid/sys/PipeHandle.h
new file mode 100755
index 0000000000..8aac76996b
--- /dev/null
+++ b/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/cpp/src/qpid/cluster/PollableCondition.h b/cpp/src/qpid/sys/PollableCondition.h
index 6bfca6cabe..2eb6f2d947 100644
--- a/cpp/src/qpid/cluster/PollableCondition.h
+++ b/cpp/src/qpid/sys/PollableCondition.h
@@ -22,39 +22,43 @@
*
*/
-#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/CommonImportExport.h"
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
-// FIXME aconway 2008-08-11: this could be of more general interest,
-// move to sys namespace in common lib.
-//
namespace qpid {
-namespace cluster {
+namespace sys {
-/**
- * A pollable condition to integrate in-process conditions with IO
- * conditions in a polling loop.
- *
- * Setting the condition makes it readable for a poller.
- *
- * Writable/disconnected conditions are undefined and should not be
- * polled for.
- */
-class PollableCondition : public sys::IOHandle {
- public:
- PollableCondition();
+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);
- /** Set the condition, triggers readable in a poller. */
- void set();
+ QPID_COMMON_EXTERN ~PollableCondition();
- /** Get the current state of the condition, then clear it.
- *@return The state of the condition before it was cleared.
+ /**
+ * Set the condition. Triggers callback to Callback from Poller.
*/
- bool clear();
+ QPID_COMMON_EXTERN void set();
- private:
- int writeFd;
+ /**
+ * 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::cluster
+
+}} // namespace qpid::sys
#endif /*!QPID_SYS_POLLABLECONDITION_H*/
diff --git a/cpp/src/qpid/sys/PollableQueue.h b/cpp/src/qpid/sys/PollableQueue.h
new file mode 100644
index 0000000000..0786b21610
--- /dev/null
+++ b/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()) condition.set();
+ queue.push_back(t);
+}
+
+template <class T> void PollableQueue<T>::dispatch(PollableCondition& cond) {
+ ScopedLock l(lock);
+ assert(dispatcher.id() == 0);
+ 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.id() != Thread::current().id())
+ while (dispatcher.id()) lock.wait();
+}
+
+}} // namespace qpid::sys
+
+#endif /*!QPID_SYS_POLLABLEQUEUE_H*/
diff --git a/cpp/src/qpid/sys/Poller.h b/cpp/src/qpid/sys/Poller.h
index e39528bdb5..413d4242b8 100644
--- a/cpp/src/qpid/sys/Poller.h
+++ b/cpp/src/qpid/sys/Poller.h
@@ -22,8 +22,9 @@
*
*/
-#include "Time.h"
-
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/CommonImportExport.h"
#include <boost/shared_ptr.hpp>
namespace qpid {
@@ -37,7 +38,7 @@ namespace sys {
*/
class PollerHandle;
class PollerPrivate;
-class Poller {
+class Poller : public Runnable {
PollerPrivate* const impl;
public:
@@ -45,8 +46,8 @@ public:
enum Direction {
NONE = 0,
- IN,
- OUT,
+ INPUT,
+ OUTPUT,
INOUT
};
@@ -57,7 +58,8 @@ public:
READ_WRITABLE,
DISCONNECTED,
SHUTDOWN,
- TIMEOUT
+ TIMEOUT,
+ INTERRUPTED
};
struct Event {
@@ -72,16 +74,31 @@ public:
void process();
};
- Poller();
- ~Poller();
+ QPID_COMMON_EXTERN Poller();
+ QPID_COMMON_EXTERN ~Poller();
/** Note: this function is async-signal safe */
- void shutdown();
+ 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();
- void addFd(PollerHandle& handle, Direction dir);
- void delFd(PollerHandle& handle);
- void modFd(PollerHandle& handle, Direction dir);
- void rearmFd(PollerHandle& handle);
- Event wait(Duration timeout = TIME_INFINITE);
+ 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);
};
/**
@@ -91,14 +108,15 @@ class IOHandle;
class PollerHandlePrivate;
class PollerHandle {
friend class Poller;
+ friend class PollerPrivate;
friend struct Poller::Event;
PollerHandlePrivate* const impl;
- virtual void processEvent(Poller::EventType) {};
+ QPID_COMMON_EXTERN virtual void processEvent(Poller::EventType) {};
public:
- PollerHandle(const IOHandle& h);
- virtual ~PollerHandle();
+ QPID_COMMON_EXTERN PollerHandle(const IOHandle& h);
+ QPID_COMMON_EXTERN virtual ~PollerHandle();
};
inline void Poller::Event::process() {
diff --git a/cpp/src/qpid/sys/ProtocolFactory.h b/cpp/src/qpid/sys/ProtocolFactory.h
index 4aa14d2cf6..b233b2da1a 100644
--- a/cpp/src/qpid/sys/ProtocolFactory.h
+++ b/cpp/src/qpid/sys/ProtocolFactory.h
@@ -22,9 +22,9 @@
*
*/
-#include <stdint.h>
+#include "qpid/sys/IntegerTypes.h"
#include "qpid/SharedObject.h"
-#include "ConnectionCodec.h"
+#include "qpid/sys/ConnectionCodec.h"
#include <boost/function.hpp>
namespace qpid {
@@ -46,6 +46,7 @@ class ProtocolFactory : public qpid::SharedObject<ProtocolFactory>
const std::string& host, int16_t port,
ConnectionCodec::Factory* codec,
ConnectFailedCallback failed) = 0;
+ virtual bool supports(const std::string& /*capability*/) { return false; }
};
inline ProtocolFactory::~ProtocolFactory() {}
diff --git a/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
new file mode 100644
index 0000000000..bd19247124
--- /dev/null
+++ b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
@@ -0,0 +1,353 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#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/OutputControl.h"
+
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+
+#include <netdb.h>
+
+using std::auto_ptr;
+using std::string;
+using std::stringstream;
+
+namespace qpid {
+namespace sys {
+
+class RdmaIOHandler : public OutputControl {
+ Rdma::Connection::intrusive_ptr connection;
+ std::string identifier;
+ Rdma::AsynchIO* aio;
+ ConnectionCodec::Factory* factory;
+ ConnectionCodec* codec;
+ bool readError;
+
+ void write(const framing::ProtocolInitiation&);
+
+ public:
+ RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, ConnectionCodec::Factory* f);
+ ~RdmaIOHandler();
+ void init(Rdma::AsynchIO* a);
+ void start(Poller::shared_ptr poller) {aio->start(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);
+};
+
+RdmaIOHandler::RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, qpid::sys::ConnectionCodec::Factory* f) :
+ connection(c),
+ identifier(c->getPeerName()),
+ factory(f),
+ codec(0),
+ readError(false)
+{
+}
+
+void RdmaIOHandler::init(Rdma::AsynchIO* a) {
+ aio = a;
+}
+
+RdmaIOHandler::~RdmaIOHandler() {
+ if (codec)
+ codec->closed();
+ delete codec;
+
+ aio->deferDelete();
+}
+
+void RdmaIOHandler::write(const framing::ProtocolInitiation& data)
+{
+ QPID_LOG(debug, "Rdma: SENT [" << identifier << "] INIT(" << data << ")");
+ Rdma::Buffer* buff = aio->getBuffer();
+ framing::Buffer out(buff->bytes, buff->byteCount);
+ data.encode(out);
+ buff->dataCount = data.encodedSize();
+ aio->queueWrite(buff);
+}
+
+void RdmaIOHandler::close() {
+ aio->queueWriteClose();
+}
+
+// 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() && aio->bufferAvailable()) ) {
+ return;
+ }
+ if (codec == 0) return;
+ if (codec->canEncode()) {
+ Rdma::Buffer* buff = aio->getBuffer();
+ size_t encoded=codec->encode(buff->bytes, buff->byteCount);
+ buff->dataCount = encoded;
+ aio->queueWrite(buff);
+ }
+ if (codec->isClosed())
+ aio->queueWriteClose();
+}
+
+void RdmaIOHandler::initProtocolOut() {
+ // We mustn't have already started the conversation
+ // but we must be able to send
+ assert( codec == 0 );
+ assert( aio->writable() && aio->bufferAvailable() );
+ codec = factory->create(*this, identifier, 0);
+ write(framing::ProtocolInitiation(codec->getVersion()));
+}
+
+void RdmaIOHandler::error(Rdma::AsynchIO&) {
+ close();
+}
+
+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->dataStart, buff->dataCount);
+ }else{
+ // Need to start protocol processing
+ initProtocolIn(buff);
+ }
+ }catch(const std::exception& e){
+ QPID_LOG(error, e.what());
+ readError = true;
+ aio->queueWriteClose();
+ }
+}
+
+void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) {
+ framing::Buffer in(buff->bytes+buff->dataStart, 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, 0);
+
+ // 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;
+ aio->queueWriteClose();
+ }
+ }
+}
+
+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, int16_t port, ConnectionCodec::Factory*, ConnectFailedCallback);
+
+ uint16_t getPort() const;
+ string getHost() 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 {
+ RdmaIOHandler* async = new RdmaIOHandler(ci, f);
+ Rdma::AsynchIO* aio =
+ new Rdma::AsynchIO(ci->getQueuePair(),
+ 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) {
+ async->close();
+ }
+ delete async;
+}
+
+uint16_t RdmaIOProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+string RdmaIOProtocolFactory::getHost() const {
+ //return listener.getSockname();
+ return "";
+}
+
+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;
+
+ SocketAddress sa("",boost::lexical_cast<std::string>(listeningPort));
+ listener.reset(
+ new Rdma::Listener(sa,
+ 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)));
+
+ listener->start(poller);
+}
+
+// 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, int16_t port,
+ ConnectionCodec::Factory* f,
+ ConnectFailedCallback failed)
+{
+ SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ Rdma::Connector* c =
+ new Rdma::Connector(
+ sa,
+ 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));
+
+ c->start(poller);
+}
+
+}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/Runnable.cpp b/cpp/src/qpid/sys/Runnable.cpp
index 30122c682f..325d87c91b 100644
--- a/cpp/src/qpid/sys/Runnable.cpp
+++ b/cpp/src/qpid/sys/Runnable.cpp
@@ -16,7 +16,7 @@
*
*/
-#include "Runnable.h"
+#include "qpid/sys/Runnable.h"
#include <boost/bind.hpp>
namespace qpid {
diff --git a/cpp/src/qpid/sys/SystemInfo.h b/cpp/src/qpid/sys/SecurityLayer.h
index 73c3ca3c17..52bc40e352 100644
--- a/cpp/src/qpid/sys/SystemInfo.h
+++ b/cpp/src/qpid/sys/SecurityLayer.h
@@ -1,7 +1,8 @@
-#ifndef QPID_SYS_SYSTEMINFO_H
-#define QPID_SYS_SYSTEMINFO_H
+#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
@@ -9,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -20,25 +21,22 @@
* under the License.
*
*/
+#include "qpid/sys/Codec.h"
namespace qpid {
namespace sys {
/**
- * Retrieve information about the system we are running on.
- * Results may be dependent on OS/hardware.
+ * Defines interface to a SASL negotiated Security Layer (for
+ * encryption/integrity)
*/
-class SystemInfo
+class SecurityLayer : public Codec
{
public:
- /** Estimate available concurrency, e.g. number of CPU cores.
- * -1 means estimate not available on this platform.
- */
- static long concurrency();
+ virtual void init(Codec*) = 0;
+ virtual ~SecurityLayer() {}
};
}} // namespace qpid::sys
-
-
-#endif /*!QPID_SYS_SYSTEMINFO_H*/
+#endif /*!QPID_SYS_SECURITYLAYER_H*/
diff --git a/cpp/src/qpid/sys/Semaphore.h b/cpp/src/qpid/sys/Semaphore.h
index 3efb7ce2df..9d70f89aeb 100644
--- a/cpp/src/qpid/sys/Semaphore.h
+++ b/cpp/src/qpid/sys/Semaphore.h
@@ -19,7 +19,7 @@
*
*/
-#include "Monitor.h"
+#include "qpid/sys/Monitor.h"
namespace qpid {
namespace sys {
@@ -51,10 +51,22 @@ public:
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);
- if (!count++) monitor.notifyAll();
+ count = 0;
}
private:
diff --git a/cpp/src/qpid/sys/Shlib.cpp b/cpp/src/qpid/sys/Shlib.cpp
index 8fd3f42cc6..342d726876 100644
--- a/cpp/src/qpid/sys/Shlib.cpp
+++ b/cpp/src/qpid/sys/Shlib.cpp
@@ -18,7 +18,7 @@
*
*/
-#include "Shlib.h"
+#include "qpid/sys/Shlib.h"
#include "qpid/log/Statement.h"
diff --git a/cpp/src/qpid/sys/Shlib.h b/cpp/src/qpid/sys/Shlib.h
index e2752dc7d6..7f66cfec14 100644
--- a/cpp/src/qpid/sys/Shlib.h
+++ b/cpp/src/qpid/sys/Shlib.h
@@ -21,10 +21,10 @@
* under the License.
*
*/
-
+
+#include "qpid/CommonImportExport.h"
#include <boost/noncopyable.hpp>
#include <iostream>
-#include <dlfcn.h>
namespace qpid {
namespace sys {
@@ -41,10 +41,10 @@ class Shlib {
Shlib(const std::string& libname) { load(libname.c_str()); }
/** Unload shared library. */
- void unload();
+ QPID_COMMON_EXTERN void unload();
/** Look up symbol. */
- void* getSymbol(const char* 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.
@@ -58,7 +58,7 @@ class Shlib {
private:
void* handle;
- void load(const char* libname);
+ QPID_COMMON_EXTERN void load(const char* libname);
};
/** A shared library handle that unloads the shlib in it's dtor */
@@ -67,7 +67,7 @@ class AutoShlib : public Shlib {
/** Load shared library */
AutoShlib(const std::string& libname) : Shlib(libname) {}
/** Calls unload() */
- ~AutoShlib() throw();
+ QPID_COMMON_EXTERN ~AutoShlib() throw();
};
diff --git a/cpp/src/qpid/sys/Socket.h b/cpp/src/qpid/sys/Socket.h
index dd7ef9a96d..7b50c42a3c 100644
--- a/cpp/src/qpid/sys/Socket.h
+++ b/cpp/src/qpid/sys/Socket.h
@@ -22,47 +22,48 @@
*
*/
-#include "IOHandle.h"
-
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/CommonImportExport.h"
#include <string>
-struct sockaddr;
-
namespace qpid {
namespace sys {
class Duration;
+class SocketAddress;
class Socket : public IOHandle
{
public:
/** Create a socket wrapper for descriptor. */
- Socket();
+ QPID_COMMON_EXTERN Socket();
- /** Create an initialized TCP socket */
- void createTcp() const;
-
/** Set timeout for read and write */
void setTimeout(const Duration& interval) const;
-
+
/** Set socket non blocking */
void setNonblocking() const;
- void connect(const std::string& host, uint16_t port) const;
+ QPID_COMMON_EXTERN void setTcpNoDelay() const;
+
+ QPID_COMMON_EXTERN void connect(const std::string& host, uint16_t port) const;
+ QPID_COMMON_EXTERN void connect(const SocketAddress&) const;
- void close() 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.
*/
- int listen(uint16_t port = 0, int backlog = 10) const;
-
+ QPID_COMMON_EXTERN int listen(uint16_t port = 0, int backlog = 10) const;
+ QPID_COMMON_EXTERN int listen(const SocketAddress&, int backlog = 10) const;
+
/** Returns the "socket name" ie the address bound to
* the near end of the socket
*/
- std::string getSockname() const;
+ QPID_COMMON_EXTERN std::string getSockname() const;
/** Returns the "peer name" ie the address bound to
* the remote end of the socket
@@ -73,14 +74,14 @@ public:
* Returns an address (host and port) for the remote end of the
* socket
*/
- std::string getPeerAddress() const;
+ QPID_COMMON_EXTERN std::string getPeerAddress() const;
/**
* Returns an address (host and port) for the local end of the
* socket
*/
std::string getLocalAddress() const;
- uint16_t getLocalPort() const;
+ QPID_COMMON_EXTERN uint16_t getLocalPort() const;
uint16_t getRemotePort() const;
/**
@@ -92,17 +93,20 @@ public:
/** Accept a connection from a socket that is already listening
* and has an incoming connection
*/
- Socket* accept(struct sockaddr *addr, socklen_t *addrlen) const;
+ QPID_COMMON_EXTERN Socket* 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;
-
- void setTcpNoDelay(bool nodelay) const;
+ 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 connectname;
+ mutable bool nonblocking;
+ mutable bool nodelay;
};
}}
diff --git a/cpp/src/qpid/sys/SocketAddress.h b/cpp/src/qpid/sys/SocketAddress.h
new file mode 100644
index 0000000000..27b9642f2c
--- /dev/null
+++ b/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() const;
+
+private:
+ std::string host;
+ std::string port;
+ mutable ::addrinfo* addrInfo;
+};
+
+}}
+#endif /*!_sys_SocketAddress_h*/
diff --git a/cpp/src/qpid/sys/SslPlugin.cpp b/cpp/src/qpid/sys/SslPlugin.cpp
new file mode 100644
index 0000000000..c143f1f1d0
--- /dev/null
+++ b/cpp/src/qpid/sys/SslPlugin.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/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;
+
+ SslServerOptions() : port(5671),
+ clientAuth(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");
+ }
+};
+
+class SslProtocolFactory : public ProtocolFactory {
+ const bool tcpNoDelay;
+ qpid::sys::ssl::SslSocket listener;
+ const uint16_t listeningPort;
+ std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor;
+
+ public:
+ SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+ void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
+ void connect(Poller::shared_ptr, const std::string& host, int16_t port,
+ ConnectionCodec::Factory*,
+ boost::function2<void, int, std::string> failed);
+
+ uint16_t getPort() const;
+ std::string getHost() 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(info, "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))
+{}
+
+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.getPeerAddress(), f);
+
+ 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.
+}
+
+std::string SslProtocolFactory::getHost() const {
+ return listener.getSockname();
+}
+
+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, int16_t 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/cpp/src/qpid/sys/TCPIOPlugin.cpp b/cpp/src/qpid/sys/TCPIOPlugin.cpp
index f38cf88e45..39ae12c189 100644
--- a/cpp/src/qpid/sys/TCPIOPlugin.cpp
+++ b/cpp/src/qpid/sys/TCPIOPlugin.cpp
@@ -19,12 +19,13 @@
*
*/
-#include "ProtocolFactory.h"
-#include "AsynchIOHandler.h"
-#include "AsynchIO.h"
+#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"
@@ -45,7 +46,7 @@ class AsynchIOProtocolFactory : public ProtocolFactory {
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
void connect(Poller::shared_ptr, const std::string& host, int16_t port,
ConnectionCodec::Factory*,
- boost::function2<void, int, std::string> failed);
+ ConnectFailedCallback);
uint16_t getPort() const;
std::string getHost() const;
@@ -53,6 +54,7 @@ class AsynchIOProtocolFactory : public ProtocolFactory {
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
@@ -65,9 +67,10 @@ static class TCPIOPlugin : public Plugin {
// Only provide to a Broker
if (broker) {
const broker::Broker::Options& opts = broker->getOptions();
- ProtocolFactory::shared_ptr protocol(new AsynchIOProtocolFactory(opts.port, opts.connectionBacklog, opts.tcpNoDelay));
- QPID_LOG(info, "Listening on TCP port " << protocol->getPort());
- broker->registerProtocolFactory(protocol);
+ ProtocolFactory::shared_ptr protocol(new AsynchIOProtocolFactory(opts.port, opts.connectionBacklog,
+ opts.tcpNoDelay));
+ QPID_LOG(notice, "Listening on TCP port " << protocol->getPort());
+ broker->registerProtocolFactory("tcp", protocol);
}
}
} tcpPlugin;
@@ -81,19 +84,20 @@ void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socke
AsynchIOHandler* async = new AsynchIOHandler(s.getPeerAddress(), f);
if (tcpNoDelay) {
- s.setTcpNoDelay(tcpNoDelay);
+ s.setTcpNoDelay();
QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
}
if (isClient)
async->setClient();
- AsynchIO* aio = new AsynchIO(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));
+ 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);
@@ -110,11 +114,20 @@ std::string AsynchIOProtocolFactory::getHost() const {
void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller,
ConnectionCodec::Factory* fact) {
acceptor.reset(
- new AsynchAcceptor(listener,
+ 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, int16_t port,
@@ -128,9 +141,14 @@ void AsynchIOProtocolFactory::connect(
// is no longer needed.
Socket* socket = new Socket();
- new AsynchConnector (*socket, poller, host, port,
- boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, true),
- failed);
+ AsynchConnector::create(*socket,
+ poller,
+ host,
+ port,
+ boost::bind(&AsynchIOProtocolFactory::established,
+ this, poller, _1, fact, true),
+ boost::bind(&AsynchIOProtocolFactory::connectFailed,
+ this, _1, _2, _3, failed));
}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/Time.h b/cpp/src/qpid/sys/Time.h
deleted file mode 100644
index 6501cd0806..0000000000
--- a/cpp/src/qpid/sys/Time.h
+++ /dev/null
@@ -1,154 +0,0 @@
-#ifndef _sys_Time_h
-#define _sys_Time_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 <stdint.h>
-#include <limits>
-#include <iosfwd>
-
-namespace qpid {
-namespace sys {
-
-class Duration;
-
-/** Class to represent an instant in time:
- * The time resolution is in nanosecs, and this is held with 64 bits
- * giving a total time span from about 25 million years ago to 25 million
- * years hence. As an aside the internal time can sensibly be negative
- * meaning before the epoch (probably 1/1/1970 although this class doesn't
- * care).
- *
- * The AbsTime class is a value class and so you don't need to add any accessors
- * to its internal state. If you think you want to replace its value,i
- * You need to construct a new AbsTime and assign it, viz:
- *
- * AbsTime when = AbsTime::now();
- * ...
- * when = AbsTime(when, 2*TIME_SEC); // Advance timer 2 secs
- *
- * If for some reason you need access to the internal nanosec value you need
- * to convert the AbsTime to a Duration and use its conversion to int64_t, viz:
- *
- * AbsTime now = AbsTime::now();
- *
- * int64_t ns = Duration(now);
- *
- * However note that the nanosecond value that is returned here is not defined to be
- * anything in particular and could vary from platform to platform.
- *
- * There are some sensible operations that are currently missing from AbsTime, but
- * nearly all that's needed can be done with a mixture of AbsTimes and Durations.
- *
- * For example, convenience operators to add a Duration and AbsTime returning an AbsTime
- * would fit here (although you can already perform the operation with one of the AbsTime
- * constructors). However trying to add 2 AbsTimes doesn't make sense.
- */
-class AbsTime {
- static int64_t max() { return std::numeric_limits<int64_t>::max(); }
- int64_t time_ns;
-
- friend class Duration;
-
-public:
- inline AbsTime() {}
- inline AbsTime(const AbsTime& time0, const Duration& duration);
- // Default assignment operation fine
- // Default copy constructor fine
-
- static AbsTime now();
- inline static AbsTime FarFuture();
- bool operator==(const AbsTime& t) const { return t.time_ns == time_ns; }
- template <class S> void serialize(S& s) { s(time_ns); }
-
- friend bool operator<(const AbsTime& a, const AbsTime& b);
- friend bool operator>(const AbsTime& a, const AbsTime& b);
- friend std::ostream& operator << (std::ostream&, const AbsTime&);
-};
-
-std::ostream& operator << (std::ostream&, const AbsTime&);
-
-/** Class to represent the duration between instants of time:
- * As AbsTime this class also uses nanosecs for its time
- * resolution. For the most part a duration can be dealt with like a
- * 64 bit integer, and indeed there is an implicit conversion which
- * makes this quite conveient.
- */
-class Duration {
- static int64_t max() { return std::numeric_limits<int64_t>::max(); }
- int64_t nanosecs;
-
- friend class AbsTime;
-
-public:
- inline Duration(int64_t time0);
- inline explicit Duration(const AbsTime& time0);
- inline explicit Duration(const AbsTime& start, const AbsTime& finish);
- inline operator int64_t() const;
-};
-
-std::ostream& operator << (std::ostream&, const Duration&);
-
-AbsTime::AbsTime(const AbsTime& t, const Duration& d) :
- time_ns(d == Duration::max() ? max() : t.time_ns+d.nanosecs)
-{}
-
-AbsTime AbsTime::FarFuture() { AbsTime ff; ff.time_ns = max(); return ff;}
-
-inline AbsTime now() { return AbsTime::now(); }
-
-inline bool operator<(const AbsTime& a, const AbsTime& b) { return a.time_ns < b.time_ns; }
-inline bool operator>(const AbsTime& a, const AbsTime& b) { return a.time_ns > b.time_ns; }
-
-Duration::Duration(int64_t time0) :
- nanosecs(time0)
-{}
-
-Duration::Duration(const AbsTime& time0) :
- nanosecs(time0.time_ns)
-{}
-
-Duration::Duration(const AbsTime& start, const AbsTime& finish) :
- nanosecs(finish.time_ns - start.time_ns)
-{}
-
-Duration::operator int64_t() const
-{ return nanosecs; }
-
-/** Nanoseconds per second. */
-const Duration TIME_SEC = 1000*1000*1000;
-/** Nanoseconds per millisecond */
-const Duration TIME_MSEC = 1000*1000;
-/** Nanoseconds per microseconds. */
-const Duration TIME_USEC = 1000;
-/** Nanoseconds per nanosecond. */
-const Duration TIME_NSEC = 1;
-
-/** Value to represent an infinite timeout */
-const Duration TIME_INFINITE = std::numeric_limits<int64_t>::max();
-
-/** Time greater than any other time */
-const AbsTime FAR_FUTURE = AbsTime::FarFuture();
-
-}}
-
-#endif /*!_sys_Time_h*/
diff --git a/cpp/src/qpid/sys/Timer.cpp b/cpp/src/qpid/sys/Timer.cpp
new file mode 100644
index 0000000000..c18fd93538
--- /dev/null
+++ b/cpp/src/qpid/sys/Timer.cpp
@@ -0,0 +1,179 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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) :
+ sortTime(AbsTime::FarFuture()),
+ period(timeout),
+ nextFireTime(AbsTime::now(), timeout),
+ cancelled(false)
+{}
+
+TimerTask::TimerTask(AbsTime time) :
+ 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, "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)
+{
+ 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) {
+ if (delay > 500 * TIME_MSEC) {
+ QPID_LOG(debug, "cancelled Timer woken up " << delay / TIME_MSEC << "ms late");
+ }
+ continue;
+ } else if(Duration(t->nextFireTime, start) >= 0) {
+ Monitor::ScopedUnlock u(monitor);
+ t->fireTask();
+ // Warn on callback overrun
+ AbsTime end(AbsTime::now());
+ Duration overrun(tasks.top()->nextFireTime, end);
+ bool late = delay > 50 * TIME_MSEC;
+ bool overran = overrun > 2 * TIME_MSEC;
+ if (late)
+ if (overran) {
+ QPID_LOG(warning,
+ "Timer woken up " << delay / TIME_MSEC << "ms late, "
+ "overrunning by " << overrun / TIME_MSEC << "ms [taking "
+ << Duration(start, end) << "]");
+ } else {
+ QPID_LOG(warning, "Timer woken up " << delay / TIME_MSEC << "ms late");
+ } else if (overran) {
+ QPID_LOG(warning,
+ "Timer callback overran by " << overrun / TIME_MSEC << "ms [taking "
+ << Duration(start, end) << "]");
+ }
+ 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);
+ }
+ }
+ 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();
+}
+
+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/cpp/src/qpid/sys/Timer.h b/cpp/src/qpid/sys/Timer.h
new file mode 100644
index 0000000000..5748503841
--- /dev/null
+++ b/cpp/src/qpid/sys/Timer.h
@@ -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.
+ *
+ */
+#ifndef sys_Timer
+#define sys_Timer
+
+#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>&);
+
+ AbsTime sortTime;
+ Duration period;
+ AbsTime nextFireTime;
+ Mutex callbackLock;
+ volatile bool cancelled;
+
+ bool readyToFire() const;
+ void fireTask();
+
+public:
+ QPID_COMMON_EXTERN TimerTask(Duration period);
+ QPID_COMMON_EXTERN TimerTask(AbsTime fireTime);
+ QPID_COMMON_EXTERN virtual ~TimerTask();
+
+ QPID_COMMON_EXTERN void setupNextFire();
+ QPID_COMMON_EXTERN void restart();
+ QPID_COMMON_EXTERN void cancel();
+
+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 ~Timer();
+
+ QPID_COMMON_EXTERN void add(boost::intrusive_ptr<TimerTask> task);
+ QPID_COMMON_EXTERN void start();
+ QPID_COMMON_EXTERN void stop();
+};
+
+
+}}
+
+
+#endif
diff --git a/cpp/src/qpid/sys/alloca.h b/cpp/src/qpid/sys/alloca.h
new file mode 100644
index 0000000000..e989670e4f
--- /dev/null
+++ b/cpp/src/qpid/sys/alloca.h
@@ -0,0 +1,39 @@
+#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)) && defined(_MSC_VER)
+#include <malloc.h>
+#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 /*!QPID_SYS_ALLOCA_H*/
diff --git a/cpp/src/qpid/sys/apr/APRBase.cpp b/cpp/src/qpid/sys/apr/APRBase.cpp
index 724c489303..8bdba66bdc 100644
--- a/cpp/src/qpid/sys/apr/APRBase.cpp
+++ b/cpp/src/qpid/sys/apr/APRBase.cpp
@@ -20,7 +20,7 @@
*/
#include <iostream>
#include "qpid/log/Statement.h"
-#include "APRBase.h"
+#include "qpid/sys/apr/APRBase.h"
using namespace qpid::sys;
diff --git a/cpp/src/qpid/sys/apr/APRPool.cpp b/cpp/src/qpid/sys/apr/APRPool.cpp
index e8b71f6e8a..e221bfc2f1 100644
--- a/cpp/src/qpid/sys/apr/APRPool.cpp
+++ b/cpp/src/qpid/sys/apr/APRPool.cpp
@@ -19,8 +19,8 @@
*
*/
-#include "APRPool.h"
-#include "APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
#include <boost/pool/detail/singleton.hpp>
using namespace qpid::sys;
diff --git a/cpp/src/qpid/sys/apr/Condition.h b/cpp/src/qpid/sys/apr/Condition.h
index 5e544219ab..66d465ca75 100644
--- a/cpp/src/qpid/sys/apr/Condition.h
+++ b/cpp/src/qpid/sys/apr/Condition.h
@@ -22,7 +22,7 @@
*
*/
-#include "APRPool.h"
+#include "qpid/sys/apr/APRPool.h"
#include "qpid/sys/Mutex.h"
#include "qpid/sys/Time.h"
diff --git a/cpp/src/qpid/sys/apr/Mutex.h b/cpp/src/qpid/sys/apr/Mutex.h
index 51089c98ff..cb75f5b339 100644
--- a/cpp/src/qpid/sys/apr/Mutex.h
+++ b/cpp/src/qpid/sys/apr/Mutex.h
@@ -19,8 +19,8 @@
*
*/
-#include "APRBase.h"
-#include "APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
#include <boost/noncopyable.hpp>
#include <apr_thread_mutex.h>
diff --git a/cpp/src/qpid/sys/apr/Shlib.cpp b/cpp/src/qpid/sys/apr/Shlib.cpp
index b0ba706713..b7ee13a03b 100644
--- a/cpp/src/qpid/sys/apr/Shlib.cpp
+++ b/cpp/src/qpid/sys/apr/Shlib.cpp
@@ -19,8 +19,8 @@
*/
#include "qpid/sys/Shlib.h"
-#include "APRBase.h"
-#include "APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
#include <apr_dso.h>
namespace qpid {
diff --git a/cpp/src/qpid/sys/apr/Socket.cpp b/cpp/src/qpid/sys/apr/Socket.cpp
index 577268844a..d9024d11c1 100644
--- a/cpp/src/qpid/sys/apr/Socket.cpp
+++ b/cpp/src/qpid/sys/apr/Socket.cpp
@@ -22,8 +22,8 @@
#include "qpid/sys/Socket.h"
-#include "APRBase.h"
-#include "APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
#include <apr_network_io.h>
diff --git a/cpp/src/qpid/sys/apr/Thread.cpp b/cpp/src/qpid/sys/apr/Thread.cpp
index 3369ef7eb1..b52d0e6ace 100644
--- a/cpp/src/qpid/sys/apr/Thread.cpp
+++ b/cpp/src/qpid/sys/apr/Thread.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "Thread.h"
+#include "qpid/sys/apr/Thread.h"
#include "qpid/sys/Runnable.h"
using namespace qpid::sys;
diff --git a/cpp/src/qpid/sys/apr/Thread.h b/cpp/src/qpid/sys/apr/Thread.h
index 8cbbc0456e..6cc63db5c9 100644
--- a/cpp/src/qpid/sys/apr/Thread.h
+++ b/cpp/src/qpid/sys/apr/Thread.h
@@ -22,8 +22,8 @@
*
*/
-#include "APRPool.h"
-#include "APRBase.h"
+#include "qpid/sys/apr/APRPool.h"
+#include "qpid/sys/apr/APRBase.h"
#include <apr_thread_proc.h>
#include <apr_portable.h>
diff --git a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
new file mode 100644
index 0000000000..454ce62495
--- /dev/null
+++ b/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 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/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h
new file mode 100644
index 0000000000..1645cf1a58
--- /dev/null
+++ b/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/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
index 5e20e312e0..d7f64f3b4c 100644
--- a/cpp/src/qpid/sys/epoll/EpollPoller.cpp
+++ b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -25,18 +25,20 @@
#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 <vector>
+#include <queue>
#include <exception>
namespace qpid {
namespace sys {
-// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used
+// 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
@@ -45,6 +47,7 @@ DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerH
class PollerHandlePrivate {
friend class Poller;
+ friend class PollerPrivate;
friend class PollerHandle;
enum FDStat {
@@ -53,28 +56,36 @@ class PollerHandlePrivate {
INACTIVE,
HUNGUP,
MONITORED_HUNGUP,
+ INTERRUPTED,
+ INTERRUPTED_HUNGUP,
DELETED
};
- int fd;
::__uint32_t events;
+ const IOHandlePrivate* ioHandle;
PollerHandle* pollerHandle;
FDStat stat;
Mutex lock;
- PollerHandlePrivate(int f, PollerHandle* p) :
- fd(f),
+ 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) ? MONITORED_HUNGUP : MONITORED;
+ stat = (stat == HUNGUP || stat == INTERRUPTED_HUNGUP)
+ ? MONITORED_HUNGUP
+ : MONITORED;
}
bool isInactive() const {
@@ -94,14 +105,27 @@ class PollerHandlePrivate {
}
bool isHungup() const {
- return stat == MONITORED_HUNGUP || stat == HUNGUP;
+ 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;
}
@@ -112,13 +136,22 @@ class PollerHandlePrivate {
};
PollerHandle::PollerHandle(const IOHandle& h) :
- impl(new PollerHandlePrivate(toFd(h.impl), this))
+ impl(new PollerHandlePrivate(h.impl, this))
{}
PollerHandle::~PollerHandle() {
+ {
ScopedLock<Mutex> l(impl->lock);
- if (impl->isActive()) {
+ if (impl->isDeleted()) {
+ return;
+ }
+ impl->pollerHandle = 0;
+ if (impl->isInterrupted()) {
impl->setDeleted();
+ return;
+ }
+ assert(impl->isIdle());
+ impl->setDeleted();
}
PollerHandleDeletionManager.markForDeletion(impl);
}
@@ -134,7 +167,7 @@ class PollerPrivate {
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
@@ -144,31 +177,69 @@ class PollerPrivate {
// 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;
+ ::sigset_t sigMask;
static ::__uint32_t directionToEpollEvent(Poller::Direction dir) {
switch (dir) {
- case Poller::IN: return ::EPOLLIN;
- case Poller::OUT: return ::EPOLLOUT;
- case Poller::INOUT: return ::EPOLLIN | ::EPOLLOUT;
+ 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!
@@ -188,81 +259,161 @@ class PollerPrivate {
epollFd(::epoll_create(DefaultFds)),
isShutdown(false) {
QPID_POSIX_CHECK(epollFd);
+ ::sigemptyset(&sigMask);
+ // 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::addFd(PollerHandle& handle, Direction dir) {
+void Poller::registerHandle(PollerHandle& handle) {
PollerHandlePrivate& eh = *handle.impl;
ScopedLock<Mutex> l(eh.lock);
+ assert(eh.isIdle());
+
::epoll_event epe;
- int op;
-
- if (eh.isIdle()) {
- op = EPOLL_CTL_ADD;
- epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT;
- } else {
- assert(eh.isActive());
- op = EPOLL_CTL_MOD;
- epe.events = eh.events | PollerPrivate::directionToEpollEvent(dir);
- }
+ epe.events = ::EPOLLONESHOT;
+ epe.data.u64 = 0; // Keep valgrind happy
epe.data.ptr = &eh;
-
- QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, op, eh.fd, &epe));
-
- // Record monitoring state of this fd
- eh.events = epe.events;
+
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, eh.fd(), &epe));
+
eh.setActive();
}
-void Poller::delFd(PollerHandle& handle) {
+void Poller::unregisterHandle(PollerHandle& handle) {
PollerHandlePrivate& eh = *handle.impl;
ScopedLock<Mutex> l(eh.lock);
assert(!eh.isIdle());
- int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0);
+
+ 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);
+ QPID_POSIX_CHECK(rc);
}
+
eh.setIdle();
}
-// modFd is equivalent to delFd followed by addFd
-void Poller::modFd(PollerHandle& handle, Direction dir) {
+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 = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT;
+ 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));
-
- // Record monitoring state of this fd
- eh.events = epe.events;
- eh.setActive();
+
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
}
-void Poller::rearmFd(PollerHandle& handle) {
+void Poller::unmonitorHandle(PollerHandle& handle, Direction dir) {
PollerHandlePrivate& eh = *handle.impl;
ScopedLock<Mutex> l(eh.lock);
- assert(eh.isInactive());
+ 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;
+ 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));
-
- eh.setActive();
+ QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
}
void Poller::shutdown() {
@@ -273,63 +424,182 @@ void Poller::shutdown() {
if (impl->isShutdown)
return;
- // Don't use any locking here - isshutdown will be visible to all
+ // 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;
-
- // Add always readable fd to epoll (not EPOLLONESHOT)
- int fd = impl->alwaysReadable.getFD();
- ::epoll_event epe;
- epe.events = ::EPOLLIN;
- epe.data.ptr = 0;
- QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, fd, &epe));
+
+ 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);
+
+ 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();
+ 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();
}
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 interupted
+ // Repeat until we weren't interrupted by signal
do {
PollerHandleDeletionManager.markAllUnusedInThisThread();
+ // Need to run on kernels without epoll_pwait()
+ // - fortunately in this case we don't really need the atomicity of epoll_pwait()
+#if 1
+ sigset_t os;
+ pthread_sigmask(SIG_SETMASK, &impl->sigMask, &os);
int rc = ::epoll_wait(impl->epollFd, &epe, 1, timeoutMs);
-
- if (impl->isShutdown) {
- PollerHandleDeletionManager.markAllUnusedInThisThread();
- return Event(0, SHUTDOWN);
- }
-
+ pthread_sigmask(SIG_SETMASK, &os, 0);
+#else
+ int rc = ::epoll_pwait(impl->epollFd, &epe, 1, timeoutMs, &impl->sigMask);
+#endif
+
if (rc ==-1 && errno != EINTR) {
QPID_POSIX_CHECK(rc);
} else if (rc > 0) {
assert(rc == 1);
- PollerHandlePrivate& eh = *static_cast<PollerHandlePrivate*>(epe.data.ptr);
-
+ void* dataPtr = epe.data.ptr;
+
+ // Check if this is an interrupt
+ PollerPrivate::InterruptHandle& interruptHandle = impl->interruptHandle;
+ if (dataPtr == &interruptHandle) {
+ 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));
- } else if (eh.isDeleted()) {
- // The handle has been deleted whilst still active and so must be removed
- // from the poller
- int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0);
- // Ignore EBADF since it's quite likely that we could race with closing the fd
- if (rc == -1 && errno != EBADF) {
- QPID_POSIX_CHECK(rc);
- }
}
}
// We only get here if one of the following:
@@ -340,10 +610,12 @@ Poller::Event Poller::wait(Duration timeout) {
// 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, but we were interrupted then we have to return
- // with a timeout as we don't know how long we've waited so far and so we can't
- // continue the wait.
- if (rc == 0 || timeoutMs != -1) {
+ // 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);
}
diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp
index 7598eefe83..67b5cf0534 100644
--- a/cpp/src/qpid/sys/posix/AsynchIO.cpp
+++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp
@@ -21,13 +21,16 @@
#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 "check.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
+// 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>
@@ -35,18 +38,21 @@
#include <string.h>
#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
using namespace qpid::sys;
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);
-}
+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
@@ -65,14 +71,37 @@ __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, Callback callback) :
+};
+
+AsynchAcceptor::AsynchAcceptor(const Socket& s,
+ AsynchAcceptor::Callback callback) :
acceptedCallback(callback),
handle(s, boost::bind(&AsynchAcceptor::readable, this, _1), 0, 0),
socket(s) {
s.setNonblocking();
- ignoreSigpipe();
+}
+
+AsynchAcceptor::~AsynchAcceptor() {
+ handle.stopWatch();
}
void AsynchAcceptor::start(Poller::shared_ptr poller) {
@@ -89,7 +118,7 @@ void AsynchAcceptor::readable(DispatchHandle& h) {
// TODO: Currently we ignore the peers address, perhaps we should
// log it or use it for connection acceptance.
try {
- s = socket.accept(0, 0);
+ s = socket.accept();
if (s) {
acceptedCallback(*s);
} else {
@@ -97,6 +126,7 @@ void AsynchAcceptor::readable(DispatchHandle& h) {
}
} catch (const std::exception& e) {
QPID_LOG(error, "Could not accept socket: " << e.what());
+ break;
}
} while (true);
@@ -104,8 +134,32 @@ void AsynchAcceptor::readable(DispatchHandle& h) {
}
/*
- * Asynch Connector
+ * 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);
+ void failure(int, const std::string&);
+
+private:
+ ConnectedCallback connCallback;
+ FailedCallback failCallback;
+ std::string errMsg;
+ const Socket& socket;
+
+public:
+ AsynchConnector(const Socket& socket,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb);
+};
AsynchConnector::AsynchConnector(const Socket& s,
Poller::shared_ptr poller,
@@ -122,12 +176,17 @@ AsynchConnector::AsynchConnector(const Socket& s,
socket(s)
{
socket.setNonblocking();
+ SocketAddress sa(hostname, boost::lexical_cast<std::string>(port));
try {
- socket.connect(hostname, port);
- startWatch(poller);
+ socket.connect(sa);
} catch(std::exception& e) {
- failure(-1, std::string(e.what()));
+ // Defer reporting failure
+ startWatch(poller);
+ errMsg = e.what();
+ DispatchHandle::call(boost::bind(&AsynchConnector::failure, this, -1, errMsg));
+ return;
}
+ startWatch(poller);
}
void AsynchConnector::connComplete(DispatchHandle& h)
@@ -139,25 +198,87 @@ void AsynchConnector::connComplete(DispatchHandle& h)
connCallback(socket);
DispatchHandle::doDelete();
} else {
- // TODO: This need to be fixed as strerror isn't thread safe
- failure(errCode, std::string(::strerror(errCode)));
+ failure(errCode, strError(errCode));
}
}
-void AsynchConnector::failure(int errCode, std::string message)
+void AsynchConnector::failure(int errCode, const std::string& message)
{
- if (failCallback)
- failCallback(errCode, message);
-
- socket.close();
- delete &socket;
+ failCallback(socket, errCode, message);
DispatchHandle::doDelete();
}
/*
- * Asynch reader/writer
+ * 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) :
@@ -174,7 +295,8 @@ AsynchIO::AsynchIO(const Socket& s,
idleCallback(iCb),
socket(s),
queuedClose(false),
- writePending(false) {
+ writePending(false),
+ readingStopped(false) {
s.setNonblocking();
}
@@ -202,8 +324,11 @@ void AsynchIO::queueReadBuffer(BufferBase* buff) {
assert(buff);
buff->dataStart = 0;
buff->dataCount = 0;
+
+ bool queueWasEmpty = bufferQueue.empty();
bufferQueue.push_back(buff);
- DispatchHandle::rewatchRead();
+ if (queueWasEmpty && !readingStopped)
+ DispatchHandle::rewatchRead();
}
void AsynchIO::unread(BufferBase* buff) {
@@ -212,15 +337,18 @@ void AsynchIO::unread(BufferBase* buff) {
memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount);
buff->dataStart = 0;
}
+
+ bool queueWasEmpty = bufferQueue.empty();
bufferQueue.push_front(buff);
- DispatchHandle::rewatchRead();
+ 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) {
- bufferQueue.push_front(buff);
+ queueReadBuffer(buff);
return;
} else {
writeQueue.push_front(buff);
@@ -229,6 +357,7 @@ void AsynchIO::queueWrite(BufferBase* buff) {
DispatchHandle::rewatchWrite();
}
+// This can happen outside the callback context
void AsynchIO::notifyPendingWrite() {
writePending = true;
DispatchHandle::rewatchWrite();
@@ -239,6 +368,33 @@ void AsynchIO::queueWriteClose() {
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
*/
@@ -277,6 +433,11 @@ void AsynchIO::readable(DispatchHandle& h) {
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;
@@ -303,7 +464,7 @@ void AsynchIO::readable(DispatchHandle& h) {
break;
} else {
// Report error then just treat as a socket disconnect
- QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(rc) << "(" << rc << ")" );
+ QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(errno) << "(" << errno << ")" );
eofCallback(*this);
h.unwatchRead();
break;
@@ -427,3 +588,33 @@ void AsynchIO::close(DispatchHandle& h) {
}
}
+} // namespace posix
+
+AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
+ Callback callback)
+{
+ return new posix::AsynchAcceptor(s, callback);
+}
+
+AsynchConnector* AsynchConnector::create(const Socket& s,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb)
+{
+ return new posix::AsynchConnector(s, poller, 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/cpp/src/qpid/sys/posix/Condition.h b/cpp/src/qpid/sys/posix/Condition.h
deleted file mode 100644
index 86d6500ee9..0000000000
--- a/cpp/src/qpid/sys/posix/Condition.h
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef _sys_posix_Condition_h
-#define _sys_posix_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 "PrivatePosix.h"
-
-#include "qpid/sys/Mutex.h"
-#include "qpid/sys/Time.h"
-
-#include <time.h>
-#include <sys/errno.h>
-#include <boost/noncopyable.hpp>
-
-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:
- pthread_cond_t condition;
-};
-
-Condition::Condition() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_cond_init(&condition, 0));
-}
-
-Condition::~Condition() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_cond_destroy(&condition));
-}
-
-void Condition::wait(Mutex& mutex) {
- QPID_POSIX_ASSERT_THROW_IF(pthread_cond_wait(&condition, &mutex.mutex));
-}
-
-bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){
- struct timespec ts;
- toTimespec(ts, Duration(absoluteTime));
- int status = pthread_cond_timedwait(&condition, &mutex.mutex, &ts);
- if (status != 0) {
- if (status == ETIMEDOUT) return false;
- throw QPID_POSIX_ERROR(status);
- }
- return true;
-}
-
-void Condition::notify(){
- QPID_POSIX_ASSERT_THROW_IF(pthread_cond_signal(&condition));
-}
-
-void Condition::notifyAll(){
- QPID_POSIX_ASSERT_THROW_IF(pthread_cond_broadcast(&condition));
-}
-
-}}
-#endif /*!_sys_posix_Condition_h*/
diff --git a/cpp/src/qpid/sys/posix/FileSysDir.cpp b/cpp/src/qpid/sys/posix/FileSysDir.cpp
new file mode 100755
index 0000000000..22dc487e74
--- /dev/null
+++ b/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/cpp/src/qpid/sys/posix/Fork.cpp b/cpp/src/qpid/sys/posix/Fork.cpp
index ec3af620ef..a0d404a16e 100644
--- a/cpp/src/qpid/sys/posix/Fork.cpp
+++ b/cpp/src/qpid/sys/posix/Fork.cpp
@@ -22,7 +22,9 @@
#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>
diff --git a/cpp/src/qpid/sys/posix/IOHandle.cpp b/cpp/src/qpid/sys/posix/IOHandle.cpp
index 80b487eadc..9c049ee1de 100644
--- a/cpp/src/qpid/sys/posix/IOHandle.cpp
+++ b/cpp/src/qpid/sys/posix/IOHandle.cpp
@@ -21,7 +21,7 @@
#include "qpid/sys/IOHandle.h"
-#include "PrivatePosix.h"
+#include "qpid/sys/posix/PrivatePosix.h"
namespace qpid {
namespace sys {
@@ -31,6 +31,8 @@ int toFd(const IOHandlePrivate* h)
return h->fd;
}
+NullIOHandle DummyIOHandle;
+
IOHandle::IOHandle(IOHandlePrivate* h) :
impl(h)
{}
diff --git a/cpp/src/qpid/sys/posix/LockFile.cpp b/cpp/src/qpid/sys/posix/LockFile.cpp
new file mode 100755
index 0000000000..1862ff6ac9
--- /dev/null
+++ b/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/cpp/src/qpid/sys/posix/LockFile.h b/cpp/src/qpid/sys/posix/LockFile.h
deleted file mode 100644
index 027735e759..0000000000
--- a/cpp/src/qpid/sys/posix/LockFile.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef _sys_posix_LockFile_h
-#define _sys_posix_LockFile_h
-
-#include "check.h"
-
-#include <boost/noncopyable.hpp>
-#include <string>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.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.
- *
- */
-namespace qpid {
-namespace sys {
-
-class LockFile : private boost::noncopyable {
-public:
- LockFile(const std::string& path_, bool create):
- path(path_), fd(-1), created(create) {
- errno = 0;
- int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR;
- fd = ::open(path.c_str(), flags, 0644);
- if (fd < 0) throw ErrnoException("Cannot open " + path, errno);
- if (::lockf(fd, F_TLOCK, 0) < 0) throw ErrnoException("Cannot lock " + path, errno);
- }
-
- ~LockFile() {
- if (fd >= 0) {
- (void) ::lockf(fd, F_ULOCK, 0); // Suppress warnings about ignoring return value.
- ::close(fd);
- }
- }
-
- std::string path;
- int fd;
- bool created;
-};
-
-}
-}
-#endif /*!_sys_posix_LockFile_h*/
diff --git a/cpp/src/qpid/sys/posix/Mutex.h b/cpp/src/qpid/sys/posix/Mutex.h
deleted file mode 100644
index cd5a8affd4..0000000000
--- a/cpp/src/qpid/sys/posix/Mutex.h
+++ /dev/null
@@ -1,158 +0,0 @@
-#ifndef _sys_posix_Mutex_h
-#define _sys_posix_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 "check.h"
-
-#include <pthread.h>
-#include <boost/noncopyable.hpp>
-
-namespace qpid {
-namespace sys {
-
-class Condition;
-
-/**
- * Mutex lock.
- */
-class Mutex : private boost::noncopyable {
- friend class Condition;
- static const pthread_mutexattr_t* getAttribute();
-
-public:
- typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock;
- typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock;
-
- inline Mutex();
- inline ~Mutex();
- inline void lock();
- inline void unlock();
- inline bool trylock();
-
-
-protected:
- pthread_mutex_t mutex;
-};
-
-/**
- * RW lock.
- */
-class RWlock : private boost::noncopyable {
- friend class Condition;
-
-public:
- typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock;
- typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock;
-
- inline RWlock();
- inline ~RWlock();
- inline void wlock(); // will write-lock
- inline void rlock(); // will read-lock
- inline void unlock();
- inline void trywlock(); // will write-try
- inline void tryrlock(); // will read-try
-
-protected:
- pthread_rwlock_t rwlock;
-};
-
-
-/**
- * PODMutex is a POD, can be static-initialized with
- * PODMutex m = QPID_PODMUTEX_INITIALIZER
- */
-struct PODMutex
-{
- typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock;
-
- inline void lock();
- inline void unlock();
- inline bool trylock();
-
- // Must be public to be a POD:
- pthread_mutex_t mutex;
-};
-
-#define QPID_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
-
-void PODMutex::lock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex));
-}
-
-void PODMutex::unlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex));
-}
-
-bool PODMutex::trylock() {
- return pthread_mutex_trylock(&mutex) == 0;
-}
-
-Mutex::Mutex() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_init(&mutex, getAttribute()));
-}
-
-Mutex::~Mutex(){
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_destroy(&mutex));
-}
-
-void Mutex::lock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex));
-}
-
-void Mutex::unlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex));
-}
-
-bool Mutex::trylock() {
- return pthread_mutex_trylock(&mutex) == 0;
-}
-
-
-RWlock::RWlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_init(&rwlock, NULL));
-}
-
-RWlock::~RWlock(){
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_destroy(&rwlock));
-}
-
-void RWlock::wlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_wrlock(&rwlock));
-}
-
-void RWlock::rlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_rdlock(&rwlock));
-}
-
-void RWlock::unlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_unlock(&rwlock));
-}
-
-void RWlock::trywlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_trywrlock(&rwlock));
-}
-
-void RWlock::tryrlock() {
- QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_tryrdlock(&rwlock));
-}
-
-
-}}
-#endif /*!_sys_posix_Mutex_h*/
diff --git a/cpp/src/qpid/sys/posix/PidFile.h b/cpp/src/qpid/sys/posix/PidFile.h
new file mode 100644
index 0000000000..fb19d407f4
--- /dev/null
+++ b/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/cpp/src/qpid/sys/posix/PipeHandle.cpp b/cpp/src/qpid/sys/posix/PipeHandle.cpp
new file mode 100755
index 0000000000..4b19783338
--- /dev/null
+++ b/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/cpp/src/qpid/sys/posix/PollableCondition.cpp b/cpp/src/qpid/sys/posix/PollableCondition.cpp
new file mode 100644
index 0000000000..b22a615a54
--- /dev/null
+++ b/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/cpp/src/qpid/sys/posix/Shlib.cpp b/cpp/src/qpid/sys/posix/Shlib.cpp
index 1552aa06b5..299331103c 100644
--- a/cpp/src/qpid/sys/posix/Shlib.cpp
+++ b/cpp/src/qpid/sys/posix/Shlib.cpp
@@ -27,11 +27,11 @@ namespace qpid {
namespace sys {
void Shlib::load(const char* name) {
- dlerror();
+ ::dlerror();
handle = ::dlopen(name, RTLD_NOW);
const char* error = ::dlerror();
if (error) {
- throw Exception(QPID_MSG(error));
+ throw Exception(QPID_MSG(error << ": " << name));
}
}
@@ -52,7 +52,7 @@ void* Shlib::getSymbol(const char* name) {
void* sym = ::dlsym(handle, name);
const char* error = ::dlerror();
if (error)
- throw Exception(QPID_MSG(error));
+ throw Exception(QPID_MSG(error << ": " << name));
return sym;
}
diff --git a/cpp/src/qpid/sys/posix/Socket.cpp b/cpp/src/qpid/sys/posix/Socket.cpp
index 415d5293ef..7b906f33e8 100644
--- a/cpp/src/qpid/sys/posix/Socket.cpp
+++ b/cpp/src/qpid/sys/posix/Socket.cpp
@@ -21,8 +21,9 @@
#include "qpid/sys/Socket.h"
-#include "check.h"
-#include "PrivatePosix.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>
@@ -36,6 +37,7 @@
#include <iostream>
#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
namespace qpid {
namespace sys {
@@ -95,22 +97,33 @@ std::string getService(int fd, bool local)
}
Socket::Socket() :
- IOHandle(new IOHandlePrivate)
-{
- createTcp();
-}
+ IOHandle(new IOHandlePrivate),
+ nonblocking(false),
+ nodelay(false)
+{}
Socket::Socket(IOHandlePrivate* h) :
- IOHandle(h)
+ IOHandle(h),
+ nonblocking(false),
+ nodelay(false)
{}
-void Socket::createTcp() const
+void Socket::createSocket(const SocketAddress& sa) const
{
int& socket = impl->fd;
if (socket != -1) Socket::close();
- int s = ::socket (PF_INET, SOCK_STREAM, 0);
+ 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::setTimeout(const Duration& interval) const
@@ -123,40 +136,42 @@ void Socket::setTimeout(const Duration& interval) const
}
void Socket::setNonblocking() const {
- QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK));
+ int& socket = impl->fd;
+ nonblocking = true;
+ if (socket != -1) {
+ QPID_POSIX_CHECK(::fcntl(socket, F_SETFL, O_NONBLOCK));
+ }
}
-namespace {
-const char* h_errstr(int e) {
- switch (e) {
- case HOST_NOT_FOUND: return "Host not found";
- case NO_ADDRESS: return "Name does not have an IP address";
- case TRY_AGAIN: return "A temporary error occurred on an authoritative name server.";
- case NO_RECOVERY: return "Non-recoverable name server error";
- default: return "Unknown error";
+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, uint16_t port) const
{
- std::stringstream namestream;
- namestream << host << ":" << port;
- connectname = namestream.str();
+ SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ connect(sa);
+}
+
+void Socket::connect(const SocketAddress& addr) const
+{
+ connectname = addr.asString();
+
+ createSocket(addr);
const int& socket = impl->fd;
- struct sockaddr_in name;
- name.sin_family = AF_INET;
- name.sin_port = htons(port);
- // TODO: Be good to make this work for IPv6 as well as IPv4
- // Use more modern lookup functions
- struct hostent* hp = gethostbyname ( host.c_str() );
- if (hp == 0)
- throw Exception(QPID_MSG("Cannot resolve " << host << ": " << h_errstr(h_errno)));
- ::memcpy(&name.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
- if ((::connect(socket, (struct sockaddr*)(&name), sizeof(name)) < 0) &&
- (errno != EINPROGRESS))
- throw qpid::Exception(QPID_MSG(strError(errno) << ": " << host << ":" << port));
+ // 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) << ": " << connectname));
+ }
}
void
@@ -170,18 +185,24 @@ Socket::close() const
int Socket::listen(uint16_t port, int backlog) const
{
+ SocketAddress sa("", boost::lexical_cast<std::string>(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)));
- 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 (::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 " << port << ": " << strError(errno)));
-
+ 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);
@@ -189,9 +210,9 @@ int Socket::listen(uint16_t port, int backlog) const
return ntohs(name.sin_port);
}
-Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const
+Socket* Socket::accept() const
{
- int afd = ::accept(impl->fd, addr, addrlen);
+ int afd = ::accept(impl->fd, 0, 0);
if ( afd >= 0)
return new Socket(new IOHandlePrivate(afd));
else if (errno == EAGAIN)
@@ -221,9 +242,10 @@ std::string Socket::getPeername() const
std::string Socket::getPeerAddress() const
{
- if (!connectname.empty())
- return std::string (connectname);
- return getName(impl->fd, false, true);
+ if (connectname.empty()) {
+ connectname = getName(impl->fd, false, true);
+ }
+ return connectname;
}
std::string Socket::getLocalAddress() const
@@ -238,7 +260,7 @@ uint16_t Socket::getLocalPort() const
uint16_t Socket::getRemotePort() const
{
- return atoi(getService(impl->fd, true).c_str());
+ return std::atoi(getService(impl->fd, true).c_str());
}
int Socket::getError() const
@@ -252,13 +274,4 @@ int Socket::getError() const
return result;
}
-void Socket::setTcpNoDelay(bool nodelay) const
-{
- if (nodelay) {
- int flag = 1;
- int result = setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
- QPID_POSIX_CHECK(result);
- }
-}
-
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/posix/SocketAddress.cpp b/cpp/src/qpid/sys/posix/SocketAddress.cpp
new file mode 100644
index 0000000000..cb44f8e41f
--- /dev/null
+++ b/cpp/src/qpid/sys/posix/SocketAddress.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/sys/SocketAddress.h"
+
+#include "qpid/sys/posix/check.h"
+
+#include <sys/socket.h>
+#include <string.h>
+#include <netdb.h>
+
+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)
+{
+ if (&sa != this) {
+ host = sa.host;
+ port = sa.port;
+
+ if (addrInfo) {
+ ::freeaddrinfo(addrInfo);
+ addrInfo = 0;
+ }
+ }
+ return *this;
+}
+
+SocketAddress::~SocketAddress()
+{
+ if (addrInfo) {
+ ::freeaddrinfo(addrInfo);
+ }
+}
+
+std::string SocketAddress::asString() const
+{
+ return host + ":" + port;
+}
+
+const ::addrinfo& getAddrInfo(const SocketAddress& sa)
+{
+ if (!sa.addrInfo) {
+ ::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 (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.host << ": " << ::gai_strerror(n)));
+ }
+
+ return *sa.addrInfo;
+}
+
+}}
diff --git a/cpp/src/qpid/sys/posix/SystemInfo.cpp b/cpp/src/qpid/sys/posix/SystemInfo.cpp
new file mode 100755
index 0000000000..55737fcfcc
--- /dev/null
+++ b/cpp/src/qpid/sys/posix/SystemInfo.cpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/SystemInfo.h"
+
+#include "qpid/sys/posix/check.h"
+
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <unistd.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 (TcpAddress &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");
+
+void SystemInfo::getLocalIpAddresses (uint16_t port,
+ std::vector<Address> &addrList) {
+ int s = ::socket(PF_INET, SOCK_STREAM, 0);
+ for (int i=1;;i++) {
+ ::ifreq ifr;
+ ifr.ifr_ifindex = i;
+ if (::ioctl (s, SIOCGIFNAME, &ifr) < 0)
+ break;
+ /* now ifr.ifr_name is set */
+ if (::ioctl (s, SIOCGIFADDR, &ifr) < 0)
+ continue;
+ ::sockaddr *saddr = (::sockaddr *) &ifr.ifr_addr;
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo(saddr, sizeof(ifr.ifr_addr), dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0)
+ throw QPID_POSIX_ERROR(rc);
+ string addr(dispName);
+ if (addr != LOCALHOST)
+ addrList.push_back(TcpAddress(addr, port));
+ }
+ if (addrList.empty()) {
+ addrList.push_back(TcpAddress(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();
+}
+
+// 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/cpp/src/qpid/sys/posix/Thread.cpp b/cpp/src/qpid/sys/posix/Thread.cpp
index bb5641bc53..a784e63195 100644
--- a/cpp/src/qpid/sys/posix/Thread.cpp
+++ b/cpp/src/qpid/sys/posix/Thread.cpp
@@ -22,7 +22,7 @@
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
-#include "check.h"
+#include "qpid/sys/posix/check.h"
#include <pthread.h>
diff --git a/cpp/src/qpid/sys/posix/Time.cpp b/cpp/src/qpid/sys/posix/Time.cpp
index 8aa9fd9946..0734abd1df 100644
--- a/cpp/src/qpid/sys/posix/Time.cpp
+++ b/cpp/src/qpid/sys/posix/Time.cpp
@@ -19,25 +19,46 @@
*
*/
-#include "PrivatePosix.h"
+#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>
+
+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::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.time_ns = toTime(ts).nanosecs;
+ time_now.timepoint = toTime(ts).nanosecs;
return time_now;
}
+Duration::Duration(const AbsTime& time0) :
+ nanosecs(time0.timepoint)
+{}
+
+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;
@@ -58,25 +79,35 @@ std::ostream& operator<<(std::ostream& o, const Duration& d) {
return o << int64_t(d) << "ns";
}
-std::ostream& operator<<(std::ostream& o, const AbsTime& t) {
- static const char * month_abbrevs[] = {
- "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
- };
- struct tm * timeinfo;
- time_t rawtime(t.time_ns/TIME_SEC);
- timeinfo = localtime (&rawtime);
+namespace {
+inline std::ostream& outputFormattedTime(std::ostream& o, const ::time_t* time) {
+ ::tm timeinfo;
char time_string[100];
- sprintf ( time_string,
- "%d-%s-%02d %02d:%02d:%02d",
- 1900 + timeinfo->tm_year,
- month_abbrevs[timeinfo->tm_mon],
- timeinfo->tm_mday,
- timeinfo->tm_hour,
- timeinfo->tm_min,
- timeinfo->tm_sec
- );
+ ::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 sleep(int secs) {
+ ::sleep(secs);
+}
+
+void usleep(uint64_t usecs) {
+ ::usleep(usecs);
+}
+
+}}
diff --git a/cpp/src/qpid/sys/posix/check.h b/cpp/src/qpid/sys/posix/check.h
deleted file mode 100644
index f3031b7593..0000000000
--- a/cpp/src/qpid/sys/posix/check.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef _posix_check_h
-#define _posix_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/Exception.h"
-
-#include <cerrno>
-#include <assert.h>
-#include <stdio.h>
-
-#define QPID_POSIX_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRNO)))
-
-/** THROW QPID_POSIX_ERROR(errno) if RESULT is less than zero */
-#define QPID_POSIX_CHECK(RESULT) \
- if ((RESULT) < 0) throw QPID_POSIX_ERROR((errno))
-
-/** Throw a posix error if ERRNO is non-zero */
-#define QPID_POSIX_THROW_IF(ERRNO) \
- do { int e=(ERRNO); if (e) throw QPID_POSIX_ERROR(e); } while(0)
-
-/** Same as _THROW_IF in a release build, but abort a debug build */
-#ifdef NDEBUG
-#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) QPID_POSIX_THROW_IF(ERRNO)
-#else
-#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) \
- do { int e=(ERRNO); if (e) { errno=e; ::perror(0); assert(0); } } while(0)
-#endif
-
-#endif /*!_posix_check_h*/
diff --git a/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/cpp/src/qpid/sys/rdma/RdmaClient.cpp
index afff96b72f..d39f7885a5 100644
--- a/cpp/src/qpid/sys/rdma/RdmaClient.cpp
+++ b/cpp/src/qpid/sys/rdma/RdmaClient.cpp
@@ -1,4 +1,24 @@
-#include "RdmaIO.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/rdma/RdmaIO.h"
#include "qpid/sys/Time.h"
#include <netdb.h>
@@ -20,18 +40,20 @@ using std::rand;
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;
-int64_t cmsgs = 0;
-int writable = true;
int target = 1000000;
int msgsize = 200;
@@ -42,17 +64,15 @@ Duration fullTestDuration(TIME_INFINITE);
vector<char> testString;
void write(Rdma::AsynchIO& aio) {
- if ((cmsgs - rmsgs) < Rdma::DEFAULT_WR_ENTRIES/2) {
- while (writable) {
- if (smsgs >= target)
- return;
- Rdma::Buffer* b = aio.getBuffer();
- std::copy(testString.begin(), testString.end(), b->bytes);
- b->dataCount = msgsize;
- aio.queueWrite(b);
- ++smsgs;
- sbytes += b->byteCount;
- }
+ while (aio.writable()) {
+ if (smsgs >= target)
+ return;
+ Rdma::Buffer* b = aio.getBuffer();
+ std::copy(testString.begin(), testString.end(), b->bytes);
+ b->dataCount = msgsize;
+ aio.queueWrite(b);
+ ++smsgs;
+ sbytes += msgsize;
}
}
@@ -62,39 +82,46 @@ void dataError(Rdma::AsynchIO&) {
void data(Poller::shared_ptr p, Rdma::AsynchIO& aio, Rdma::Buffer* b) {
++rmsgs;
- rbytes += b->byteCount;
+ rbytes += b->dataCount;
// When all messages have been recvd stop
if (rmsgs < target) {
write(aio);
} else {
fullTestDuration = std::min(fullTestDuration, Duration(startTime, AbsTime::now()));
- if (cmsgs >= target)
+ if (aio.incompletedWrites() == 0)
p->shutdown();
}
}
-void full(Rdma::AsynchIO&) {
- writable = false;
+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.returnBuffer(b);
}
void idle(Poller::shared_ptr p, Rdma::AsynchIO& aio) {
- writable = true;
- ++cmsgs;
if (smsgs < target) {
write(aio);
} else {
sendingDuration = std::min(sendingDuration, Duration(startTime, AbsTime::now()));
- if (rmsgs >= target && cmsgs >= target)
+ if (rmsgs >= target && aio.incompletedWrites() == 0)
p->shutdown();
}
}
-void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) {
+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(), msgsize,
+ Rdma::AsynchIO* aio = new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES,
boost::bind(&data, poller, _1, _2),
boost::bind(&idle, poller, _1),
&full,
@@ -111,31 +138,25 @@ void disconnected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&)
p->shutdown();
}
-void connectionError(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&) {
+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&) {
+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]);
- ::addrinfo *res;
- ::addrinfo hints = {};
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
+ string host = args[1];
string port = (args.size() < 3) ? "20079" : args[2];
- int n = ::getaddrinfo(args[1].c_str(), port.c_str(), &hints, &res);
- if (n<0) {
- cerr << "Can't find information for: " << args[1] << "\n";
- return 1;
- } else {
- cout << "Connecting to: " << args[1] << ":" << port <<"\n";
- }
if (args.size() > 3)
msgsize = atoi(args[3].c_str());
@@ -144,19 +165,22 @@ int main(int argc, char* argv[]) {
// Make a random message of that size
testString.resize(msgsize);
for (int i = 0; i < msgsize; ++i) {
- testString[i] = 32 + rand() & 0x3f;
+ testString[i] = 32 + (rand() & 0x3f);
}
try {
boost::shared_ptr<Poller> p(new Poller());
Dispatcher d(p);
+ SocketAddress sa(host, port);
+ cout << "Connecting to: " << sa.asString() <<"\n";
Rdma::Connector c(
- *res->ai_addr,
- boost::bind(&connected, p, _1),
- boost::bind(&connectionError, p, _1),
+ sa,
+ 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));
+ boost::bind(&rejected, p, _1, _2));
c.start(p);
d.run();
diff --git a/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/cpp/src/qpid/sys/rdma/RdmaIO.cpp
index 755d6f17c4..8d06fccba1 100644
--- a/cpp/src/qpid/sys/rdma/RdmaIO.cpp
+++ b/cpp/src/qpid/sys/rdma/RdmaIO.cpp
@@ -1,12 +1,41 @@
-#include "RdmaIO.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/rdma/RdmaIO.h"
+
+#include "qpid/log/Statement.h"
+
#include <iostream>
#include <boost/bind.hpp>
+using qpid::sys::SocketAddress;
+using qpid::sys::DispatchHandle;
+using qpid::sys::Poller;
+
namespace Rdma {
AsynchIO::AsynchIO(
QueuePair::intrusive_ptr q,
- int s,
+ int size,
+ int xCredit,
+ int rCount,
ReadCallback rc,
IdleCallback ic,
FullCallback fc,
@@ -14,10 +43,15 @@ namespace Rdma {
) :
qp(q),
dataHandle(*qp, boost::bind(&AsynchIO::dataEvent, this, _1), 0, 0),
- bufferSize(s),
- recvBufferCount(DEFAULT_WR_ENTRIES),
- xmitBufferCount(DEFAULT_WR_ENTRIES),
+ bufferSize(size),
+ recvCredit(0),
+ xmitCredit(xCredit),
+ recvBufferCount(rCount),
+ xmitBufferCount(xCredit),
outstandingWrites(0),
+ closed(false),
+ deleting(false),
+ state(IDLE),
readCallback(rc),
idleCallback(ic),
fullCallback(fc),
@@ -29,80 +63,281 @@ namespace Rdma {
// Prepost some recv buffers before we go any further
for (int i = 0; i<recvBufferCount; ++i) {
+ // Allocate recv buffer
Buffer* b = qp->createBuffer(bufferSize);
buffers.push_front(b);
b->dataCount = b->byteCount;
qp->postRecv(b);
}
+
+ for (int i = 0; i<xmitBufferCount; ++i) {
+ // Allocate xmit buffer
+ Buffer* b = qp->createBuffer(bufferSize);
+ buffers.push_front(b);
+ bufferQueue.push_front(b);
+ b->dataCount = 0;
+ b->dataStart = 0;
+ }
}
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 (before doing the deletes)
+ dataHandle.stopWatch();
+
// The buffers ptr_deque automatically deletes all the buffers we've allocated
+ // 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);
}
- // TODO: Currently we don't prevent write buffer overrun we just advise
- // when to stop writing.
- void AsynchIO::queueWrite(Buffer* buff) {
- qp->postSend(buff);
- ++outstandingWrites;
- if (outstandingWrites >= xmitBufferCount) {
- fullCallback(*this);
+ // Mark for deletion/Delete this object when we have no outstanding writes
+ void AsynchIO::deferDelete() {
+ State oldState;
+ State newState;
+ bool doReturn;
+ //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ // It is safe to assign to deleting here as we either delete ourselves
+ // before leaving this function or deleting is set on exit
+ do {
+ newState = oldState = state.get();
+ doReturn = false;
+ if (outstandingWrites > 0 || oldState != IDLE) {
+ deleting = true;
+ doReturn = true;
+ } else{
+ newState = DELETED; // Stop any read callback before the dataHandle.stopWatch() in the destructor
+ }
+ } while (!state.boolCompareAndSwap(oldState, newState));
+ if (doReturn) {
+ return;
}
+ delete this;
}
- void AsynchIO::notifyPendingWrite() {
- // Just perform the idle callback (if possible)
- if (outstandingWrites < xmitBufferCount) {
- idleCallback(*this);
+ 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
+ if (recvCredit > 0) {
+ int creditSent = recvCredit & ~FlagsMask;
+ qp->postSend(creditSent, buff);
+ recvCredit -= creditSent;
+ } else {
+ qp->postSend(buff);
+ }
+ ++outstandingWrites;
+ --xmitCredit;
+ } else {
+ if (fullCallback) {
+ fullCallback(*this, buff);
+ } else {
+ QPID_LOG(error, "RDMA: qp=" << qp << ": Write queue full, but no callback, throwing buffer away");
+ returnBuffer(buff);
+ }
}
}
+ // Mark now closed (so we don't accept any more writes or make any idle callbacks)
void AsynchIO::queueWriteClose() {
+ // Don't think we actually need to lock here as transition is 1 way only to closed
+ closed = true;
}
- Buffer* AsynchIO::getBuffer() {
- qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock);
- if (bufferQueue.empty()) {
- Buffer* b = qp->createBuffer(bufferSize);
- buffers.push_front(b);
- b->dataCount = 0;
- return b;
- } else {
- Buffer* b = bufferQueue.front();
- bufferQueue.pop_front();
- b->dataCount = 0;
- b->dataStart = 0;
- return b;
+ void AsynchIO::notifyPendingWrite() {
+ // As notifyPendingWrite can be called on an arbitrary thread it must check whether we are processing or not.
+ // If we are then we just return as we know that we will eventually do the idle callback anyway.
+ //
+ // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ // We can get here in any state (as the caller could be in any thread)
+ State oldState;
+ State newState;
+ bool doReturn;
+ do {
+ newState = oldState = state.get();
+ doReturn = false;
+ switch (oldState) {
+ case NOTIFY_WRITE:
+ case PENDING_NOTIFY:
+ // We only need to note a pending notify if we're already doing a notify as data processing
+ // is always followed by write notification processing
+ newState = PENDING_NOTIFY;
+ doReturn = true;
+ break;
+ case PENDING_DATA:
+ doReturn = true;
+ break;
+ case DATA:
+ // Only need to return here as data processing will do the idleCallback itself anyway
+ doReturn = true;
+ break;
+ case IDLE:
+ newState = NOTIFY_WRITE;
+ break;
+ case DELETED:
+ assert(oldState!=DELETED);
+ doReturn = true;
+ };
+ } while (!state.boolCompareAndSwap(oldState, newState));
+ if (doReturn) {
+ return;
+ }
+
+ doWriteCallback();
+
+ // Keep track of what we need to do so that we can release the lock
+ enum {COMPLETION, NOTIFY, RETURN, EXIT} action;
+ // If there was pending data whilst we were doing this, process it now
+ //
+ // Using NOTIFY_WRITE for both NOTIFY & COMPLETION is a bit strange, but we're making sure we get the
+ // correct result if we reenter notifyPendingWrite(), in which case we want to
+ // end up in PENDING_NOTIFY (entering dataEvent doesn't matter as it only checks
+ // not IDLE)
+ do {
+ //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ do {
+ newState = oldState = state.get();
+ action = RETURN; // Anything but COMPLETION
+ switch (oldState) {
+ case NOTIFY_WRITE:
+ newState = IDLE;
+ action = (action == COMPLETION) ? EXIT : RETURN;
+ break;
+ case PENDING_DATA:
+ newState = NOTIFY_WRITE;
+ action = COMPLETION;
+ break;
+ case PENDING_NOTIFY:
+ newState = NOTIFY_WRITE;
+ action = NOTIFY;
+ break;
+ default:
+ assert(oldState!=IDLE && oldState!=DATA && oldState!=DELETED);
+ action = RETURN;
+ }
+ } while (!state.boolCompareAndSwap(oldState, newState));
+
+ // Note we only get here if we were in the PENDING_DATA or PENDING_NOTIFY state
+ // so that we do need to process completions or notifications now
+ switch (action) {
+ case COMPLETION:
+ processCompletions();
+ // Fall through
+ case NOTIFY:
+ doWriteCallback();
+ break;
+ case RETURN:
+ return;
+ case EXIT:
+ // If we just processed completions we might need to delete ourselves
+ if (deleting && outstandingWrites == 0) {
+ delete this;
+ }
+ return;
+ }
+ } while (true);
+ }
+
+ void AsynchIO::dataEvent(qpid::sys::DispatchHandle&) {
+ // Keep track of writable notifications
+ // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ State oldState;
+ State newState;
+ bool doReturn;
+ do {
+ newState = oldState = state.get();
+ doReturn = false;
+ // We're already processing a notification
+ switch (oldState) {
+ case IDLE:
+ newState = DATA;
+ break;
+ default:
+ // Can't get here in DATA state as that would violate the serialisation rules
+ assert( oldState!=DATA );
+ newState = PENDING_DATA;
+ doReturn = true;
+ }
+ } while (!state.boolCompareAndSwap(oldState, newState));
+ if (doReturn) {
+ return;
}
+ processCompletions();
+
+ //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ do {
+ newState = oldState = state.get();
+ assert( oldState==DATA );
+ newState = NOTIFY_WRITE;
+ } while (!state.boolCompareAndSwap(oldState, newState));
+
+ do {
+ doWriteCallback();
+
+ // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock);
+ bool doBreak;
+ do {
+ newState = oldState = state.get();
+ doBreak = false;
+ if ( oldState==NOTIFY_WRITE ) {
+ newState = IDLE;
+ doBreak = true;
+ } else {
+ // Can't get DATA/PENDING_DATA here as dataEvent cannot be reentered
+ assert( oldState==PENDING_NOTIFY );
+ newState = NOTIFY_WRITE;
+ }
+ } while (!state.boolCompareAndSwap(oldState, newState));
+ if (doBreak) {
+ break;
+ }
+ } while (true);
+
+ // We might need to delete ourselves
+ if (deleting && outstandingWrites == 0) {
+ delete this;
+ }
}
- void AsynchIO::dataEvent(DispatchHandle&) {
+ 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);
- // Re-enable notification for queue
- qp->notifySend();
- qp->notifyRecv();
-
// Repeat until no more events
do {
QueuePairEvent e(qp->getNextEvent());
if (!e)
- return;
+ break;
::ibv_wc_status status = e.getEventStatus();
if (status != IBV_WC_SUCCESS) {
errorCallback(*this);
+ // TODO: Probably need to flush queues at this point
return;
}
@@ -111,46 +346,143 @@ namespace Rdma {
Buffer* b = e.getBuffer();
QueueDirection dir = e.getDirection();
if (dir == RECV) {
- readCallback(*this, b);
+ ++recvEvents;
+
+ // Get our xmitCredit if it was sent
+ bool dataPresent = true;
+ if (e.immPresent() ) {
+ xmitCredit += (e.getImm() & ~FlagsMask);
+ dataPresent = ((e.getImm() & IgnoreData) == 0);
+ }
+
+ // if there was no data sent then the message was only to update our credit
+ if ( dataPresent ) {
+ readCallback(*this, b);
+ }
+
// At this point the buffer has been consumed so put it back on the recv queue
+ b->dataStart = 0;
+ b->dataCount = 0;
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()) {
+ Buffer* ob = getBuffer();
+ // Have to send something as adapters hate it when you try to transfer 0 bytes
+ *reinterpret_cast< uint32_t* >(ob->bytes) = htonl(recvCredit);
+ ob->dataCount = sizeof(uint32_t);
+
+ int creditSent = recvCredit & ~FlagsMask;
+ qp->postSend(creditSent | IgnoreData, ob);
+ recvCredit -= creditSent;
+ ++outstandingWrites;
+ --xmitCredit;
+ } else {
+ QPID_LOG(warning, "RDMA: qp=" << qp << ": Unable to send unsolicited credit");
+ }
+ }
} else {
+ ++sendEvents;
{
qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock);
bufferQueue.push_front(b);
}
--outstandingWrites;
- // TODO: maybe don't call idle unless we're low on write buffers
- idleCallback(*this);
}
} 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
+ 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;
+ }
+ }
+ }
+
+ Buffer* AsynchIO::getBuffer() {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock);
+ assert(!bufferQueue.empty());
+ Buffer* b = bufferQueue.front();
+ bufferQueue.pop_front();
+ b->dataCount = 0;
+ b->dataStart = 0;
+ return b;
+ }
+
+ void AsynchIO::returnBuffer(Buffer* b) {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock);
+ bufferQueue.push_front(b);
+ b->dataCount = 0;
+ b->dataStart = 0;
+ }
+
+ ConnectionManager::ConnectionManager(
+ ErrorCallback errc,
+ DisconnectedCallback dc
+ ) :
+ ci(Connection::make()),
+ handle(*ci, boost::bind(&ConnectionManager::event, this, _1), 0, 0),
+ errorCallback(errc),
+ disconnectedCallback(dc)
+ {
+ ci->nonblocking();
+ }
+
+ ConnectionManager::~ConnectionManager()
+ {
+ handle.stopWatch();
+ }
+
+ void ConnectionManager::start(Poller::shared_ptr poller) {
+ startConnection(ci);
+ handle.startWatch(poller);
+ }
+
+ void ConnectionManager::event(DispatchHandle&) {
+ connectionEvent(ci);
}
Listener::Listener(
- const sockaddr& src,
- ConnectedCallback cc,
+ const SocketAddress& src,
+ const ConnectionParams& cp,
+ EstablishedCallback ec,
ErrorCallback errc,
DisconnectedCallback dc,
ConnectionRequestCallback crc
) :
+ ConnectionManager(errc, dc),
src_addr(src),
- ci(Connection::make()),
- handle(*ci, boost::bind(&Listener::connectionEvent, this, _1), 0, 0),
- connectedCallback(cc),
- errorCallback(errc),
- disconnectedCallback(dc),
- connectionRequestCallback(crc)
+ checkConnectionParams(cp),
+ connectionRequestCallback(crc),
+ establishedCallback(ec)
{
- ci->nonblocking();
}
- void Listener::start(Poller::shared_ptr poller) {
+ void Listener::startConnection(Connection::intrusive_ptr ci) {
ci->bind(src_addr);
ci->listen();
- handle.startWatch(poller);
}
- void Listener::connectionEvent(DispatchHandle&) {
+ void Listener::connectionEvent(Connection::intrusive_ptr ci) {
ConnectionEvent e(ci->getNextEvent());
// If (for whatever reason) there was no event do nothing
@@ -161,65 +493,75 @@ namespace Rdma {
// 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();
switch (eventType) {
case RDMA_CM_EVENT_CONNECT_REQUEST: {
- bool accept = true;
- // Extract connection parameters and private data from event
- ::rdma_conn_param conn_param = e.getConnectionParam();
+ // Make sure peer has sent params we can use
+ if (!conn_param.private_data || conn_param.private_data_len < sizeof(ConnectionParams)) {
+ id->reject();
+ break;
+ }
+ ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data);
+
+ // Reject if requested msg size is bigger than we allow
+ if (cp.maxRecvBufferSize > checkConnectionParams.maxRecvBufferSize) {
+ id->reject(&checkConnectionParams);
+ break;
+ }
+ bool accept = true;
if (connectionRequestCallback)
- //TODO: pass private data to callback (and accept new private data for accept somehow)
- accept = connectionRequestCallback(id);
+ accept = connectionRequestCallback(id, cp);
+
if (accept) {
// Accept connection
- id->accept(conn_param);
+ cp.initialXmitCredit = checkConnectionParams.initialXmitCredit;
+ id->accept(conn_param, &cp);
} else {
- //Reject connection
+ // Reject connection
id->reject();
}
-
break;
}
case RDMA_CM_EVENT_ESTABLISHED:
- connectedCallback(id);
+ establishedCallback(id);
break;
case RDMA_CM_EVENT_DISCONNECTED:
disconnectedCallback(id);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
- errorCallback(id);
+ errorCallback(id, CONNECT_ERROR);
break;
default:
- std::cerr << "Warning: unexpected response to listen - " << eventType << "\n";
+ // Unexpected response
+ errorCallback(id, UNKNOWN);
+ //std::cerr << "Warning: unexpected response to listen - " << eventType << "\n";
}
}
Connector::Connector(
- const sockaddr& dst,
+ const SocketAddress& dst,
+ const ConnectionParams& cp,
ConnectedCallback cc,
ErrorCallback errc,
DisconnectedCallback dc,
RejectedCallback rc
) :
+ ConnectionManager(errc, dc),
dst_addr(dst),
- ci(Connection::make()),
- handle(*ci, boost::bind(&Connector::connectionEvent, this, _1), 0, 0),
- connectedCallback(cc),
- errorCallback(errc),
- disconnectedCallback(dc),
- rejectedCallback(rc)
+ connectionParams(cp),
+ rejectedCallback(rc),
+ connectedCallback(cc)
{
- ci->nonblocking();
}
- void Connector::start(Poller::shared_ptr poller) {
+ void Connector::startConnection(Connection::intrusive_ptr ci) {
ci->resolve_addr(dst_addr);
- handle.startWatch(poller);
}
- void Connector::connectionEvent(DispatchHandle&) {
+ void Connector::connectionEvent(Connection::intrusive_ptr ci) {
ConnectionEvent e(ci->getNextEvent());
// If (for whatever reason) there was no event do nothing
@@ -227,6 +569,8 @@ namespace Rdma {
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
@@ -234,38 +578,46 @@ namespace Rdma {
break;
case RDMA_CM_EVENT_ADDR_ERROR:
// RESOLVE_ADDR
- errorCallback(ci);
+ errorCallback(ci, ADDR_ERROR);
break;
case RDMA_CM_EVENT_ROUTE_RESOLVED:
// RESOLVE_ROUTE:
- ci->connect();
+ ci->connect(&connectionParams);
break;
case RDMA_CM_EVENT_ROUTE_ERROR:
// RESOLVE_ROUTE:
- errorCallback(ci);
+ errorCallback(ci, ROUTE_ERROR);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
// CONNECTING
- errorCallback(ci);
+ errorCallback(ci, CONNECT_ERROR);
break;
case RDMA_CM_EVENT_UNREACHABLE:
// CONNECTING
- errorCallback(ci);
+ errorCallback(ci, UNREACHABLE);
break;
- case RDMA_CM_EVENT_REJECTED:
+ case RDMA_CM_EVENT_REJECTED: {
// CONNECTING
- rejectedCallback(ci);
+ // Extract private data from event
+ assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams));
+ ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data);
+ rejectedCallback(ci, cp);
break;
- case RDMA_CM_EVENT_ESTABLISHED:
+ }
+ case RDMA_CM_EVENT_ESTABLISHED: {
// CONNECTING
- connectedCallback(ci);
+ // Extract private data from event
+ assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams));
+ ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data);
+ connectedCallback(ci, cp);
break;
+ }
case RDMA_CM_EVENT_DISCONNECTED:
// ESTABLISHED
disconnectedCallback(ci);
break;
default:
- std::cerr << "Warning: unexpected event in connect: " << eventType << "\n";
+ QPID_LOG(warning, "RDMA: Unexpected event in connect: " << eventType);
}
}
}
diff --git a/cpp/src/qpid/sys/rdma/RdmaIO.h b/cpp/src/qpid/sys/rdma/RdmaIO.h
index 5c8d49607c..12a1b98d24 100644
--- a/cpp/src/qpid/sys/rdma/RdmaIO.h
+++ b/cpp/src/qpid/sys/rdma/RdmaIO.h
@@ -1,10 +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 Rdma_Acceptor_h
#define Rdma_Acceptor_h
-#include "rdma_wrap.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>
@@ -12,32 +35,30 @@
#include <boost/ptr_container/ptr_deque.hpp>
#include <deque>
-using qpid::sys::DispatchHandle;
-using qpid::sys::Poller;
-
namespace Rdma {
class Connection;
- typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> ConnectedCallback;
- typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> ErrorCallback;
- typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> DisconnectedCallback;
- typedef boost::function1<bool, Rdma::Connection::intrusive_ptr&> ConnectionRequestCallback;
- typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> RejectedCallback;
-
class AsynchIO
{
typedef boost::function1<void, AsynchIO&> ErrorCallback;
typedef boost::function2<void, AsynchIO&, Buffer*> ReadCallback;
typedef boost::function1<void, AsynchIO&> IdleCallback;
- typedef boost::function1<void, AsynchIO&> FullCallback;
+ typedef boost::function2<void, AsynchIO&, Buffer*> FullCallback;
QueuePair::intrusive_ptr qp;
- DispatchHandle dataHandle;
+ qpid::sys::DispatchHandleRef dataHandle;
int bufferSize;
+ int recvCredit;
+ int xmitCredit;
int recvBufferCount;
int xmitBufferCount;
int outstandingWrites;
+ bool closed; // TODO: Perhaps (probably) this state can be merged with the following...
+ bool deleting; // TODO: Perhaps (probably) this state can be merged with the following...
+ enum State { IDLE, DATA, PENDING_DATA, NOTIFY_WRITE, PENDING_NOTIFY, DELETED };
+ qpid::sys::AtomicValue<State> state;
+ //qpid::sys::Mutex stateLock;
std::deque<Buffer*> bufferQueue;
qpid::sys::Mutex bufferQueueLock;
boost::ptr_deque<Buffer> buffers;
@@ -48,72 +69,154 @@ namespace Rdma {
ErrorCallback errorCallback;
public:
+ // 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 s,
+ int size,
+ int xCredit,
+ int rCount,
ReadCallback rc,
IdleCallback ic,
FullCallback fc,
ErrorCallback ec
);
- ~AsynchIO();
- void start(Poller::shared_ptr poller);
+ void start(qpid::sys::Poller::shared_ptr poller);
+ bool writable() const;
+ bool bufferAvailable() const;
void queueWrite(Buffer* buff);
void notifyPendingWrite();
void queueWriteClose();
+ void deferDelete();
+ int incompletedWrites() const;
Buffer* getBuffer();
+ void returnBuffer(Buffer*);
private:
- void dataEvent(DispatchHandle& handle);
+ // Don't let anyone else delete us to make sure there can't be outstanding callbacks
+ ~AsynchIO();
+
+ // 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 = 0x10000000; // 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(qpid::sys::DispatchHandle& handle);
+ void processCompletions();
+ void doWriteCallback();
};
- class Listener
- {
- sockaddr src_addr;
+ inline bool AsynchIO::writable() const {
+ return (!closed && outstandingWrites < xmitBufferCount && xmitCredit > 0);
+ }
+
+ inline int AsynchIO::incompletedWrites() const {
+ return outstandingWrites;
+ }
+
+ inline bool AsynchIO::bufferAvailable() const {
+ return !bufferQueue.empty();
+ }
+ // 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 {
+ int maxRecvBufferSize;
+ int initialXmitCredit ;
+
+ ConnectionParams(int s, int c) :
+ maxRecvBufferSize(s),
+ initialXmitCredit(c)
+ {}
+ };
+
+ 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 {
Connection::intrusive_ptr ci;
- DispatchHandle handle;
- ConnectedCallback connectedCallback;
+ qpid::sys::DispatchHandle handle;
+
+ protected:
ErrorCallback errorCallback;
DisconnectedCallback disconnectedCallback;
+
+ public:
+ ConnectionManager(
+ ErrorCallback errc,
+ DisconnectedCallback dc
+ );
+
+ virtual ~ConnectionManager();
+
+ void start(qpid::sys::Poller::shared_ptr poller);
+
+ private:
+ void event(qpid::sys::DispatchHandle& handle);
+
+ virtual void startConnection(Connection::intrusive_ptr ci) = 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
+ {
+ qpid::sys::SocketAddress src_addr;
+ ConnectionParams checkConnectionParams;
ConnectionRequestCallback connectionRequestCallback;
+ EstablishedCallback establishedCallback;
public:
Listener(
- const sockaddr& src,
- ConnectedCallback cc,
+ const qpid::sys::SocketAddress& src,
+ const ConnectionParams& cp,
+ EstablishedCallback ec,
ErrorCallback errc,
DisconnectedCallback dc,
ConnectionRequestCallback crc = 0
);
- void start(Poller::shared_ptr poller);
private:
- void connectionEvent(DispatchHandle& handle);
+ void startConnection(Connection::intrusive_ptr ci);
+ void connectionEvent(Connection::intrusive_ptr ci);
};
- class Connector
+ 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
{
- sockaddr dst_addr;
- Connection::intrusive_ptr ci;
- DispatchHandle handle;
- ConnectedCallback connectedCallback;
- ErrorCallback errorCallback;
- DisconnectedCallback disconnectedCallback;
+ qpid::sys::SocketAddress dst_addr;
+ ConnectionParams connectionParams;
RejectedCallback rejectedCallback;
+ ConnectedCallback connectedCallback;
public:
Connector(
- const sockaddr& dst,
+ const qpid::sys::SocketAddress& dst,
+ const ConnectionParams& cp,
ConnectedCallback cc,
ErrorCallback errc,
DisconnectedCallback dc,
RejectedCallback rc = 0
);
- void start(Poller::shared_ptr poller);
private:
- void connectionEvent(DispatchHandle& handle);
+ void startConnection(Connection::intrusive_ptr ci);
+ void connectionEvent(Connection::intrusive_ptr ci);
};
}
diff --git a/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/cpp/src/qpid/sys/rdma/RdmaServer.cpp
index f7f739d6c2..4c11ba23eb 100644
--- a/cpp/src/qpid/sys/rdma/RdmaServer.cpp
+++ b/cpp/src/qpid/sys/rdma/RdmaServer.cpp
@@ -1,4 +1,24 @@
-#include "RdmaIO.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/rdma/RdmaIO.h"
#include <arpa/inet.h>
@@ -15,19 +35,21 @@ using std::string;
using std::cout;
using std::cerr;
+using qpid::sys::SocketAddress;
using qpid::sys::Poller;
using qpid::sys::Dispatcher;
// All the accepted connections
+namespace qpid {
+namespace tests {
+
struct ConRec {
Rdma::Connection::intrusive_ptr connection;
Rdma::AsynchIO* data;
- bool writable;
queue<Rdma::Buffer*> queuedWrites;
ConRec(Rdma::Connection::intrusive_ptr c) :
- connection(c),
- writable(true)
+ connection(c)
{}
};
@@ -35,50 +57,53 @@ 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* buf = cr->queuedWrites.front();
+ cr->queuedWrites.pop();
+ a.queueWrite(buf);
+ }
+}
+
void data(ConRec* cr, Rdma::AsynchIO& a, Rdma::Buffer* b) {
// Echo data back
Rdma::Buffer* buf = a.getBuffer();
std::copy(b->bytes+b->dataStart, b->bytes+b->dataStart+b->dataCount, buf->bytes);
buf->dataCount = b->dataCount;
- if (cr->queuedWrites.empty() && cr->writable) {
+ if (cr->queuedWrites.empty()) {
+ // If can't write then full will be called and push buffer on back of queue
a.queueWrite(buf);
} else {
cr->queuedWrites.push(buf);
+ // Try to empty queue
+ idle(cr, a);
}
}
-void full(ConRec* cr, Rdma::AsynchIO&) {
- cr->writable = false;
-}
-
-void idle(ConRec* cr, Rdma::AsynchIO& a) {
- cr->writable = true;
- while (!cr->queuedWrites.empty() && cr->writable) {
- Rdma::Buffer* buf = cr->queuedWrites.front();
- cr->queuedWrites.pop();
- a.queueWrite(buf);
- }
+void full(ConRec* cr, Rdma::AsynchIO&, Rdma::Buffer* buf) {
+ cr->queuedWrites.push(buf);
}
void disconnected(Rdma::Connection::intrusive_ptr& ci) {
ConRec* cr = ci->getContext<ConRec>();
cr->connection->disconnect();
- delete cr->data;
+ cr->data->queueWriteClose();
delete cr;
cout << "Disconnected: " << cr << "\n";
}
-void connectionError(Rdma::Connection::intrusive_ptr& ci) {
+void connectionError(Rdma::Connection::intrusive_ptr& ci, Rdma::ErrorType) {
ConRec* cr = ci->getContext<ConRec>();
cr->connection->disconnect();
if (cr) {
- delete cr->data;
+ cr->data->queueWriteClose();
delete cr;
}
cout << "Connection error: " << cr << "\n";
}
-bool connectionRequest(Rdma::Connection::intrusive_ptr& ci) {
+bool connectionRequest(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) {
cout << "Incoming connection: ";
// For fun reject alternate connection attempts
@@ -89,10 +114,11 @@ bool connectionRequest(Rdma::Connection::intrusive_ptr& ci) {
if (x) {
ConRec* cr = new ConRec(ci);
Rdma::AsynchIO* aio =
- new Rdma::AsynchIO(ci->getQueuePair(), 8000,
+ new Rdma::AsynchIO(ci->getQueuePair(),
+ cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES,
boost::bind(data, cr, _1, _2),
boost::bind(idle, cr, _1),
- boost::bind(full, cr, _1),
+ boost::bind(full, cr, _1, _2),
dataError);
ci->addContext(cr);
cr->data = aio;
@@ -112,23 +138,23 @@ void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) {
cr->data->start(poller);
}
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char* argv[]) {
vector<string> args(&argv[0], &argv[argc]);
- ::sockaddr_in sin;
-
- int port = (args.size() < 2) ? 20079 : atoi(args[1].c_str());
+ std::string port = (args.size() < 2) ? "20079" : args[1];
cout << "Listening on port: " << port << "\n";
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- sin.sin_addr.s_addr = INADDR_ANY;
-
try {
boost::shared_ptr<Poller> p(new Poller());
Dispatcher d(p);
- Rdma::Listener a((const sockaddr&)(sin),
+ SocketAddress sa("", port);
+ Rdma::Listener a(sa,
+ Rdma::ConnectionParams(16384, Rdma::DEFAULT_WR_ENTRIES),
boost::bind(connected, p, _1),
connectionError,
disconnected,
diff --git a/cpp/src/qpid/sys/rdma/rdma_exception.h b/cpp/src/qpid/sys/rdma/rdma_exception.h
index 2773f25917..7867aef2e4 100644
--- a/cpp/src/qpid/sys/rdma/rdma_exception.h
+++ b/cpp/src/qpid/sys/rdma/rdma_exception.h
@@ -1,3 +1,23 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
#ifndef RDMA_EXCEPTION_H
#define RDMA_EXCEPTION_H
diff --git a/cpp/src/qpid/sys/rdma/rdma_factories.cpp b/cpp/src/qpid/sys/rdma/rdma_factories.cpp
index 53dc4f8935..a5e9ebd23c 100644
--- a/cpp/src/qpid/sys/rdma/rdma_factories.cpp
+++ b/cpp/src/qpid/sys/rdma/rdma_factories.cpp
@@ -1,4 +1,24 @@
-#include "rdma_factories.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/rdma/rdma_factories.h"
namespace Rdma {
void acker(::rdma_cm_event* e) throw () {
@@ -36,4 +56,9 @@ namespace Rdma {
(void) ::ibv_destroy_cq(cq);
}
+ void destroyQp(::ibv_qp* qp) throw () {
+ if (qp)
+ (void) ::ibv_destroy_qp(qp);
+ }
+
}
diff --git a/cpp/src/qpid/sys/rdma/rdma_factories.h b/cpp/src/qpid/sys/rdma/rdma_factories.h
index 26a93bb494..783181cf1b 100644
--- a/cpp/src/qpid/sys/rdma/rdma_factories.h
+++ b/cpp/src/qpid/sys/rdma/rdma_factories.h
@@ -1,7 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
#ifndef RDMA_FACTORIES_H
#define RDMA_FACTORIES_H
-#include "rdma_exception.h"
+#include "qpid/sys/rdma/rdma_exception.h"
#include <rdma/rdma_cma.h>
@@ -15,10 +35,11 @@ namespace Rdma {
void deallocPd(::ibv_pd* p) throw ();
void destroyCChannel(::ibv_comp_channel* c) throw ();
void destroyCq(::ibv_cq* cq) throw ();
+ void destroyQp(::ibv_qp* qp) throw ();
inline boost::shared_ptr< ::rdma_event_channel > mkEChannel() {
- return
- boost::shared_ptr< ::rdma_event_channel >(::rdma_create_event_channel(), destroyEChannel);
+ ::rdma_event_channel* c = CHECK_NULL(::rdma_create_event_channel());
+ return boost::shared_ptr< ::rdma_event_channel >(c, destroyEChannel);
}
inline boost::shared_ptr< ::rdma_cm_id >
diff --git a/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
new file mode 100644
index 0000000000..53e31ca766
--- /dev/null
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
@@ -0,0 +1,185 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/rdma/rdma_wrap.h"
+
+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;
+ }
+
+ ::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;
+ }
+ }
+
+ 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_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 = 4;
+ qp_attr.cap.max_recv_wr = DEFAULT_WR_ENTRIES;
+ qp_attr.cap.max_recv_sge = 4;
+
+ 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 = boost::shared_ptr< ::ibv_qp >(i->qp, destroyQp);
+
+ // Set the qp context to this so we can find ourselves again
+ qp->qp_context = this;
+ }
+
+ QueuePair::~QueuePair() {
+ if (outstandingSendEvents > 0)
+ ::ibv_ack_cq_events(scq.get(), outstandingSendEvents);
+ if (outstandingRecvEvents > 0)
+ ::ibv_ack_cq_events(rcq.get(), outstandingRecvEvents);
+
+ // Reset back pointer in case someone else has the qp
+ qp->qp_context = 0;
+ }
+
+ void QueuePair::postRecv(Buffer* buf) {
+ ::ibv_recv_wr rwr = {};
+ ::ibv_sge sge;
+
+ sge.addr = (uintptr_t) buf->bytes+buf->dataStart;
+ sge.length = buf->dataCount;
+ sge.lkey = buf->mr->lkey;
+
+ rwr.wr_id = reinterpret_cast<uint64_t>(buf);
+ rwr.sg_list = &sge;
+ rwr.num_sge = 1;
+
+ ::ibv_recv_wr* badrwr = 0;
+ CHECK_IBV(::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 = {};
+ ::ibv_sge sge;
+
+ sge.addr = (uintptr_t) buf->bytes+buf->dataStart;
+ sge.length = buf->dataCount;
+ sge.lkey = buf->mr->lkey;
+
+ swr.wr_id = reinterpret_cast<uint64_t>(buf);
+ swr.opcode = IBV_WR_SEND;
+ swr.send_flags = IBV_SEND_SIGNALED;
+ swr.sg_list = &sge;
+ swr.num_sge = 1;
+
+ ::ibv_send_wr* badswr = 0;
+ CHECK_IBV(::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 = {};
+ ::ibv_sge sge;
+
+ sge.addr = (uintptr_t) buf->bytes+buf->dataStart;
+ sge.length = buf->dataCount;
+ sge.lkey = buf->mr->lkey;
+ swr.send_flags = IBV_SEND_SIGNALED;
+
+ swr.wr_id = reinterpret_cast<uint64_t>(buf);
+ swr.imm_data = htonl(imm);
+ swr.opcode = IBV_WR_SEND_WITH_IMM;
+ swr.sg_list = &sge;
+ swr.num_sge = 1;
+
+ ::ibv_send_wr* badswr = 0;
+ CHECK_IBV(::ibv_post_send(qp.get(), &swr, &badswr));
+ if (badswr)
+ throw std::logic_error("ibv_post_send(): Bad swr");
+ }
+}
+
+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/cpp/src/qpid/sys/rdma/rdma_wrap.h b/cpp/src/qpid/sys/rdma/rdma_wrap.h
index 421c8fc41b..e11497dc02 100644
--- a/cpp/src/qpid/sys/rdma/rdma_wrap.h
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.h
@@ -1,7 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
#ifndef RDMA_WRAP_H
#define RDMA_WRAP_H
-#include "rdma_factories.h"
+#include "qpid/sys/rdma/rdma_factories.h"
#include <rdma/rdma_cma.h>
@@ -11,6 +31,8 @@
#include <fcntl.h>
+#include <netdb.h>
+
#include <vector>
#include <algorithm>
#include <iostream>
@@ -23,15 +45,9 @@ namespace Rdma {
const int DEFAULT_BACKLOG = 100;
const int DEFAULT_CQ_ENTRIES = 256;
const int DEFAULT_WR_ENTRIES = 64;
- 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
- };
+ extern const ::rdma_conn_param DEFAULT_CONNECT_PARAM;
+
+ int deviceCount();
struct Buffer {
friend class QueuePair;
@@ -95,6 +111,14 @@ namespace Rdma {
return dir != NONE;
}
+ bool immPresent() const {
+ return wc.wc_flags & IBV_WC_WITH_IMM;
+ }
+
+ uint32_t getImm() const {
+ return ntohl(wc.imm_data);
+ }
+
QueueDirection getDirection() const {
return dir;
}
@@ -117,15 +141,12 @@ namespace Rdma {
// 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.
- //
- // Currently QueuePairs are contained inside Connections and have no
- // separate lifetime
class QueuePair : public qpid::sys::IOHandle, public qpid::RefCounted {
boost::shared_ptr< ::ibv_pd > pd;
boost::shared_ptr< ::ibv_comp_channel > cchannel;
boost::shared_ptr< ::ibv_cq > scq;
boost::shared_ptr< ::ibv_cq > rcq;
- boost::shared_ptr< ::rdma_cm_id > id;
+ boost::shared_ptr< ::ibv_qp > qp;
int outstandingSendEvents;
int outstandingRecvEvents;
@@ -187,6 +208,7 @@ namespace Rdma {
void postRecv(Buffer* buf);
void postSend(Buffer* buf);
+ void postSend(uint32_t imm, Buffer* buf);
void notifyRecv();
void notifySend();
};
@@ -213,14 +235,7 @@ namespace Rdma {
return event->event;
}
- ::rdma_conn_param getConnectionParam() const {
- if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
- return event->param.conn;
- } else {
- ::rdma_conn_param p = {};
- return p;
- }
- }
+ ::rdma_conn_param getConnectionParam() const;
boost::intrusive_ptr<Connection> getConnection () const {
return id;
@@ -271,6 +286,11 @@ namespace Rdma {
impl->fd = channel->fd;
}
+ ~Connection() {
+ // Reset the id context in case someone else has it
+ id->context = 0;
+ }
+
// Default destructor fine
void ensureQueuePair() {
@@ -330,9 +350,9 @@ namespace Rdma {
return ConnectionEvent(e);
}
- void bind(sockaddr& src_addr) const {
+ void bind(qpid::sys::SocketAddress& src_addr) const {
assert(id.get());
- CHECK(::rdma_bind_addr(id.get(), &src_addr));
+ CHECK(::rdma_bind_addr(id.get(), getAddrInfo(src_addr).ai_addr));
}
void listen(int backlog = DEFAULT_BACKLOG) const {
@@ -341,12 +361,11 @@ namespace Rdma {
}
void resolve_addr(
- sockaddr& dst_addr,
- sockaddr* src_addr = 0,
+ qpid::sys::SocketAddress& dst_addr,
int timeout_ms = DEFAULT_TIMEOUT) const
{
assert(id.get());
- CHECK(::rdma_resolve_addr(id.get(), src_addr, &dst_addr, timeout_ms));
+ CHECK(::rdma_resolve_addr(id.get(), 0, getAddrInfo(dst_addr).ai_addr, timeout_ms));
}
void resolve_route(int timeout_ms = DEFAULT_TIMEOUT) const {
@@ -425,52 +444,38 @@ namespace Rdma {
return qp;
}
+
+ std::string 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 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;
+ }
};
- inline 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())),
- id(i),
- 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_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 = 4;
- qp_attr.cap.max_recv_wr = DEFAULT_WR_ENTRIES;
- qp_attr.cap.max_recv_sge = 4;
-
- qp_attr.send_cq = scq.get();
- qp_attr.recv_cq = rcq.get();
- qp_attr.qp_type = IBV_QPT_RC;
-
- CHECK(::rdma_create_qp(id.get(), pd.get(), &qp_attr));
-
- // Set the qp context to this so we can find ourselves again
- id->qp->qp_context = this;
- }
-
- inline QueuePair::~QueuePair() {
- if (outstandingSendEvents > 0)
- ::ibv_ack_cq_events(scq.get(), outstandingSendEvents);
- if (outstandingRecvEvents > 0)
- ::ibv_ack_cq_events(rcq.get(), outstandingRecvEvents);
-
- ::rdma_destroy_qp(id.get());
- }
-
inline void QueuePair::notifyRecv() {
CHECK_IBV(ibv_req_notify_cq(rcq.get(), 0));
}
@@ -479,44 +484,6 @@ namespace Rdma {
CHECK_IBV(ibv_req_notify_cq(scq.get(), 0));
}
- inline void QueuePair::postRecv(Buffer* buf) {
- ::ibv_recv_wr rwr = {};
- ::ibv_sge sge;
-
- sge.addr = (uintptr_t) buf->bytes+buf->dataStart;
- sge.length = buf->dataCount;
- sge.lkey = buf->mr->lkey;
-
- rwr.wr_id = reinterpret_cast<uint64_t>(buf);
- rwr.sg_list = &sge;
- rwr.num_sge = 1;
-
- ::ibv_recv_wr* badrwr = 0;
- CHECK_IBV(::ibv_post_recv(id->qp, &rwr, &badrwr));
- if (badrwr)
- throw std::logic_error("ibv_post_recv(): Bad rwr");
- }
-
- inline void QueuePair::postSend(Buffer* buf) {
- ::ibv_send_wr swr = {};
- ::ibv_sge sge;
-
- sge.addr = (uintptr_t) buf->bytes+buf->dataStart;
- sge.length = buf->dataCount;
- sge.lkey = buf->mr->lkey;
-
- swr.wr_id = reinterpret_cast<uint64_t>(buf);
- swr.opcode = IBV_WR_SEND;
- swr.send_flags = IBV_SEND_SIGNALED;
- swr.sg_list = &sge;
- swr.num_sge = 1;
-
- ::ibv_send_wr* badswr = 0;
- CHECK_IBV(::ibv_post_send(id->qp, &swr, &badswr));
- if (badswr)
- throw std::logic_error("ibv_post_send(): Bad swr");
- }
-
inline ConnectionEvent::ConnectionEvent(::rdma_cm_event* e) :
id((e->event != RDMA_CM_EVENT_CONNECT_REQUEST) ?
Connection::find(e->id) : new Connection(e->id)),
@@ -525,26 +492,6 @@ namespace Rdma {
{}
}
-inline 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)
- }
-# undef CHECK_TYPE
- return o;
-}
+std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t);
#endif // RDMA_WRAP_H
diff --git a/cpp/src/qpid/sys/solaris/ECFPoller.cpp b/cpp/src/qpid/sys/solaris/ECFPoller.cpp
index fefa21dae1..f12012cbb0 100644
--- a/cpp/src/qpid/sys/solaris/ECFPoller.cpp
+++ b/cpp/src/qpid/sys/solaris/ECFPoller.cpp
@@ -30,9 +30,11 @@
#include <port.h>
#include <poll.h>
#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
#include <assert.h>
-#include <vector>
+#include <queue>
#include <exception>
@@ -43,11 +45,11 @@ namespace qpid {
namespace sys {
// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used
-DeletionManager<PollerHandle> PollerHandleDeletionManager;
+DeletionManager<PollerHandlePrivate> PollerHandleDeletionManager;
// Instantiate (and define) class static for DeletionManager
template <>
-DeletionManager<PollerHandle>::AllThreadsStatuses DeletionManager<PollerHandle>::allThreadsStatuses(0);
+DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerHandlePrivate>::allThreadsStatuses(0);
class PollerHandlePrivate {
friend class Poller;
@@ -58,7 +60,8 @@ class PollerHandlePrivate {
MONITORED,
INACTIVE,
HUNGUP,
- MONITORED_HUNGUP
+ MONITORED_HUNGUP,
+ DELETED
};
int fd;
@@ -104,6 +107,14 @@ class PollerHandlePrivate {
assert(stat == MONITORED);
stat = HUNGUP;
}
+
+ bool isDeleted() const {
+ return stat == DELETED;
+ }
+
+ void setDeleted() {
+ stat = DELETED;
+ }
};
PollerHandle::PollerHandle(const IOHandle& h) :
@@ -111,11 +122,16 @@ PollerHandle::PollerHandle(const IOHandle& h) :
{}
PollerHandle::~PollerHandle() {
- delete impl;
-}
-
-void PollerHandle::deferDelete() {
- PollerHandleDeletionManager.markForDeletion(this);
+ {
+ ScopedLock<Mutex> l(impl->lock);
+ if (impl->isDeleted()) {
+ return;
+ }
+ if (impl->isActive()) {
+ impl->setDeleted();
+ }
+ }
+ PollerHandleDeletionManager.markForDeletion(impl);
}
/**
@@ -125,35 +141,82 @@ void PollerHandle::deferDelete() {
class PollerPrivate {
friend class Poller;
- const int portId;
+ 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::IN: return POLLIN;
- case Poller::OUT: return POLLOUT;
- case Poller::INOUT: return POLLIN | POLLOUT;
- default: return 0;
+ 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;
+ 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()) {
+ 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) {
@@ -177,7 +240,6 @@ void Poller::addFd(PollerHandle& handle, Direction dir) {
QPID_LOG(trace, "Poller::addFd(handle=" << &handle
<< "[" << typeid(&handle).name()
<< "], fd=" << eh.fd << ")");
- //assert(dynamic_cast<DispatchHandle*>(&handle));
}
void Poller::delFd(PollerHandle& handle) {
@@ -223,17 +285,56 @@ void Poller::rearmFd(PollerHandle& handle) {
}
void Poller::shutdown() {
- //Send an Alarm to the port
- //We need to send a nonzero event mask, using POLLHUP, but
- //The wait method will only look for a PORT_ALERT_SET
- QPID_POSIX_CHECK(::port_alert(impl->portId, PORT_ALERT_SET, POLLHUP, NULL));
- QPID_LOG(trace, "Poller::shutdown");
+ //Allow sloppy code to shut us down more than once
+ if (impl->isShutdown)
+ return;
+
+ impl->isShutdown = true;
+ impl->interrupt();
+}
+
+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;
@@ -243,12 +344,21 @@ Poller::Event Poller::wait(Duration timeout) {
do {
PollerHandleDeletionManager.markAllUnusedInThisThread();
- QPID_LOG(trace, "About to enter port_get. Thread "
- << pthread_self()
+ 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:
@@ -259,33 +369,61 @@ Poller::Event Poller::wait(Duration timeout) {
QPID_POSIX_CHECK(rc);
}
} else {
- //We use alert mode to notify the shutdown of the Poller
- if (pe.portev_source == PORT_SOURCE_ALERT) {
- return Event(0, SHUTDOWN);
- }
- if (pe.portev_source == PORT_SOURCE_FD) {
- PollerHandle *handle = static_cast<PollerHandle*>(pe.portev_user);
- PollerHandlePrivate& eh = *handle->impl;
- ScopedLock<Mutex> l(eh.lock);
- QPID_LOG(trace, "About to send handle: " << handle);
+ 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 (eh.isActive()) {
- if (pe.portev_events & POLLHUP) {
- if (eh.isHungup()) {
- return Event(handle, DISCONNECTED);
+ 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();
}
- 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));
+ 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);
}
diff --git a/cpp/src/qpid/sys/solaris/SystemInfo.cpp b/cpp/src/qpid/sys/solaris/SystemInfo.cpp
new file mode 100755
index 0000000000..0075a89021
--- /dev/null
+++ b/cpp/src/qpid/sys/solaris/SystemInfo.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/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(TcpAddress &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");
+
+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(TcpAddress(addr, port));
+ }
+ if (addrList.empty()) {
+ addrList.push_back(TcpAddress(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/cpp/src/qpid/sys/ssl/SslHandler.cpp b/cpp/src/qpid/sys/ssl/SslHandler.cpp
new file mode 100644
index 0000000000..3469f88c0f
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslHandler.cpp
@@ -0,0 +1,187 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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) :
+ identifier(id),
+ aio(0),
+ factory(f),
+ codec(0),
+ readError(false),
+ isClient(false)
+{}
+
+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, aio->getKeyLen());
+ 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, aio->getKeyLen());
+ 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();
+}
+
+
+}}} // namespace qpid::sys::ssl
diff --git a/cpp/src/qpid/sys/ssl/SslHandler.h b/cpp/src/qpid/sys/ssl/SslHandler.h
new file mode 100644
index 0000000000..8f6b8e732a
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslHandler.h
@@ -0,0 +1,76 @@
+#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;
+class SslIOBufferBase;
+class SslSocket;
+
+class SslHandler : public OutputControl {
+ std::string identifier;
+ SslIO* aio;
+ ConnectionCodec::Factory* factory;
+ ConnectionCodec* codec;
+ bool readError;
+ bool isClient;
+
+ void write(const framing::ProtocolInitiation&);
+
+ public:
+ SslHandler(std::string id, ConnectionCodec::Factory* f);
+ ~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/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp
new file mode 100644
index 0000000000..c149d6ea74
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslIo.cpp
@@ -0,0 +1,439 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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/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,
+ uint16_t 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: " << qpid::sys::strError(rc) << "(" << rc << ")" );
+ 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_POSIX_CHECK(rc);
+ }
+ }
+ } 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);
+ }
+}
+
+int SslIO::getKeyLen() {return socket.getKeyLen();}
diff --git a/cpp/src/qpid/sys/ssl/SslIo.h b/cpp/src/qpid/sys/ssl/SslIo.h
new file mode 100644
index 0000000000..3162abac40
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslIo.h
@@ -0,0 +1,171 @@
+#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 <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,
+ uint16_t 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();
+
+ int getKeyLen();
+
+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/cpp/src/qpid/sys/ssl/SslSocket.cpp b/cpp/src/qpid/sys/ssl/SslSocket.cpp
new file mode 100644
index 0000000000..aa8cf127d7
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslSocket.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/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;
+}
+
+}
+
+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, uint16_t port) const
+{
+ std::stringstream namestream;
+ namestream << host << ":" << port;
+ connectname = namestream.str();
+
+ void* arg = SslOptions::global.certName.empty() ? 0 : 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, 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));
+}
+
+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);
+ }
+}
+
+
+/** 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;
+}
+
+}}} // namespace qpid::sys::ssl
diff --git a/cpp/src/qpid/sys/ssl/SslSocket.h b/cpp/src/qpid/sys/ssl/SslSocket.h
new file mode 100644
index 0000000000..f1f05e7a98
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/SslSocket.h
@@ -0,0 +1,119 @@
+#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;
+
+ void connect(const std::string& host, uint16_t 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;
+
+ 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;
+
+private:
+ mutable std::string connectname;
+ mutable PRFileDesc* socket;
+ /**
+ * '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/cpp/src/qpid/sys/ssl/check.cpp b/cpp/src/qpid/sys/ssl/check.cpp
new file mode 100644
index 0000000000..c5e6005e03
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/check.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/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 {
+
+const std::string SSL_ERROR_BAD_CERT_DOMAIN_STR =
+ "Unable to communicate securely with peer: requested domain name does not match the server's certificate.";
+const std::string SSL_ERROR_BAD_CERT_ALERT_STR = "SSL peer cannot verify your certificate.";
+const std::string SEC_ERROR_BAD_DATABASE_STR = "Security library: bad database.";
+const std::string SSL_ERROR_NO_CERTIFICATE_STR = "Unable to find the certificate or key necessary for authentication.";
+const std::string SSL_ERROR_UNKNOWN = "Unknown NSS error code.";
+
+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?):
+ switch (code) {
+ case SSL_ERROR_BAD_CERT_DOMAIN: msg = SSL_ERROR_BAD_CERT_DOMAIN_STR; break;
+ case SSL_ERROR_BAD_CERT_ALERT: msg = SSL_ERROR_BAD_CERT_ALERT_STR; break;
+ case SEC_ERROR_BAD_DATABASE: msg = SEC_ERROR_BAD_DATABASE_STR; break;
+ case SSL_ERROR_NO_CERTIFICATE: msg = SSL_ERROR_NO_CERTIFICATE_STR; break;
+ default: msg = SSL_ERROR_UNKNOWN; 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/cpp/src/qpid/sys/ssl/check.h b/cpp/src/qpid/sys/ssl/check.h
new file mode 100644
index 0000000000..984c338a18
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/check.h
@@ -0,0 +1,53 @@
+#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 <iostream>
+#include <string>
+#include <nspr.h>
+#include <nss.h>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+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/cpp/src/qpid/sys/ssl/util.cpp b/cpp/src/qpid/sys/ssl/util.cpp
new file mode 100644
index 0000000000..53326e2f55
--- /dev/null
+++ b/cpp/src/qpid/sys/ssl/util.cpp
@@ -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.
+ *
+ */
+#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 {
+
+std::string defaultCertName()
+{
+ TcpAddress 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/cpp/src/qpid/sys/ssl/util.h b/cpp/src/qpid/sys/ssl/util.h
new file mode 100644
index 0000000000..f34adab7be
--- /dev/null
+++ b/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/cpp/src/qpid/broker/BrokerSingleton.cpp b/cpp/src/qpid/sys/uuid.h
index 5ba8c9d1e1..804ab34463 100644
--- a/cpp/src/qpid/broker/BrokerSingleton.cpp
+++ b/cpp/src/qpid/sys/uuid.h
@@ -1,3 +1,6 @@
+#ifndef _sys_uuid_h
+#define _sys_uuid_h
+
/*
*
* Copyright (c) 2006 The Apache Software Foundation
@@ -16,21 +19,10 @@
*
*/
-#include "BrokerSingleton.h"
-
-namespace qpid {
-namespace broker {
-
-BrokerSingleton::BrokerSingleton() {
- if (broker.get() == 0)
- broker = Broker::create();
- boost::intrusive_ptr<Broker>::operator=(broker);
-}
-
-BrokerSingleton::~BrokerSingleton() {
- broker->shutdown();
-}
-
-boost::intrusive_ptr<Broker> BrokerSingleton::broker;
+#ifdef _WIN32
+# include "qpid/sys/windows/uuid.h"
+#else
+# include <uuid/uuid.h>
+#endif /* _WIN32 */
-}} // namespace qpid::broker
+#endif /* _sys_uuid_h */
diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp
new file mode 100644
index 0000000000..e4174ee445
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -0,0 +1,751 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR 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 <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) {
+ poller->monitorHandle(PollerHandle(socket), 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;
+
+public:
+ AsynchConnector(const Socket& socket,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb = 0);
+};
+
+AsynchConnector::AsynchConnector(const Socket& sock,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb)
+ : connCallback(connCb), failCallback(failCb), socket(sock) {
+ try {
+ socket.connect(hostname, port);
+ socket.setNonblocking();
+ connCallback(socket);
+ } catch(std::exception& e) {
+ if (failCallback)
+ failCallback(socket, -1, std::string(e.what()));
+ socket.close();
+ delete &socket;
+ }
+}
+
+} // namespace windows
+
+AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
+ Callback callback)
+{
+ return new windows::AsynchAcceptor(s, callback);
+}
+
+AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s,
+ Poller::shared_ptr poller,
+ std::string hostname,
+ uint16_t port,
+ ConnectedCallback connCb,
+ FailedCallback failCb)
+{
+ return new windows::AsynchConnector(s,
+ poller,
+ 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) {
+ poller = poller0;
+ poller->monitorHandle(PollerHandle(socket), 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);
+ if (buff->dataStart != 0) {
+ memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount);
+ buff->dataStart = 0;
+ }
+ 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);
+ int writeCount = buff->byteCount-buff->dataCount;
+ 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());
+ if (status == 0)
+ notifyEof();
+ else
+ 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/cpp/src/qpid/sys/windows/AsynchIoResult.h b/cpp/src/qpid/sys/windows/AsynchIoResult.h
new file mode 100755
index 0000000000..66c89efc11
--- /dev/null
+++ b/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/cpp/src/qpid/sys/windows/FileSysDir.cpp b/cpp/src/qpid/sys/windows/FileSysDir.cpp
new file mode 100644
index 0000000000..88f1637d48
--- /dev/null
+++ b/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/cpp/src/qpid/sys/SystemInfo.cpp b/cpp/src/qpid/sys/windows/IOHandle.cpp
index dcc7ad9985..250737cb99 100644..100755
--- a/cpp/src/qpid/sys/SystemInfo.cpp
+++ b/cpp/src/qpid/sys/windows/IOHandle.cpp
@@ -1,4 +1,5 @@
/*
+ *
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -6,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,18 +19,24 @@
*
*/
-#include "SystemInfo.h"
-#include <unistd.h>
+#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
+#include <windows.h>
namespace qpid {
namespace sys {
-long SystemInfo::concurrency() {
-#ifdef _SC_NPROCESSORS_ONLN // Linux specific.
- return sysconf(_SC_NPROCESSORS_ONLN);
-#else
- return -1;
-#endif
+SOCKET toFd(const IOHandlePrivate* h)
+{
+ return h->fd;
+}
+
+IOHandle::IOHandle(IOHandlePrivate* h) :
+ impl(h)
+{}
+
+IOHandle::~IOHandle() {
+ delete impl;
}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/IoHandlePrivate.h b/cpp/src/qpid/sys/windows/IoHandlePrivate.h
new file mode 100755
index 0000000000..5943db5cc7
--- /dev/null
+++ b/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/cpp/src/qpid/sys/windows/IocpPoller.cpp b/cpp/src/qpid/sys/windows/IocpPoller.cpp
new file mode 100755
index 0000000000..4fcc9155f1
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/IocpPoller.cpp
@@ -0,0 +1,214 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#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::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/cpp/src/qpid/sys/windows/LockFile.cpp b/cpp/src/qpid/sys/windows/LockFile.cpp
new file mode 100755
index 0000000000..048c2d5b18
--- /dev/null
+++ b/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/cpp/src/qpid/sys/windows/PipeHandle.cpp b/cpp/src/qpid/sys/windows/PipeHandle.cpp
new file mode 100755
index 0000000000..062458ae5f
--- /dev/null
+++ b/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/cpp/src/qpid/sys/windows/PollableCondition.cpp b/cpp/src/qpid/sys/windows/PollableCondition.cpp
new file mode 100644
index 0000000000..5ccc136bd1
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/PollableCondition.cpp
@@ -0,0 +1,116 @@
+#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;
+ LONG armed;
+};
+
+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), armed(0)
+{
+}
+
+PollableConditionPrivate::~PollableConditionPrivate()
+{
+}
+
+void PollableConditionPrivate::poke()
+{
+ if (!armed)
+ return;
+
+ // 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);
+}
+
+ /* 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/cpp/src/qpid/sys/windows/Shlib.cpp b/cpp/src/qpid/sys/windows/Shlib.cpp
new file mode 100644
index 0000000000..38027de93f
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/Shlib.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/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) {
+ void* sym = GetProcAddress(static_cast<HMODULE>(handle), name);
+ if (sym == NULL)
+ throw QPID_WINDOWS_ERROR(GetLastError());
+ return sym;
+}
+
+}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/Socket.cpp
new file mode 100755
index 0000000000..11fb8b4133
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/Socket.cpp
@@ -0,0 +1,343 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#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, bool includeService = false)
+{
+ 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 (includeService) {
+ 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);
+ } else {
+ if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
+ dispName, sizeof(dispName),
+ 0, 0,
+ NI_NUMERICHOST) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ return dispName;
+ }
+}
+
+std::string getService(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];
+ if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
+ 0, 0,
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ return 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::setTimeout(const Duration& interval) const
+{
+ const SOCKET& socket = impl->fd;
+ int64_t nanosecs = interval;
+ nanosecs /= (1000 * 1000); // nsecs -> usec -> msec
+ int msec = 0;
+ if (nanosecs > std::numeric_limits<int>::max())
+ msec = std::numeric_limits<int>::max();
+ else
+ msec = static_cast<int>(nanosecs);
+ setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msec, sizeof(msec));
+ setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msec, sizeof(msec));
+}
+
+void Socket::setNonblocking() const {
+ u_long nonblock = 1;
+ QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock));
+}
+
+void Socket::connect(const std::string& host, uint16_t port) const
+{
+ SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ connect(sa);
+}
+
+void
+Socket::connect(const SocketAddress& addr) const
+{
+ 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) << ": " << connectname));
+}
+
+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(uint16_t 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(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::getSockname() const
+{
+ return getName(impl->fd, true);
+}
+
+std::string Socket::getPeername() const
+{
+ return getName(impl->fd, false);
+}
+
+std::string Socket::getPeerAddress() const
+{
+ if (!connectname.empty())
+ return std::string (connectname);
+ return getName(impl->fd, false, true);
+}
+
+std::string Socket::getLocalAddress() const
+{
+ return getName(impl->fd, true, true);
+}
+
+uint16_t Socket::getLocalPort() const
+{
+ return atoi(getService(impl->fd, true).c_str());
+}
+
+uint16_t Socket::getRemotePort() const
+{
+ return atoi(getService(impl->fd, true).c_str());
+}
+
+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/cpp/src/qpid/sys/windows/SocketAddress.cpp b/cpp/src/qpid/sys/windows/SocketAddress.cpp
new file mode 100644
index 0000000000..a3e03c9be8
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/SocketAddress.cpp
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/SocketAddress.h"
+
+#include "qpid/sys/windows/check.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() const
+{
+ return host + ":" + port;
+}
+
+const ::addrinfo& getAddrInfo(const SocketAddress& sa)
+{
+ return *sa.addrInfo;
+}
+
+}}
diff --git a/cpp/src/qpid/sys/windows/StrError.cpp b/cpp/src/qpid/sys/windows/StrError.cpp
new file mode 100755
index 0000000000..9c1bfcd79c
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/StrError.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/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];
+ if (0 == FormatMessage (FORMAT_MESSAGE_MAX_WIDTH_MASK
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ err,
+ 0, // Default language
+ buf,
+ bufsize,
+ 0))
+ {
+ strerror_s (buf, bufsize, err);
+ }
+ return std::string(buf);
+}
+
+}}
diff --git a/cpp/src/qpid/sys/windows/SystemInfo.cpp b/cpp/src/qpid/sys/windows/SystemInfo.cpp
new file mode 100755
index 0000000000..ea53fc199c
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/SystemInfo.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.
+ *
+ */
+
+/* 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 (TcpAddress &address) {
+ char name[HOST_NAME_MAX];
+ if (::gethostname(name, sizeof(name)) != 0) {
+ errno = WSAGetLastError();
+ return false;
+ }
+ address.host = name;
+ return true;
+}
+
+void SystemInfo::getLocalIpAddresses (uint16_t port,
+ std::vector<Address> &addrList) {
+ enum { MAX_URL_INTERFACES = 100 };
+ static const std::string LOCALHOST("127.0.0.1");
+
+ 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(TcpAddress(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/cpp/src/qpid/sys/windows/Thread.cpp b/cpp/src/qpid/sys/windows/Thread.cpp
new file mode 100755
index 0000000000..fed82e4d54
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/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/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)) {}
+
+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::id() {
+ return impl ? impl->threadId : 0;
+}
+
+/* static */
+Thread Thread::current() {
+ Thread t;
+ t.impl.reset(new ThreadPrivate());
+ return t;
+}
+
+}} /* qpid::sys */
diff --git a/cpp/src/qpid/sys/windows/Time.cpp b/cpp/src/qpid/sys/windows/Time.cpp
new file mode 100644
index 0000000000..1d7b94e8d7
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/Time.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 "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 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::now() {
+ AbsTime time_now;
+ time_now.timepoint = boost::get_system_time();
+ return time_now;
+}
+
+Duration::Duration(const AbsTime& time0) : nanosecs(0) {
+ time_period p(ptime(min_date_time), time0.timepoint);
+ nanosecs = p.length().total_nanoseconds();
+}
+
+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 toPtime(ptime& pt, const AbsTime& t) {
+ pt = t.getPrivate();
+}
+
+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 );
+ ::localtime_s(&timeinfo, &rawtime);
+ ::strftime(time_string, 100,
+ "%Y-%m-%d %H:%M:%S",
+ &timeinfo);
+ o << time_string << " ";
+}
+}}
diff --git a/cpp/src/qpid/sys/windows/uuid.cpp b/cpp/src/qpid/sys/windows/uuid.cpp
new file mode 100644
index 0000000000..6ff3a3cb8a
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/uuid.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 <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) {
+ strncpy_s (out, 36+1, (char*)formatted, _TRUNCATE);
+ RpcStringFree(&formatted);
+ }
+}
+
diff --git a/cpp/src/qpid/sys/windows/uuid.h b/cpp/src/qpid/sys/windows/uuid.h
new file mode 100644
index 0000000000..c79abe95c6
--- /dev/null
+++ b/cpp/src/qpid/sys/windows/uuid.h
@@ -0,0 +1,38 @@
+#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/CommonImportExport.h"
+#include <qpid/sys/IntegerTypes.h>
+
+namespace qpid { namespace sys { const size_t UuidSize = 16; }}
+typedef uint8_t uuid_t[qpid::sys::UuidSize];
+
+QPID_COMMON_EXTERN void uuid_clear (uuid_t uu);
+QPID_COMMON_EXTERN void uuid_copy (uuid_t dst, const uuid_t src);
+QPID_COMMON_EXTERN void uuid_generate (uuid_t out);
+QPID_COMMON_EXTERN int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0
+QPID_COMMON_EXTERN int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1
+QPID_COMMON_EXTERN void uuid_unparse (const uuid_t uu, char *out);
+
+#endif /*!_sys_windows_uuid_h*/
diff --git a/cpp/src/qpid/sys/StrError.h b/cpp/src/qpid/xml/XmlBinding.h
index 3843f2abe1..cc6b4dca5d 100644
--- a/cpp/src/qpid/sys/StrError.h
+++ b/cpp/src/qpid/xml/XmlBinding.h
@@ -1,6 +1,3 @@
-#ifndef _sys_StrError_h
-#define _sys_StrError_h
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,15 +18,20 @@
* under the License.
*
*/
-
+#include <qpid/framing/FieldTable>
#include <string>
+#ifndef _XmlBinding_
+#define _XmlBinding_
+
namespace qpid {
-namespace sys {
+namespace client {
-/** Get the error message for a system number err, e.g. errno. */
-std::string strError(int err);
+class XmlBinding : public framing::FieldTable {
+ public:
+ setQuery(string query) { setString("xquery", query); }
+};
-}} // namespace qpid
-
-#endif // _sys_StrError_h
+}
+}
+#endif
diff --git a/cpp/src/qpid/xml/XmlExchange.cpp b/cpp/src/qpid/xml/XmlExchange.cpp
new file mode 100644
index 0000000000..472ca28954
--- /dev/null
+++ b/cpp/src/qpid/xml/XmlExchange.cpp
@@ -0,0 +1,262 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "config.h"
+
+#include "qpid/xml/XmlExchange.h"
+
+#include "qpid/broker/DeliverableMessage.h"
+
+#include "qpid/log/Statement.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>
+
+#include <xqilla/ast/XQGlobalVariable.hpp>
+
+#include <xqilla/context/ItemFactory.hpp>
+#include <xqilla/xqilla-simple.hpp>
+
+#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 {
+
+
+ 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);
+}
+
+
+ // #### TODO: The Binding should take the query text
+ // #### only. Consider encapsulating the entire block, including
+ // #### the if condition.
+
+
+bool XmlExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* bindingArguments)
+{
+ string queryText = bindingArguments->getAsString("xquery");
+
+ try {
+ RWlock::ScopedWlock l(lock);
+
+ XmlBinding::vector& bindings(bindingsMap[routingKey]);
+ XmlBinding::vector::ConstPtr p = bindings.snapshot();
+ if (!p || std::find_if(p->begin(), p->end(), MatchQueue(queue)) == p->end()) {
+ Query query(xqilla.parse(X(queryText.c_str())));
+ XmlBinding::shared_ptr binding(new XmlBinding (routingKey, queue, this, query));
+ bindings.add(binding);
+ QPID_LOG(trace, "Bound successfully with query: " << queryText );
+
+ binding->parse_message_content = false;
+
+ if (query->getQueryBody()->getStaticAnalysis().areContextFlagsUsed()) {
+ binding->parse_message_content = true;
+ }
+ else {
+ GlobalVariables &vars = const_cast<GlobalVariables&>(query->getVariables());
+ for(GlobalVariables::iterator it = vars.begin(); it != vars.end(); ++it) {
+ if ((*it)->getStaticAnalysis().areContextFlagsUsed()) {
+ binding->parse_message_content = true;
+ break;
+ }
+ }
+ }
+
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_bindingCount();
+ ((_qmf::Queue*) queue->GetManagementObject())->inc_bindingCount();
+ }
+ } else {
+ return false;
+ }
+ }
+ catch (XQException& e) {
+ throw InternalErrorException(QPID_MSG("Could not parse xquery:"+ queryText));
+ }
+ catch (...) {
+ throw InternalErrorException(QPID_MSG("Unexpected error - Could not parse xquery:"+ queryText));
+ }
+ routeIVE();
+ return true;
+}
+
+bool XmlExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/)
+{
+ RWlock::ScopedWlock l(lock);
+ if (bindingsMap[routingKey].remove_if(MatchQueue(queue))) {
+ if (mgmtExchange != 0) {
+ mgmtExchange->dec_bindingCount();
+ ((_qmf::Queue*) queue->GetManagementObject())->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) {
+ // ### TODO: Do types properly
+ if (v->second->convertsTo<std::string>()) {
+ QPID_LOG(trace, "XmlExchange, external variable: " << v->first << " = " << v->second->getData().getString().c_str());
+ Item::Ptr value = context->getItemFactory()->createString(X(v->second->getData().getString().c_str()), context.get());
+ context->setExternalVariable(X(v->first.c_str()), value);
+ }
+ }
+ }
+
+ Result result = query->execute(context.get());
+ return result->getEffectiveBooleanValue(context.get(), 0);
+ }
+ 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 routingKey, const FieldTable* const)
+{
+ RWlock::ScopedRlock l(lock);
+ if (routingKey) {
+ XmlBindingsMap::iterator i = bindingsMap.find(*routingKey);
+
+ 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();
+}
+
+const std::string XmlExchange::typeName("xml");
+
+}
+}
diff --git a/cpp/src/qpid/broker/XmlExchange.h b/cpp/src/qpid/xml/XmlExchange.h
index 883bfceaca..38cb7699b6 100644
--- a/cpp/src/qpid/broker/XmlExchange.h
+++ b/cpp/src/qpid/xml/XmlExchange.h
@@ -21,10 +21,11 @@
#ifndef _XmlExchange_
#define _XmlExchange_
-#include "Exchange.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/CopyOnWriteArray.h"
#include "qpid/sys/Monitor.h"
-#include "Queue.h"
+#include "qpid/broker/Queue.h"
#include <xqilla/xqilla-simple.hpp>
@@ -36,18 +37,20 @@
namespace qpid {
namespace broker {
+class Broker;
class XmlExchange : public virtual Exchange {
typedef boost::shared_ptr<XQQuery> Query;
struct XmlBinding : public Exchange::Binding {
typedef boost::shared_ptr<XmlBinding> shared_ptr;
- typedef std::vector<XmlBinding::shared_ptr> vector;
+ typedef qpid::sys::CopyOnWriteArray<XmlBinding::shared_ptr> vector;
boost::shared_ptr<XQQuery> xquery;
+ bool parse_message_content;
XmlBinding(const std::string& key, const Queue::shared_ptr queue, Exchange* parent, Query query):
- Binding(key, queue, parent), xquery(query) {}
+ Binding(key, queue, parent), xquery(query), parse_message_content(true) {}
};
@@ -57,14 +60,14 @@ class XmlExchange : public virtual Exchange {
XQilla xqilla;
qpid::sys::RWlock lock;
- bool matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args);
+ 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);
+ XmlExchange(const std::string& name, management::Manageable* parent = 0, Broker* broker = 0);
XmlExchange(const string& _name, bool _durable,
- const qpid::framing::FieldTable& _args, management::Manageable* parent = 0);
+ const qpid::framing::FieldTable& _args, management::Manageable* parent = 0, Broker* broker = 0);
virtual std::string getType() const { return typeName; }
diff --git a/cpp/src/qpid/xml/XmlExchangePlugin.cpp b/cpp/src/qpid/xml/XmlExchangePlugin.cpp
new file mode 100644
index 0000000000..742b878e86
--- /dev/null
+++ b/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